简体   繁体   中英

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. I have come across question from Chapter 4 that puzzles me greatly. 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. 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:

#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. 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 . So what ever made modification in the function doesn't affect p in the main . 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. Here's what clang trunk produces when compiling with -O3 on x86_64:

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. This is a legitimate transformation, because clang can prove that the expression has undefined behaviour. 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 ). This may not be what you expected, but that is the nature of undefined behaviour!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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