ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

char p[]= “hello“;q = p; 复制的是字符串吗?C 的“举隅法“陷阱

char p[]= “hello“;q = p; 复制的是字符串吗?C 的“举隅法“陷阱 参考Andrew Koenig《C 陷阱与缺陷第二版》3.7节目录p 里装的不是字符串q p 复制了什么想要副本得用数组进内存看看p 和它指向的数据是两个地方地址相等 ≠ 内容相等先看三行代码char *p, *q; p xyz; q p;一个问题现在内存里有几个xyz不是两个是一个。书里管这叫举隅法作者先讲了个修辞学名词。这个词我还是头一次见不过这个词讲了一个简单的道理。举隅法synecdoche是一种文学修辞手段《牛津英语词典》的解释是以含义更宽泛的词语来代替含义相对较窄的词语或者相反例如以整体代表部分或者以部分代表整体。然后他说《牛津英语词典》中这一词条的解释倒是恰如其分地描述了 C 语言中一个常见的陷阱混淆指针与指针所指的数据。对于字符串的情形编程者更是经常犯这种错误。把指向字符串的指针当成字符串就是 C 里的举隅法。其实也不止是字符串同样的也有把“指向数组的指针”数组名当成“数组”下面按书里的顺序一步步把这件事拆开。p 里装的不是字符串char *p, *q; p xyz;书里写得很直接尽管某些时候我们可以不妨认为上面的赋值语句使得 p 的值就是字符串 xyz然而实际情况并不是这样记住这一点尤其重要。实际上p 的值是一个指向由x、y、z和\0 4 个字符组成的数组的起始元素的指针。注意最后那半句4 个字符。xyz是 3 个字母但内存里还有第 4 个字节\0。用sizeof一眼就能看出这是两个东西#include stdio.h int main(void) { char *p xyz; printf(sizeof(p) %d\n, (int)sizeof(p)); printf(sizeof(\xyz\) %d\n, (int)sizeof(xyz)); return 0; }sizeof(p) 8 sizeof(xyz) 48 字节的指针和4 字节的字符数组。p 里装的是后者的地址不是后者本身。q p 复制了什么q p;p 和 q 现在是两个指向内存中同一地址的指针。这个赋值语句并没有同时复制内存中的字符。书里配了张图看清楚这张图只有箭头被复制了一份底下那个x y z \0还是原来那一个。动手验证。先造一块自己的内存用数组再让两个指针都指过去#include stdio.h int main(void) { char s[] xyz; char *p s; char *q p; q[0] X; printf(s %s, p %s, q %s\n, s, p, q); return 0; }s Xyz, p Xyz, q Xyz只改了q[0]s、p、q三个全变了。因为这里压根只有一个地方存着那四个字符三个名字都指着它。动一个就是动全部。想要副本得用数组对比这两个声明char s[] xyz; /* 数组在栈上抄一份 x y z \0 */ char *p xyz; /* 指针只记下字面量的地址 */第一个会真的腾出一块内存把xyz抄进去。第二个只是记了个地址。所以下面这段是安全的s有自己的副本随便改#include stdio.h int main(void) { char s[] xyz; s[0] X; printf(%s\n, s); return 0; }Xyz记住这条分界线s是数据p是数据的地址。书里的结论Koenig 把这一节的结论单列了一行我们需要记住的是复制指针并不同时复制指针所指向的数据。q p;之后你多了一个箭头不是多了一份数据。到这里书里的内容就没有了。不过可以再挖掘一点现象进内存看看p 和它指向的数据是两个地方前面说p 里装的是地址那这个地址在哪#include stdio.h int main(void) { char *p xyz; printf(p %p\n, (void *)p); printf(p %p\n, (void *)p); printf(*p %c\n, *p); return 0; }p 0000005FC917F9E0 p 00007FF619CC1000 *p xp——变量 p 自己住在哪儿。0000005F开头这是栈区。p——p 指向的数据在哪儿。00007FF6开头跟栈差了十万八千里。*p—— 那个地址上第一个字节的内容x。变量 p和p 指向的数据是两个完全独立的地方中间只靠 p 里存的那 8 个字节连着。这下书里那句混淆指针与指针所指的数据就变成了能看见的东西。地址相等 ≠ 内容相等既然 p、q 里装的都是地址比较的时候就要当心#include stdio.h #include string.h int main(void) { char *p xyz; char *q xyz; char buf[8]; strcpy(buf, p); printf(p q : %d\n, p q); printf(strcmp(p,q) 0 : %d\n, strcmp(p, q) 0); printf(p buf : %d\n, p buf); printf(strcmp(p,buf)0 : %d\n, strcmp(p, buf) 0); return 0; }p q : 0 strcmp(p,q) 0 : 1 p buf : 0 strcmp(p,buf)0 : 1p q是0两个地址不一样strcmp(p, q) 0是1两个字符串的内容一样。buf那一组也一样。比的是地址strcmp比的是内容。想判断两个字符串一不一样用是错的——它只告诉你是不是同一份数据不告诉你长得是不是一样。
返回列表