我很清楚我的问题与this问题完全相同。
但不幸的是,我有一个问题,任何答案都没有解决。 所以代码是:-
#include <string.h>
int foo(void) {
char bar[128];
char *baz = &bar[0];
baz[127] = 0;
return strlen(baz);
}
问题是:此功能可能输出什么?
运行此代码时,每次给出0,正确的答案是0和127(我仍然不知道为什么?)。
我的问题是,该语句如何甚至有效 我的意思是我们正在计算baz
的长度,其中包含一个内存地址,例如0xb96eb740
,它是一个十六进制数,所以我们正在做的是在此地址上strlen()
来查找它的长度?我的意思是我们如何找到一个地址的长度,它只是一个数字?
我真的感到困惑,并试图在小时内理解它,但仍然无法理解。
答案 0 :(得分:8)
不要卡在地址被传递的事实上。 strlen()
总是使用地址。它的参数是const char *
,即字符串的地址。所有这些呼叫都会传递完全相同的地址:
strlen(baz);
strlen(&bar[0]);
strlen(bar);
baz
被分配了&bar[0]
,因此第一个和第二个是等效的。 An array decays to a pointer to its first element(array == &array[0]
),因此第二和第三等价。
我的意思是我们怎么能找到一个地址的长度,它只是一个数字?
假设您的示例为bar == &bar[0] == baz == (char *) 0xb96eb740
。 strlen()
将首先检查内存位置0xb96eb740是否包含\0
。如果不是,它将检查0xb96eb741。然后是0xb96eb742。然后是0xb96eb743。它将继续顺序检查每个位置,直到找到\0
。
我知道那是真的。但是为什么
strlen(baz)
返回0?
正如链接的问答所解释的,行为是不确定的,因为bar[128]
数组的内容尚未初始化。该数组中可能有任何内容。我们知道的唯一单元格是bar[127]
,它的值设置为\0
。其他所有的都未初始化。
这意味着它们中的任何一个或全部或不包含\0
字符。它可能会随运行而变化,甚至因调用而异。每次致电foo()
时,您都会得到不同的结果。那完全有可能。结果将根据调用foo()
之前堆栈中正存储的数据而有所不同。
运行此代码时,每次都会给出
0
,正确的答案是0
和127
(我仍然不知道为什么?)。
它可以返回0到127之间的任何值。由于行为不确定,您在运行程序时不应过多地了解程序会发生什么返回。如果您再次运行该程序,在foo()
之前校准了一组不同的函数,如果您事先运行了其他程序,如果您更改了编译器,以及在一周的另一天运行了该输出,则输出可能会有所不同。 ,如果您使用其他操作系统等,等等,等等。
答案 1 :(得分:2)
我的问题是,该语句如何有效?我的意思是我们正在计算包含内存地址的baz的长度,比如说一个0xb96eb740,它是一个十六进制数,因此我们在此地址上执行strlen()来查找其长度?
strlen
函数接受一个地址作为参数,其行为是读取存储在该地址的字符。 (它似乎不像您建议的那样尝试读取地址的 of 字符)。如果该字符不是'\0'
,它将读取下一个地址中的字符并查看是否为'\0'
等。
答案 2 :(得分:2)
您的问题的答案是任何事情都会发生。
数组bar
未初始化。仅bar[127]
被显式设置为'\0'
。将未初始化的数组传递给strlen()
,您可以通过传递指向baz
的{{1}}来间接进行操作,该行为未定义。
在实践中,在没有陷阱值的现代体系结构中,函数bar[0]
具有未指定的行为,并且可以根据调用时堆栈包含的内容返回foo()
和0
之间的任何值。
在您的情况下,它返回127
,因为在0
的开头碰巧有一个空字节,但是您不能依靠它,并且对bar
的连续调用可能返回不同的值
如果您运行的程序在 valgrind 下调用了foo()
或某些其他内存清理工具,它可能会抱怨foo()
访问未初始化的内存。
答案 3 :(得分:1)
其他人认为该值是不确定的,所以我直接进行以下讨论:
我的意思是我们如何找到一个仅是数字的地址长度?
你不知道。字符串的长度是通过从您要开始的地址顺序读取内存并查看在击中第一个'\0'
字符之前需要走多远而得出的。下面是一个示例,说明如何实现返回字符串长度的函数:
int strlen(char * str) {
int length=0;
while(str[length] != '\0')
length++;
return length;
}