Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > C/C++: Общие вопросы > Сравнение результатов компиляции gcc и clang


Автор: UriyG 06.05.20, 14:53
Написал маленькую программку, чтобы проверить своё понимание указателей. Компилировал gcc. Не мог понять результат разыменования
указателя "p". Получается *p = 0, а я уверен, что должно быть *p = 10, поскольку указатель "указывает" на массив, который инициализирован.
Долго бился с этим вопросом, ничего не нашел. Пришла идея - попробовать компилятор clang. Я никогда его до этого не использовал. И вот результат. В прилагаемом файле всё можно увидеть. На мой взгляд на лицо неадекватное поведение компилятора gcc. А поскольку в это не могу поверить, вопрос - где я ошибаюсь?
pointers.txt (, : 101)

Автор: Wound 06.05.20, 15:12
У тебя выход за границы массива идет, поэтому и выводится мусор или 0.
Вот тут:

Цитата
printf("\
a[0] = %d\n\
a[1] = %d\n\
a[2] = %d\n\
p = %p\n\
a[0] = %d\n\
&a[0] = %p\n\
*p = %d\n\
&(*p) = %p\n\
a[0] = %d\n\
*(p+0) = %d\n\
*(p++) = %d\n\
*(++p) = %d\n\
*p++ = %d\n\
*p = %d\n", a[0], a[1], a[2], p=a, a[0], &a[0], *p, &(*p), a[0],\
*(p+0), *(p++), *(++p), *p++,*p);

У тебя p инкрементится 3 раза. Последний p = правильный, потому что аргументы функции вычисляются в данном случае с права на лево, соответственно он еще указывает на первый элемент массива. Затем ты его инкрементишь 3 раза, и выводишь еще раз, а он уже указывает на невесть что.
Попробуй переписать вот так:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
        printf("\
                a[0] = %d\n\
                a[1] = %d\n\
                a[2] = %d\n\
                   p = %p\n\
                a[0] = %d\n\
               &a[0] = %p\n\
                  *p = %d\n\
               &(*p) = %p\n\
                a[0] = %d\n\
              *(p+0) = %d\n\
              *(p++) = %d\n\
              *(++p) = %d\n\
                *p++ = %d\n\
                  *p = %d\n", a[0], a[1], a[2], p, a[0], &a[0], *p, &(*p), a[0],\
                   *(p+0), *(p+1), *(1+p), *(p+1),*p);

И все будет работать.

Добавлено
Ну или сделай массив на больше элементов.

Автор: UriyG 06.05.20, 15:59
Вы правы, однако меня шокирует другое. При всех равных условиях разные результаты:



1. clang: a[0] = 10; // Значения первого элемента массива
2. clang: &a[0] = 0x7fffe583d708; // Адрес первого элемента массива
3. clang: &(*p) = 0x7fffe583d708; // Тот же адрес через указатель
4. clang: *p = 10; /*Верно*/

1. gcc: a[0] = 10; // Значения первого элемента массива
2. gcc: &a[0] = 0x7ffd1ef87d40; // Адрес первого элемента массива
3. gcc: &(*p) = 0x7ffd1ef87d40; // Тот же адрес через указатель
4. gcc: *p = 0; /*Не верно*/ //

Шокирует последняя строка - 4. Не могу понять в чем дело? Ведь ошибиться негде, а результат налицо!

Автор: Wound 06.05.20, 16:59
Цитата UriyG @
Шокирует последняя строка - 4. Не могу понять в чем дело? Ведь ошибиться негде, а результат налицо!

Ничего шокирующего в этом нет. Есть стандарт, в нем прописано как должны вести себя те или иные конструкции языка, то что не описывается отдается на откуп компилятору, как реализуют так и будет, плюс есть память и есть режимы выполнения(отладочный/релиз), соответственно на шланге возможно аргументы по другому вычисляются, а в элементе за массивом, мусорном оказалось число 10. Вот и все.

Автор: UriyG 06.05.20, 17:24
Провел еще серию экспериментов, результат такой. Компиляторы clang и gcc выдают одинаковые результаты только при одном варианте - один printf - только одно число. При других вариантах результаты могут быть разные. Причем, скорее всего clang не ошибается, а gcc - путается!

Добавлено
А для меня это - шок! Если в такой простой операции, с "очевидным", как казалось результатом, я имею ввиду операцию разыменования, можно получить совершенно не ожидаемый результат...

Автор: Qraizer 06.05.20, 17:49
Цитата UriyG @
А для меня это - шок!
А не знал, что аргументы функций могут вычисляться в произвольном порядке?

Автор: ЫукпШ 06.05.20, 18:20
Цитата UriyG @
Провел еще серию экспериментов,...
...
А для меня это - шок! Если в такой простой операции, с "очевидным", как казалось результатом, я имею ввиду операцию разыменования, можно получить совершенно не ожидаемый результат...

Просто измени свой исходник так, чтобы вот эти предупреждения
не выдавались.
Цитата

...
g% gcc -Wall -Wextra -Wformat pointers.c -o pointers -std=c11
pointers.c: In function ‘main’:
pointers.c:40:39: warning: operation on ‘p’ may be undefined [-Wsequence-point]
*(p+0), *(p++), *(++p), *p++,*p);
^
pointers.c:40:39: warning: operation on ‘p’ may be undefined [-Wsequence-point]
pointers.c:40:39: warning: operation on ‘p’ may be undefined [-Wsequence-point]
pointers.c:40:39: warning: operation on ‘p’ may be undefined [-Wsequence-point]
pointers.c:40:39: warning: operation on ‘p’ may be undefined [-Wsequence-point]
pointers.c:40:39: warning: operation on ‘p’ may be undefined [-Wsequence-point]
pointers.c:40:39: warning: operation on ‘p’ may be undefined [-Wsequence-point]
...

Тебя же предупреждали...

Автор: UriyG 06.05.20, 19:55
Да, я не знал, что аргументы функций могут вычисляться в произвольном порядке.
Вернее знал, но о printf не подумал.
Огромное спасибо! Это я запомню на всегда!

Добавлено
Цитата UriyG @

Кстати, clang не ошибся, если так можно сказать, ни разу!

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)