2013-05-27 5 views
4

Ok, так что все началось здесь: Unsigned integer and unsigned char holding same value yet behaving differently why?Compiler сравнения генерации кода

Я написал следующее заявление, чтобы понять, что происходит за кулисами (то есть, как компилятор обрабатывает этот вопрос).

#include <stdio.h> 

int main() 
{ 
    { 
    unsigned char k=-1; 
    if(k==-1) 
    { 
    puts("uc ok\n"); 
    } 
    } 

    { 
    unsigned int k=-1; 
    if(k==-1) 
    { 
    puts("ui ok"); 
    } 
    } 
} 

И при компиляции с GCC, как:

gcc -O0 -S -masm=intel h.c 

я получаю следующий файл сборки:

.file "h.c" 
    .intel_syntax noprefix 
    .section  .rodata 
.LC0: 
    .string "ui ok" 
    .text 
    .globl main 
    .type main, @function 
main: 
.LFB0: 
    .cfi_startproc 
    push rbp 
    .cfi_def_cfa_offset 16 
    .cfi_offset 6, -16 
    mov  rbp, rsp 
    .cfi_def_cfa_register 6 
    sub  rsp, 16 
    mov  BYTE PTR [rbp-1], -1 
    mov  DWORD PTR [rbp-8], -1 
    cmp  DWORD PTR [rbp-8], -1 
    jne  .L3 
    mov  edi, OFFSET FLAT:.LC0 
    call puts 
.L3: 
    leave 
    .cfi_def_cfa 7, 8 
    ret 
    .cfi_endproc 
.LFE0: 
    .size main, .-main 
    .ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3" 
    .section  .note.GNU-stack,"",@progbits 

И к моему большому удивлению, первый чек даже не ТАМ.

Но, если я составляю то же самое с помощью Microsoft Visual C++ (2010), я получаю (я отрезан много мусора из этого списка, поэтому это не так действует):

00B81780 push  ebp 
00B81781 mov   ebp,esp 
00B81783 sub   esp,0D8h 
00B81789 push  ebx 
00B8178A push  esi 
00B8178B push  edi 
00B8178C lea   edi,[ebp-0D8h] 
00B81792 mov   ecx,36h 
00B81797 mov   eax,0CCCCCCCCh 
00B8179C rep stos dword ptr es:[edi] 
00B8179E mov   byte ptr [k],0FFh 
00B817A2 movzx  eax,byte ptr [k] 
00B817A6 cmp   eax,0FFFFFFFFh 
00B817A9 jne   wmain+42h (0B817C2h) 
00B817AB mov   esi,esp 
00B817AD push  offset string "uc ok\n" (0B857A8h) 
00B817B2 call  dword ptr [__imp__puts (0B882ACh)] 
00B817B8 add   esp,4 
00B817BB cmp   esi,esp 
00B817BD call  @ILT+435(__RTC_CheckEsp) (0B811B8h) 
00B817C2 mov   dword ptr [k],0FFFFFFFFh 
00B817C9 cmp   dword ptr [k],0FFFFFFFFh 
00B817CD jne   wmain+66h (0B817E6h) 
00B817CF mov   esi,esp 
00B817D1 push  offset string "ui ok" (0B857A0h) 
00B817D6 call  dword ptr [__imp__puts (0B882ACh)] 
00B817DC add   esp,4 
00B817DF cmp   esi,esp 
00B817E1 call  @ILT+435(__RTC_CheckEsp) (0B811B8h) 

Вопрос в том, почему это происходит? Почему GCC «пропускает» первый IF и как я могу заставить GCC не пропускать его? Оптимизация отключена, но кажется, что она все еще оптимизирует что-то ...

+0

Потому что флаг '-O0' не * фактически * отключает все оптимизации, 2. оптимизатор GCC умнее, чем у MSVC. –

+0

@ H2CO3, то как я могу отключить ВСЕ оптимизации? даже те из статического анализа? :) – fritzone

+0

Вы получаете те же результаты, если используете -O0, и если вы вообще не устанавливаете -O в командной строке? –

ответ

7

Мое предположение (я не разработчик GCC) состоит в том, что он делает достаточно статического анализа, чтобы доказать себе, что первый тест if никогда не был правда.

