字符串和字符串函数
字符串末尾以\0结束。用双引号括起来的属于静态存储类别。只会被存储一次,在整个程序生命周期内存在。被视为指向该位置的指针。特别注意的是\0是char形式的空字符。不是数字字符0。
字符串有两种创建方式,见程序
11 int main(int argc, const char *argv[])
12 {
13 ¦ char *name = "Jack";
14
15 ¦ char arr_name[] = "Jack";
16
17
18 ¦ return 0;
19 }
这两种方式有什么不同呢?
指针形式是把字符串常量的地址赋给了指针,数组形式是对字符串常量创建了一份新的拷贝。下面用程序来验证一下。
12 int main(int argc, const char *argv[])
13 {
14 ¦ char *name = "Jack";
15
16 ¦ char arr_name[] = "Jack";
17
18 ¦ printf("name = %p\n",name);
19 ¦ printf("arr_name = %p\n",arr_name);
20 ¦ printf("Jack = %p\n","Jack");
21 ¦
22 ¦ return 0;
23 }
name = 0x8048590
arr_name = 0xbfb29d57
Jack = 0x8048590
程序很好的验证了上面的推论。由此可以得出,指针形式是字符串常量的地址,数组形式是新的拷贝。
那这两者使用上有什么区别呢,很容易就能看出,指针形式无法修改字符串,因为是在常量区,而数组形式可以进行修改,因为是新的拷贝,数组创立的空间,并不在常量区。如果想修改一个字符串,那就用数组形式,如果不想那就用指针形式。
指针形式是否就真的不能修改呢?我还是有点疑问,再做个实验。
12 int main(int argc, const char *argv[])
13 {
14 ¦ char *name = "Jack";
15
16 ¦ char arr_name[] = "Jack";
17
18 ¦ printf("name = %p\n",name);
19 ¦ printf("arr_name = %p\n",arr_name);
20 ¦ printf("Jack = %p\n","Jack");
21 ¦
22 ¦ *name = 'P';
23 ¦
24 ¦ return 0;
25 }
name = 0x80485a0
arr_name = 0xbf8ffd97
Jack = 0x80485a0
Segmentation fault
可以明显的看到,段错误了,编译器不允许这么做,其实也不难想为什么编译器不允许这么做了,因为字符串常量只是个地址,可以多出引用,如果这里允许修改的话,别处引用的值也发生变化了,对程序会有不可预知的影响。
由上面同样可以推断出字符串数组的情况了。那再来看看又有啥不同。
12 int main(int argc, const char *argv[])
13 {
14 ¦ char *name[2] = {"Jack", "Tom"};
15
16 ¦ char arr_name[][5] = {"Jack", "Tom"};
17
18 ¦ printf("name = %p\n",name);
19 ¦ printf("arr_name = %p\n",arr_name);
20
21 ¦
22 ¦ printf("sizeof name %d\n",sizeof(name));
23 ¦ printf("sizeof arr_name %d\n",sizeof(arr_name));
24 ¦ return 0;
25 }
name = 0xbfed8238
arr_name = 0xbfed822e
Jack = 0x8048560
sizeof name 8
sizeof arr_name 10
可以看出,name指针和 arr_name 地址并不相同,和字符串一样,指针是指向常量区的,而数组是创建的拷贝。那再验证一下是否再常量区,是否可以修改。要特别注意的是这里的name是一个二级指针。
12 int main(int argc, const char *argv[])
13 {
14 ¦ char *name[2] = {"Jack", "Tom"};
15
16 ¦ char arr_name[][5] = {"Jack", "Tom"};
17
18 ¦ printf("name = %p\n",name);
19 ¦ printf("arr_name = %p\n",arr_name);
20
21 ¦
22 ¦ printf("sizeof name %d\n",sizeof(name));
23 ¦ printf("sizeof arr_name %d\n",sizeof(arr_name));
24 ¦
25 ¦ *name = "Tom";
26 ¦
27 ¦ printf("%s\n",*name);
28 ¦ return 0;
29 }
name = 0xbfea1618
arr_name = 0xbfea160e
sizeof name 8
sizeof arr_name 10
Tom
很奇怪吧,不是说不能修改吗?这里Jack 明明被替换了啊,哈其实这个不是修改,是指针改变了指向,指向了另外一个字符串常量 Tom.这里要具体修改的话就得用到二级指针了,下面来验证下是否可以修改。
12 int main(int argc, const char *argv[])
13 {
14 ¦ char *name[2] = {"Jack", "Tom"};
15
16 ¦ char arr_name[][5] = {"Jack", "Tom"};
17
18 ¦ printf("name = %p\n",name);
19 ¦ printf("arr_name = %p\n",arr_name);
20
21 ¦
22 ¦ printf("sizeof name %d\n",sizeof(name));
23 ¦ printf("sizeof arr_name %d\n",sizeof(arr_name));
24 ¦
25 ¦ **name = 'T';
26 ¦
27 ¦ printf("%c\n",**name);
28 ¦ return 0;
29 }
name = 0xbfde3368
arr_name = 0xbfde335e
sizeof name 8
sizeof arr_name 10
Segmentation fault
哈,段错误,验证了常量区,不可修改。
另外,指针指向的字符串常量是否是连续的地址呢?来验证下看看。
12 int main(int argc, const char *argv[])
13 {
14 ¦ char *name[2] = {"Jack", "Tom"};
15
16 ¦ char arr_name[][5] = {"Jack", "Tom"};
17
18 ¦ printf("name = %p\n",name);
19 ¦ printf("arr_name = %p\n",arr_name);
20
21 ¦
22 ¦ printf("sizeof name %d\n",sizeof(name));
23 ¦ printf("sizeof arr_name %d\n",sizeof(arr_name));
24 ¦
25 ¦ printf("name[0] = %p, name[1] = %p\n",name[0],name[1]);
26 ¦
27 ¦ ¦
28 ¦ return 0;
29 }
name = 0xbf920fa8
arr_name = 0xbf920f9e
sizeof name 8
sizeof arr_name 10
name[0] = 0x8048560, name[1] = 0x8048565
jack字符串加上\0正好是5个字节,貌似是连续的。其实细想一下不是必须是连续的,上面做过一个实验改变指针的指向的,就明白了,这里指向的地址不一定是连续的。另外还可以看出,字符串指针的存储效率更高,因为指向的是单个字符串,不浪费空间。而字符串数组的话每个字符串都只能以数组内最大的元素算,浪费了空间。
来总结一下吧,字符串数组和字符串指针的区别,字符串数组是单独字符串的拷贝,可以修改内容,字符串指针是字符串常量的地址,不可以修改内容。想修改内容的话用字符串数组,不用修改用指正,指针的存储效率高于数组。