在尝试使用C语言中的字符串数组步进的方法时,我开发了以下小程序:
#include#include #include typedef char* string; int main() { char *family1[4] = {"father", "mother", "son", NULL}; string family2[4] = {"father", "mother", "son", NULL}; /* Loop #1: Using a simple pointer to step through "family1". */ for (char **p = family1; *p != NULL; p++) { printf("%s\n", *p); } putchar('\n'); /* Loop #2: Using the typedef for clarity and stepping through * family2. */ for (string *s = family2; *s != NULL; s++) { printf("%s\n", *s); } putchar('\n'); /* Loop #3: Again, we use the pointer, but with a unique increment * step in our for loop. This fails to work. Why? */ for (string s = family2[0]; s != NULL; s = *(&s + 1)) { printf("%s\n", s); } }
我的具体问题涉及Loop#3的失败.当通过调试器运行时,循环#1和#2成功完成,但最后一个循环因未知原因而失败.我不会在这里问这个,除了这个事实表明我对"&"运算符有一些严重的误解.
我的问题(和当前的理解)是这样的:
family2
是一个指向char的数组.因此,当s
设置为family2[0]
我们(char*)
指向"父亲"时.因此,服用&s
应该给我们相当于family2
,指向family2
预期指针衰减后的第一个元素.那么,为什么不
*(&s + 1)
按预期指向下一个元素呢?
非常感谢,
生活危机
编辑 - 更新和经验教训:
以下列表是所有相关事实和解释的摘要,这些事实和解释解释了为什么第三个循环不像前两个循环那样起作用.
s
是一个单独的变量,它保存变量的值(指向char的指针)的副本family2[0]
.即,这两个等效值位于内存中的SEPARATE位置.
family2[0]
最多family2[3]
是内存的连续元素,并且s
在此空间中没有存在,尽管它确实包含在family2[0]
循环开始时存储的相同值.
前两个事实意味着&s
并且&family2[0]
不相等.因此,添加一个&s
将返回指向未知/未定义数据的指针,而添加一个&family2[0]
将&family2[1]
根据需要提供给您.
此外,第三个for循环中的更新步骤实际上并不会导致在每次迭代时s在内存中前进.这是因为&s
在循环的所有迭代中都是恒定的.这是观察到的无限循环的原因.
感谢每个人的帮助!
lifecrisis
执行s = *(&s + 1)
此操作时,变量s
是隐式作用域中的局部变量,仅包含循环.当您这样做时,&s
您将获得该局部变量的地址,该地址变量与任何阵列无关.
与前一个循环的不同之处在于,有s
一个指向数组中第一个元素的指针.
为了更加"图解"地解释它在最后一个循环中所拥有的东西就像是
+----+ +---+ +------------+ | &s | ---> | s | ---> | family2[0] | +----+ +---+ +------------+
也就是说,&s
指向s
并s
指向family2[0]
.
当你&s + 1
有效地拥有类似的东西时
+------------+ | family2[0] | +------------+ ^ | +---+---- | s | ... +---+---- ^ ^ | | &s &s + 1
图片有很多帮助:
+----------+ | "father" | +----------+ +----------+ +-------+ NULL /-----------?1000 | "mother" | | "son" | ? +-----+ ? +----------+ +-------+ | | s | ? | 2000 2500 | +-----+ | ? ? | 6000 6008 +----------------+----------------+--------------+--------------+ | family2[0] | family2[1] | family2[2] | family2[3] | +----------------+----------------+--------------+--------------+ 5000 5008 5016 5024 ( &s refers to 6000 ) ( &s+1 refers to 6008 but ) ( *(&s+1) invokes UB )
为简单起见,地址被选为随机整数
这里的事情是,虽然两者s
并family2[0]
指向字符串的相同的基地址字面"father"
,指针不互相关联,而且它们的存储自己不同的存储位置.*(&s+1) != family2[1]
.
你这样做*(&s + 1)
是因为你&s + 1
是一个你不应该篡改的内存位置,也就是说,它不属于你创建的任何对象.你永远不知道那里存储了什么=>未定义的行为.
感谢@ 2501指出了几个错误!