2

在提出我的问题之前,我想引用“专家 C 编程”[第 276 页,最后一段]:

“Illiffe 向量数据结构的美妙之处在于,它允许将指向字符串的任意指针数组传递给函数,但只能传递指针数组和指向字符串的指针。这是因为字符串和指针都具有显式输出的约定可以用作结束标记的边界值(分别为 NUL 和NULL )。”

所以,我从上面的文字中了解到的是,如果有一个指针数组,它们有明确的越界值,比如 NULL。(纠正我,如果我错了......)

所以,它让我想知道指针数组的默认值是什么(认为指针数组的最后一个指针为 NULL)。在下面的代码片段中尝试过,结果非常不同。

    int *x[2];
    printf("%p %p",x[0],x[1]);

输出为:(零)0x400410

    int *x[3];
    printf("%p %p %p",x[0],x[1],x[2]);

输出为:0xf0b2ff 0x400680(无)

    int *x[4];
    printf("%p %p %p %p", x[0],x[1],x[2],x[3]);

输出为:(nil) 0x4003db 0x7fffe48e4776 0x4006c5

因此,通过上述输出,很明显有一个明确的越界(nil)值分配给其中一个指针(一个指针是 NIL),但它真的是结束标记吗?不。

它是 C 语言的那些“实现定义”的东西之一吗?

我在 Ubuntu 机器上使用 GCC 编译器(4.6.3)。

4

5 回答 5

4

它是 C 语言的那些“实现定义”的东西之一吗?

不,这不是实现定义的——它是简单的“未定义”。对于所有类型的数组也是如此:在显式初始化之前,您在其中看到的值是未定义的。

我从上面的文字中了解到的是,如果有一个指针数组,它们具有明确的越界值,例如NULL.

作者想说的是,有一个值(特别是NULL值)可以用来标记指针数组中的“无值”。作者并不是要暗示这样一个无值标记会被默认放置到一个指针数组中。

于 2013-09-12T19:34:02.180 回答
4

具有自动存储持续时间的数组或任何对象(即,在没有关键字的函数体中定义的任何对象static)没有默认初始值,除非您指定一个。它的初始值是垃圾,在给它赋值之前你不能访问那个值。

具有静态存储持续时间的对象(即,在任何函数之外和/或使用static关键字定义的任何对象)初始化为零,应用“零”的含义(整数为 0,浮点数为 0.0,指针为空)递归到子对象。

您可以使用初始化程序来确保将指针对象设置为空指针或您喜欢的任何值:

int *x[2] = { NULL, NULL };

或者,更简单地说:

int *x[2] = { 0 }; /* sets first element to 0, which is converted to a null
                      pointer; other elements are implicitly set to null
                      pointers as well */
于 2013-09-12T19:39:47.887 回答
2

您误读了“专家 C 编程”中的引文。那里的关键短语如下:

This is because both strings and pointers have the *convention* of an explicit 
out-of-bound value (NUL and NULL, respectively).

有一个字符串数组是可能的,甚至是常规的,这样最后一个指针设置为NULL. 这可以让人们在不知道数组中有多少元素的情况下很容易地遍历数组:

char* dwarves[] = { "Dopey",
                "Grumpy",
                "Sleepy",
                "Happy",
                "Sneezy",
                "Bashful",
                "Doc",
                NULL 
                };

但是您必须将最后一个指针显式设置为NULL. 这样的结构很有用,因为它们允许编写优雅的代码。因此,如果您想打印或以其他方式操作数组,则无需担心其中有多少字符串,因为NULL指针会发出结束信号:

for (char** pWalk = dwarves; *pWalk; pWalk++)
    printf ("%s\n", *pWalk);

这种特殊类型的不规则数组结构的美妙之处在于,根据定义,字符串具有内置的NUL终止符,并且指针数组以 终止NULL,因此两个维度的端点都是已知的。但是,NULL作为数组中最后一个指针的指针并不是语言内置的。它必须明确设置。如果不这样做,就相当于声明一个数组,char但不使用 a 终止它NUL

char myString[] = { 'H', 'e', 'l', 'l', 'o' }    // No NUL termination

就像如果你想以任何有用的方式操作它,你必须知道这个数组中有多少个字符,如果没有NULL指针数组末尾的 ,操作它会更加困难。

这就是 Peter van der Linden 在您引用的关于 Illiffe 数据结构的段落中所说的全部内容。

于 2013-09-12T20:13:52.423 回答
1

C 中没有要求任何局部变量都应该具有任何“默认”值。因此,当编译器保留两个(或三个)内存位置时,初始值就是这些内存位置之前包含的任何值 - 不会有任何默认初始化。

于 2013-09-12T19:33:46.280 回答
0

除非您的数组是在文件范围内(在任何函数之外)或使用static关键字声明的,否则数组内容将不会被初始化;每个元素将包含一些随机位模式,这些位模式可能对应于或不对应于有效地址。

如果您的数组是在文件范围内或使用static关键字声明的,则每个元素将被隐式初始化为NULL. 请注意,尝试取消引用 NULL 指针会导致未定义的行为,因此您需要在对其进行操作之前检查您的指针是否为 NULL。

空指针表示定义明确的“无处”,保证与任何有效的内存地址比较不相等。注意有一个空指针常量1和一个空指针2,两者不一定相同。在您的源代码中,宏NULL设置为空指针常量。在翻译过程中,源代码中的每次出现NULL都会被真实的空指针值替换。

存在除 NULL 以外的无效指针值;只是 NULL 是定义明确的,并且在任何地方都一样。


1. 在指针上下文中使用的任何 0 值整数表达式。可以是裸0,或(void *) 0,或其他计算结果为 0 的东西
。 2. 平台用来表示空指针的值,它不必为 0。

于 2013-09-12T19:52:51.807 回答