Это не должно быть слишком сложно, поскольку между инициализацией и тестом нет кода, нет никакого побочного эффекта или внешнего объекта, который мог бы изменить переменную.

Просто для любопытства попробуйте сделать переменную static и/или volatile, чтобы убедиться, что что-то изменилось.

+1

Маркировка этого изменчивого должна работать. Если нет, объявите int, затем volatile указатель на него, а затем обновите значение с помощью летучего указателя. – slugonamission

+1

Задание статического или неустойчивого фактически сделал «недостающий код», чтобы появиться :) nice catch! – fritzone

1

Обновлено: Я предполагаю, что char, как тип под базовым (int) типом, просто масштабируется до целочисленного типа для сравнения. (Предполагая, что компилятор принял буквальный как целое, и вообще предпочитает целые слова размера более байты ОДНИ размера)

И как беззнаковое значение, нулевое расширение всегда положителен (обратите внимание на MOVZX вместо подписанного вариант!), поэтому проверка, вероятно, была оптимизирована путем основного постоянного распространения.

Вы можете попытаться заставить литерал быть байтом (литые или суффиксы), например. сравнивая с ((unsigned char) (- 1)), и, возможно, тогда компилятор будет вставлять 1-байтовое сравнение, и результат может быть другим.

2

Похоже, проблема с GCC, хотя, по общему признанию, очень незначительная.

Из GCC's documentation website (курсив мой):

без возможности оптимизации, цель компилятора заключается в снижении затрат на составление и сделать отладку ожидаемых результатов. Заявления независимы: если вы остановите программу с точкой останова между операторами, вы можете затем присвоить новое значение любой переменной или сменить счетчик программ на любой другой оператор в функции и получить точно результаты, ожидаемые от исходного кода ,

Так с -O0 вы должны быть в состоянии поставить контрольную точку между unsigned char k=-1; и if(k==-1), во время этой точки останова изменить k, и ожидают, что отрасль будет принята; но это невозможно с испускаемым кодом.

+0

ОП задает '-O0', который * является * опцией оптимизации, нет? –

+0

@OwenWengerd Я подумал, что сначала и попытался заглянуть в документ, чтобы понять его лучше. Я думаю, что ключ заключается в том, что '-O0' является значением по умолчанию, это то, что применяется, если не используется ни одна другая опция - поэтому нет возможности не предоставлять какой-либо опции оптимизации, и я думаю, что это означает, что документация означает« без какой-либо опции оптимизации » ». – Oak

0

Есть целый ряд тонкостей здесь:

  • Компилятор даже не смотреть на инициализацию к доказать, что условие к == - 1 никогда не может быть правдой в неподписанные символ дело. Дело в том, что 8-битное значение unsigned должно быть увеличено до 32 бит, поскольку правая часть сравнения является целочисленной константой, которая по умолчанию равна 32 битам. Поскольку k без знака, результатом этой акции будет 00000000 00000000 00000000 xxxxxxxx. Константа -1 имеет битовый шаблон 11111111 11111111 11111111 11111111, поэтому не имеет значения, что такое xxxxxxxx, результат сравнения всегда будет ложным.
  • Возможно, я ошибаюсь в этой точке, но считаю, что даже если k было указано как изменчивое, компилятор должен был только загрузить его в регистр (поскольку операция загрузки может вызвать какой-то желаемый побочный эффект в аппаратном обеспечении) , чтобы фактически не выполнять сравнение или не создавать код для недостижимого if-блока.
  • На самом деле, опускание сборки для недостижимого кода полностью соответствует цели -O0, чтобы ускорить процесс компиляции.
  • AFAIK, сравнение беззнаковой и отрицательной констант в любом случае является неопределенным поведением. По крайней мере, просто нет машинной инструкции для правильной обработки дела, и компиляторы не будут вставлять необходимый код, чтобы обрабатывать его в программном обеспечении, как вы можете видеть из разборки. Все, что вы получаете, - это неявное литье между подписанным и unsigned, которое приводит к переполнению целого числа (что само по себе является неопределенным поведением) и сравнению несмешанного знака.
Смежные вопросы