Почему GCC обнаруживает ошибку, когда clang не используется в учебнике? - PullRequest
1 голос
/ 19 сентября 2011

Обучение C с использованием «Системного программирования на C и Unix» Адама Хувера. Я столкнулся с вопросом из четвертой главы, который очень меня удивляет. Вопрос в следующем:

В следующем коде первый 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 the items):

#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);
}  

Я скомпилировал этот пример, используя как gcc, так и clang (на ubuntu linux), и clang не выдает ошибку seg для кода, который должен делать именно это. Я уже некоторое время ломаю голову над этим и не могу себе представить, почему и как это произошло. Любое понимание приветствуется.

Спасибо.

Ответы [ 2 ]

7 голосов
/ 19 сентября 2011
int *p;
funct(p);
printf("%d\n",*p);

Это неправильно.p - это , переданное значением .Так что когда-либо сделанные изменения в функции не влияют на p в main.И разыменование поведения неинициализированного указателя не определено.

Что вам действительно нужно сделать, это -

funct(&p) ; // in main

void funct( int **p ){
   *p = malloc(sizeof(int));
   // ...
}
3 голосов
/ 12 декабря 2011

Это неопределенное поведение и не должно приводить к сбою (или любому другому специфическому поведению). Компилятор может создавать любой код, который ему нравится, для таких случаев. Поскольку вы спросили, почему код, созданный clang, не дает сбоя, нам нужно углубиться в этот код. Вот что выдает clang trunk при компиляции с -O3 на 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

Поскольку p неинициализирован, Clang сегодня решил скомпилировать выражение *p вообще ни к чему. Это законное преобразование, потому что Clang может доказать, что выражение имеет неопределенное поведение. В этом случае печатаемое значение соответствует тому, что попадает в регистр %esi во время вызова printf (на моем аппарате это -1). Возможно, это не то, что вы ожидали, но это характер неопределенного поведения!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...