简体   繁体   English

为什么GCC seg会在教科书上没有clang的情况下出错?

[英]Why does GCC seg fault where clang does not on a textbook exercise?

Learning C using "System Programming with C and Unix" by Adam Hoover. 使用Adam Hoover的“使用C和Unix进行系统编程”来学习C。 I have come across question from Chapter 4 that puzzles me greatly. 我遇到了来自第4章的问题,这使我非常困惑。 The question is as follows: 问题如下:

In the following code, the first printf() reached produces the output "14," but the second printf() can cause a bus error or a segmentation fault. 在下面的代码中,到达的第一个printf()产生输出“ 14”,但是第二个printf()可能导致总线错误或分段错误。 Why? 为什么?

The original code from the book: 书中的原始代码:

main()
{ 
  int *p;
  funct(p);
  printf("%d\n",*p);
}
funct(int *p2)
{
  p2=(int *)malloc(4);
  *p2=14;
  printf("%d\n",*p2);
}

My slightly modified "debugging" (printf all the things) version: 我稍作修改的“调试”(printf所有东西)版本:

#include <stdio.h>
#include <stdlib.h>

void funct(int *p2);

int main(){
    int *p;
    printf("main p - address: %p\n", p);

    funct(p);
    printf("main p - address: %p\n", p);
    printf("main p value: %d\n", *p);
}  

void funct(int *p2){
    printf("funct (pre malloc) p2 - address: %p\n", p2);

    p2 = (int *)malloc(4);
    printf("funct (post malloc) p2 - address: %p\n", p2);

    *p2 = 14;
    printf("funct p2 value: %d\n", *p2);
}  

I have compiled this sample using both gcc and clang (on ubuntu linux) and clang does not produce a seg fault for code that is supposed to do just that. 我已经使用gcc和clang(在ubuntu linux上)编译了此示例,并且clang不会为应该这样做的代码产生seg错误。 I have puzzled over this for awhile now and can not imagine the why or how of this. 我对此感到困惑了一段时间,无法想象为什么或如何这样做。 Any insight welcome. 欢迎任何有识之士。

Thanks. 谢谢。

int *p;
funct(p);
printf("%d\n",*p);

This is wrong. 错了 p is passed by value . p 通过值传递 So what ever made modification in the function doesn't affect p in the main . 因此,对函数进行的修改不会影响main p And dereferencing an uninitialized pointer behaviour is undefined. 并且取消引用未初始化的指针行为是未定义的。

What you actually need to do is - 您实际需要做的是-

funct(&p) ; // in main

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

This is undefined behaviour and doesn't have to result in a crash (or any other specific behaviour). 这是未定义的行为,不必导致崩溃(或任何其他特定行为)。 A compiler is free to produce any code it likes for such cases. 在这种情况下,编译器可以自由产生喜欢的任何代码。 Since you asked why the code produced by clang doesn't crash, we'll need to dig into that code. 既然您问了为什么clang产生的代码不会崩溃,所以我们需要深入研究该代码。 Here's what clang trunk produces when compiling with -O3 on x86_64: 以下是在x86_64上使用-O3进行编译时产生的clang干线:

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

Since p is uninitialized, clang has chosen, today, to compile the expression *p to nothing at all. 由于p未初始化,因此clang今天选择将表达式*p完全编译为空。 This is a legitimate transformation, because clang can prove that the expression has undefined behaviour. 这是合法的转换,因为clang可以证明表达式具有未定义的行为。 The value being printed is then whatever ends up in the %esi register at the time of the printf call (on my machine, that happens to be -1 ). 然后,在printf调用时(在我的机器上,恰好是-1 ),被打印的值就是%esi寄存器中最终的值。 This may not be what you expected, but that is the nature of undefined behaviour! 这可能不是您所期望的,但这就是不确定行为的本质!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM