2

我昨天在此处发布了有关指向指针的指针等的指针,并决定将讨论移至新问题答案的评论中。

我的问题是为什么编译器允许这种情况发生:

int *foo = malloc(sizeof(int));
*foo = 8;
int **********blarg = foo;

我收到了一些警告,但是当我像这样对 blarg 进行取消引用时:

*blarg;

它的值是 8。取消引用不应该返回某种类型的空值吗?难道编译器不应该知道只有在取消引用之后,例如:

**********blarg;

是否有实际的整数值,任何较低级别的取消引用只会返回更多指向指针的指针?

空指针对我来说是一个全新的困惑,因为像这样的变量:

void *asdf;

可以指向以下任何类型:

int
char
float
*void
**********void
*******int

你明白了。

所以我的主要问题:

  1. 第一段代码中到底发生了什么?那些指向指针的指针甚至被创建了吗?
  2. 为什么取消引用在第二段代码中起作用?
  3. (而且听起来很愚蠢)如果我可以为任何东西分配任何值并且只要我正确阅读它就可以工作,为什么还要有类型呢?
4

3 回答 3

3

举一个更简单的例子:

int n;
int *p = &n;
int **p2 = p;

使用 gcc 4.7,使用默认选项,我得到:

warning: initialization from incompatible pointer type [enabled by default]

C 标准说尝试初始化p2违反约束的,这意味着需要符合要求的实现才能发出诊断。这并不意味着需要符合要求的实现来拒绝翻译单元。必须拒绝翻译单元(源文件)的唯一情况是它包含#error指令。

因此,通过打印无效初始化程序的警告,gcc 就 ISO C 标准而言已经完成了它的工作。

如果您希望它彻底拒绝它,您可以使用该-pedantic-errors选项,最好与其中一个-std=...选项一起使用。

那就是语言问题。gcc默认可以拒绝这样的程序——那为什么不呢?这是关于 gcc 的问题,而不是关于 C 语言的问题。

自 1989 年 ANSI 发布官方 C 标准之前,gcc 已经存在了很长时间。在 ANSI 之前的日子里,像这样的初始化程序是非法的还不是很清楚。有(并且可能仍然是)相当多的旧代码依赖于这样的结构,并且恰好可以工作。我推测 gcc 开发人员只是没有觉得现在是进行此类更改并冒着破坏现有代码的好时机。

就个人而言,我希望gcc 默认拒绝这样的事情,但您始终可以 (a) 启用更严格的诊断,并且 (b) 密切注意警告。

于 2013-04-11T00:10:16.620 回答
2

这是一个有趣的问题。

归结为实现,拥有类型系统的 C 语言的价值在于以下承诺:“如果您的代码不触发警告,那么它在 C 类型系统的框架中是类型安全的”。程序员可能犯的许多错误都引起了他的注意。反之亦然,如果编译器没有发出警告,程序员可以在一定程度上相信他的代码不包含任何一大类问题。汇编程序员不能这么确定,可能必须仔细检查所有内容。但是 C 程序员只需要仔细检查代码的高级方面,而不是忘记取消引用指针等错误。

(void*)经常在那里使用,在那里不可能或不方便将功能、结构……与特定类型联系起来。示例包括集合、消息传递原语。

您的代码会触发警告,因此编译器当然不会声明您的代码是类型安全的,事实上,访问**blargwhile(int)*blarg == 8会在许多架构上导致内存访问错误。(即使**不是最终的反抗。)

您在询问是否正在创建指向指针的指针:当您说

int **********blarg [...] ; //10-pointer to int

您只创建了一个变量,它是一个指向 int 的 9 指针的指针。这条线没有创建也不应该创建指向 int 的 9 指针,blarg它将指向。

于 2013-04-10T23:49:53.080 回答
2

让我们仔细看看你的代码。我注意到您几乎可以肯定是在 32 位模式下编译(在 32 位机器上或在 32 位模式下的 64 位机器上)。

int *foo = malloc(sizeof(int));
*foo = 8;
int **********blarg = foo;
*blarg;

第一行分配一个指向 的指针foo,该指针指向一个足够大的空间来容纳一个int。第二行将值分配给8指向的整数foo

具有 10 级指针(10 的)的第三行会*生成警告,因为 的类型foo与 的类型不匹配blarg。但是,编译器会尝试确定您所做操作的开头或结尾,并将指针从复制fooblarg.

第四行访问blarg指向的值。结果是一个int *** *** ***(指向 int 的 9 级指针),但它的值是blarg指向的值。由于您看到8的值*blarg,我们可以合理地推断出sizeof(int *) == sizeof(int)——因此我对 32 位编译的观察。如果您在 64 位机器上(甚至是 Windows 64),*blarg将访问 8 个字节而不是 4 个字节,并且额外的 4 个字节可能不会全部为零。

所以,行为是可以预测的,即使你没有预料到。但由于分配给 的类型不匹配,它没有严格定义blarg。您也不能安全地使用**blarg或任何其他更多数量的取消引用*;您将访问无效的内存。

于 2013-04-11T00:10:40.787 回答