为什么指针转换的指针隐式指针合法?

时间:2015-06-07 20:26:09

标签: c pointers language-lawyer

我最近在stackoverflow上遇到了一些代码,其中指针指向用于更改已分配内存的指针。在检查代码时,我错误地将一个&符添加到指针,因此创建一个指向指针的指针仍然是编译器愉快地编译并发生运行时错误。作为一个例子

#include <stdio.h>

void func(int **p) {
  printf("Pointer is at %p\n", p);
}

int main(void) {
  int *p = 0;
  func(p);
  func(&p);
  int **pp = &p;
  func(&pp);
  return 0;
}

我确实理解C对指针的限制明显低于C ++,并且在C ++中不允许使用char *buf = malloc(SIZE)之类的内容。我觉得它很方便,因为它在C中发生了很多。

尽管如此,我认为引用的数量是一个很好的错误来源,我想知道为什么人们可能允许这样做,特别是因为intint*不同。此外,我想知道C标准是否说明了这一点。

修改 我在ideone.com下编译了它,可能没有显示警告。我的本地clang编译器以及gcc都会发出警告。不过,为什么当他们代表不同的事情时,他们只是警告。

PS,我觉得在过去的六年中应该问过这样的事情。如果这个重复,我很抱歉找不到。

4 个答案:

答案 0 :(得分:5)

转换合法。更确切地说,没有从int*int**或从int***int**的隐式转换。尝试将int*int***传递给需要int**参数的函数是约束违规;任何符合标准的编译器都必须诊断它。 (诊断消息可能是非致命警告。)

当我使用gcc编译你的代码时,即使使用默认选项(这会使gcc不符合),我会收到几个警告:

c.c: In function ‘func’:
c.c:4:3: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 2 has type ‘int **’ [-Wformat=]
   printf("Pointer is at %x\n", p);
   ^
c.c: In function ‘main’:
c.c:9:3: warning: passing argument 1 of ‘func’ from incompatible pointer type [enabled by default]
   func(p);
   ^
c.c:3:6: note: expected ‘int **’ but argument is of type ‘int *’
 void func(int **p) {
      ^
c.c:12:3: warning: passing argument 1 of ‘func’ from incompatible pointer type [enabled by default]
   func(&pp);
   ^
c.c:3:6: note: expected ‘int **’ but argument is of type ‘int ***’
 void func(int **p) {
      ^

我不知道为什么没有人抱怨它(http://ideone.com/uzeXur)。

答案 1 :(得分:2)

编译器显然没有启用警告:

CardLayout

所以,gcc非常抱怨。该网站可能已禁用警告或吞下它。它既不应该。

对于gcc,您还可以使用int main() { int *p = 0, **p2 = p; ... $ gcc -std=c11 test.c -lncurses test.c: In function ‘main’: test.c:8:21: warning: initialization from incompatible pointer type [enabled by default] int *p = 0, **p2 = p; ^ 或仅某些特定的-Werror将所有警告变为错误。为此,它将是-Werror=<name of warning>

这不是默认的原因可能是历史原因。默认情况下将其设为错误可能会破坏过多的遗留软件或其他破坏的软件。

可能会找到它不会产生错误的原因here in the standard。句子7.如果指针未正确对齐接收指针或取消引用(这里没有这样做),则只有UB。

答案 2 :(得分:1)

我认为对警告的看法很简单。没有ip或我的计算机上的任何额外警告标志gcc给我

-Wall

答案 3 :(得分:1)

使用错误的说明符打印数据类型调用未定义的行为。将%x更改为%p并将printf的参数投射到void *,您将收到警告。

Live Demo