`
蒙面考拉
  • 浏览: 155693 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

char *p,char p[]以及string p的区别

 
阅读更多

问题的引入:

 

     char a[]=" abc " ;  

     char b[]=" abc " ;

     char *c=" abc " ;

     char *d=" abc " ;

     const char e[]=" abc " ;

     const char f[]=" abc " ;

     const char *g=" abc " ;

     const char *h=" abc " ;

     printf(" %i\n " ,a==b);

     printf(" %i\n " ,c==d);

     printf(" %i\n " ,e==f);

     printf(" %i\n " ,g==h);

 

 

正确的答案:0 1 0 1。

 

说明:指针不分配所指向的数据的空间,数组分配所有元素的内存空间。

            char *c="abc"; 这是定义一个指针,并把常量区的一个字符串“abc”的首地址赋值给这个指针,所以,是无法修改数据

            内容的(*p='h',错!),但是可以修改指针,如c=a;

            char a[]="abc";这是定义一个数组,然后把常量区字符串"abc"拷贝到这个数组中来(数组分配所有元素的内存空间)。所

            以操作和常量区的“abc”没有关联,故可以赋值:a[0]='d',OK.但是不能修改a,如a=c错,a就像是const属性的指针。

            a与b,e与f的地址是不同的所以a==b为0,e==f为0.但是c与d,g与h的都是指向同一个常量区的地址,所以相等。

其中,strlen(a)=strlen(c)=3;sizeof(a)=4,sizeof(c)=4;sizeof(c)=4是因为c是指针,指针的sizeof是一个定值为4.

 

const char *str=“abcd”与char *str=“abcd”的区别

1.浅白一点:前者如果修改某个字符(str[1]='x')的时候在编译时就会报错,二后者在运行时报错。两者都不允许这种修改。前者在编译时报错是因为限制符const的作用。后者是因为''abcd'在常量区,str只是指向常量区的地址的指针,没法进行修改。

2.char p[]="1234";

   const char* str="abcd";

   str=p;

   str[2]='1';//非法

   str变成指向数组p的指针,数组名表示地址。但是有const char限制,指针是没法修改数组内容的(数组的内容都在栈区)。

  如果:char* str="abcd";

              str=p;

              str[2]='1';//合法。   数组及其元素在栈区是云寻修改的。

 

char *与char[]的区别:

问题引入:

     char *c="abc" 和char c[]="abc"前者改变其内容程序会崩溃,后者完全正确。

代码:

        #include<iostream> 
  using namespace std;
  main()
  {
  char *c1 = "abc";
  char c2[] = "abc";
  char *c3 = ( char* )malloc(3);
  c3 = "abc";
  printf("%d %d %s\n",&c1,c1,c1);
  printf("%d %d %s\n",&c2,c2,c2);
  printf("%d %d %s\n",&c3,c3,c3);
  getchar();
  }

运行结果

  2293628 4199056 abc   c1存储的是"abc"在常量区的地址,&c1表示指针的地址,此地址存储在栈区

  2293624 2293624 abc   c2本身表示的就是地址,所以与&c2是相同的

  2293620 4199056 abc   &c3表示的指针c3的地址,此地址也是存储在栈区,c3指向的地址存储的内容"abc"在常量区

                                                    已经存在,所以c3存储的也是"abc"在常量区的地址与c1是相同的。

同时我们还可一发现有四个地址是在一个区域的,说明这些都存在与栈区。而''abc''存在于常量区。这两个地址还是有意定距离的。

 

存取效率:

char s1[]="aaaaaaaaaaa";

char *s2="bbbbbbbbbbb";

aaaaaaaaaa是在运行时赋值的。而bbbbbbbbbbb是在编译时就确定的,因为他存在于栈区。但是,在以后的存取过程中,在栈上数组比指针所指向的字符串要快。

 

总结:

char *c1="abc";实际上先是在文字常量区分配一块内存存放"abc",然后在栈上分配一地址给c1并指向这块地址,然后改变常量"abc"自然会崩溃。

char c2[]="abc";实际上abc分配内存地址的地方在栈区与上面的地址是不同的,是可以修改的。


深入思考:

        #include  <iostream>
  using namespace std;
  main()
  {
  char *c1 = "abc";
  char c2[] = "abc";
  char *c3 = ( char* )malloc(3); //分配来的3个字节在堆区
  //  *c3 = "abc" //error ==>报错:不能将const char* 类型的值分配给char 类型的实体,所以必须

                                                                   c3=“abc”,两边都是char*类型
  strcpy(c3,"abc"); ==>把第二个参数含有NULL的字符串赋值到以c3开始的地址空间,并且两个参数所指的内存区域不

                                              可以重叠
  c3[0] = 'g';

        //c3="abc";c3[0]='g'; //error==>报错:c3指向的是在常量区的地址,而不是指向自己分配的地址,所以不能修改
  printf("%d %d %s\n",&c1,c1,c1);
  printf("%d %d %s\n",&c2,c2,c2);
  printf("%d %d %s\n",&c3,c3,c3);
  getchar();
  }

输出:

  2293628 4199056 abc

  2293624 2293624 abc

  2293620 4012976 gbc    可以看到c1存储的地址与c3存储的地址是不同的。

 

实例:

char* get_str(void)
{
    char str[] = {"abcd"};                              
    return str;
}

       char str[] = {"abcd"};定义了一个局部字符数组,尽管是数组,但它是一个局部变量,返回它的地址肯定是一个已经释放了的空间的地址。
       此函数返回的是内部一个局部字符数组str的地址,且函数调用完毕后 此数组被销毁,所以你返回的指针也就指向一块被销毁的内存,这种写法是错误的

char* get_str(void)
{
    char *str = {"abcd"};
    return str;
}

char* str = {"abcd"};表示先定义个字符串常量,并将其地址赋给str。
      此函数返回的是字符串常量的地址,而像这种字符串都是属于全局的,在编译的时候就已经分配了内存了,只有程序退出的时候才会被销毁,所以返回它的地址是没有问题的,但是你最好返回常量指针,因为你不能去改变字符串常量的值。

    这个str就在栈里面,但后面的字符串在常量区,函数返回的时候,先将常量区的地址返回,然后作用域结束,释放str在栈里面的空间。


准确的说,上面两个“abc"都是存储在静态存储区,即常量区,存储在堆上 。常量区是可读不可写的。 所以任何试图对常量区进行写的操作都是非法的,当然了,这也不是一定不可写的,你可以采取某种渠道改变常量区的内存属性,比如改变pe相关节的属性就可以 对常量区进行读写,当然了,这个目前可以忽略。
       那么为什么str[] = "abc";      可以写呢?   答案就在str[] = "abc";会有一个额外的拷贝过程,即把常量区的 "abc"拷贝到栈内存去,所以就可以写了。

 

C风格字符串与string的区别与联系:

C:字符串一般存储在一个字符数组中,通过一个char* 类型的指针来操纵它

       常用的字符串函数:字符串不包含\0的长度:int strlen(const char*)

                                           两字符串是否相等:int strcmp(const char*, const char*)

                                           将第二个字符串拷贝到第一个字符串中:char* strcpy(char*, const char*)

     相关的头文件:#include<cstring>

     操作实例:const char *st=“this is a dog”;系统在内部把字符串常量存储在一个字符串数组中,然后让st指向该数组的第一个元素。操作手段一般是while(*st++){.....};

例如: int string_length(const char* st){

                   int cnt=0;

                   if(st){

                        while(*st++)

                               __cnt;

                   }

                   return cnt;

             }

 

string类型:头文件 #include<string> string st(“this is a dog”)

                      st.size()返回的长度不包括终止空字符

                     用0一个string初始化另一个string对象:string st3(st);

                     比较两个string对象用==:两者相等返回true,否则返回false===>if(str1==str2)

                     拷贝一个字符串用=:str2=str1;

                     连接两个字符串用+:string st3=st2+st1;或者st2+=st1;

混合使用C风格的字符串以及string对象:

                   const char* pc=",";

                   string s1("hello");

                   string s2("world");

                   string s3=s1+pc+s2+"\n";   机理:string类型能够自动将C风格的字符串转换成string对象

可以将C风格的字符串赋值给一个string对象:

                 string s1;

                 const char *pc="this is a dog";

                 s1=pc;

但是反向的string向C风格的字符串的转换不是自动的,必须显式的调用c_str():

               char *str=s1;  //错误

               char *str=s1.c_str();  //几乎正确  字面意思表示:指向字符数组起始处的字符指针

               为了防止字符数组被程序直接处理,c_str()返回了一个指向常量数组的指针

               const char* str=s1.c_str();  //正确

string类型支持下标操作访问单个字符。

              例如:string str("fa.disney.com");

                          int size=str.size();

                          for(int ix=0;ix<size;i++){

                                    if(str[ix]=='.')

                                            str[ix]='_';

                         }

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics