Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/66.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么GCC seg会在clang没有在教科书上练习的地方出错?_C_Gcc_Clang - Fatal编程技术网

为什么GCC seg会在clang没有在教科书上练习的地方出错?

为什么GCC seg会在clang没有在教科书上练习的地方出错?,c,gcc,clang,C,Gcc,Clang,使用Adam Hoover的“使用C和Unix进行系统编程”学习C。我遇到了第四章的问题,这让我非常困惑。问题如下: 在下面的代码中,到达了第一个printf() 生成输出“14”,但生成第二个printf() 可能导致总线错误或分段故障。为什么? 书中的原始代码: main() { int *p; funct(p); printf("%d\n",*p); } funct(int *p2) { p2=(int *)malloc(4); *p2=14; printf("%

使用Adam Hoover的“使用C和Unix进行系统编程”学习C。我遇到了第四章的问题,这让我非常困惑。问题如下:

在下面的代码中,到达了第一个printf() 生成输出“14”,但生成第二个printf() 可能导致总线错误或分段故障。为什么?

书中的原始代码:

main()
{ 
  int *p;
  funct(p);
  printf("%d\n",*p);
}
funct(int *p2)
{
  p2=(int *)malloc(4);
  *p2=14;
  printf("%d\n",*p2);
}
我稍微修改过的“调试”(printf all things)版本:

#包括
#包括
无效函数(int*p2);
int main(){
int*p;
printf(“主p-地址:%p\n”,p);
函数(p);
printf(“主p-地址:%p\n”,p);
printf(“主p值:%d\n”,*p);
}  
无效函数(int*p2){
printf(“函数(前malloc)p2-地址:%p\n”,p2);
p2=(int*)malloc(4);
printf(“函数(后malloc)p2-地址:%p\n”,p2);
*p2=14;
printf(“函数p2值:%d\n”,*p2);
}  
我已经使用gcc和clang(在ubuntulinux上)编译了这个示例,clang不会为应该这样做的代码产生seg错误。我对此困惑了一段时间,无法想象这是为什么或如何。欢迎有任何见解

谢谢

int *p;
funct(p);
printf("%d\n",*p);
这是错误的<代码>p是通过值传递的。因此,对函数所做的修改不会影响
main
中的
p
。取消引用未初始化的指针行为是未定义的

你真正需要做的是-

funct(&p) ; // in main

void funct( int **p ){
   *p = malloc(sizeof(int));
   // ...
}

这是未定义的行为,不一定会导致崩溃(或任何其他特定行为)。编译器可以自由地为这种情况生成它喜欢的任何代码。既然您问了为什么由clang生成的代码不会崩溃,我们就需要深入研究该代码。以下是在x86_64上使用
-O3
编译时产生的叮当声:

main:                               # @main
    pushq   %rbp
    movq    %rsp, %rbp              # Build stack frame
    movl    $.L.str, %edi
    movl    $14, %esi
    xorb    %al, %al                # no XMM registers used by varargs call
    callq   printf                  # printf(%edi = "%d\n", %esi = 14)
    movl    $.L.str, %edi
    xorb    %al, %al                # no XMM registers used by varargs call
    callq   printf                  # printf(%edi = "%d\n", %esi = ?)
    xorl    %eax, %eax
    popq    %rbp
    ret                             # return %eax = 0

由于
p
未初始化,clang今天选择将表达式
*p
编译为零。这是一种合法的转换,因为clang可以证明表达式具有未定义的行为。然后,正在打印的值就是在调用
printf
%esi
寄存器中结束的值(在我的机器上,恰好是
-1
)。这可能不是您所期望的,但这是未定义行为的本质

如果您在x64上编译,
p2=(int*)malloc(4)不够大。您正在将内存分配给传递给函数的指针的副本,而不是
main()
中的指针。取消对未初始化指针的引用是一个错误。UB表示它可能会崩溃,也可能不会。关于堆栈溢出的C问题中有多少部分涉及未定义的行为?这是一个实际的练习?哇!如果要在
funct()
中修改地址,需要传递
p
&p
)的地址。我知道代码有点可怕。我相信这次演习的目的是找出问题所在。我不明白的是,为什么这段可怕的代码在使用clang编译时不会出现seg错误。为了清楚起见,我认为它应该像使用gcc编译时那样对fault进行seg。
main:                               # @main
    pushq   %rbp
    movq    %rsp, %rbp              # Build stack frame
    movl    $.L.str, %edi
    movl    $14, %esi
    xorb    %al, %al                # no XMM registers used by varargs call
    callq   printf                  # printf(%edi = "%d\n", %esi = 14)
    movl    $.L.str, %edi
    xorb    %al, %al                # no XMM registers used by varargs call
    callq   printf                  # printf(%edi = "%d\n", %esi = ?)
    xorl    %eax, %eax
    popq    %rbp
    ret                             # return %eax = 0