Возможная ошибка с переменными аргументами в mingw64-gcc - PullRequest
0 голосов
/ 02 мая 2018

У меня была досадная ошибка, которую я пытался отследить, затем я создал пример, и я все еще не уверен на 100%, если это проблема компилятора.

Позвольте мне дать вам некоторую информацию о версии, которую я использовал первым.

x86_64-w64-mingw32-g++ --version

x86_64-w64-mingw32-g++.exe (Rev1, Built by MSYS2 project) 7.2.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Я знаю, что это не самая новая версия, но это самая новая версия, которую вы можете получить для MSYS.

Это пример кода:

#include <cstdint>
#include <stdio.h>
#include <string.h>
#include <cstdarg>

void test1(){
    uint64_t a = 0x3333333333333333;
    uint64_t b = 1;
    uint64_t c = 2;
    uint64_t d = 3;
    printf("output should be:\n3 2 1 0 3333333333333333\n");
    printf("but output is:\n%llx %llx %llx %llx %llx\n",d,c,b,0,a);
}
void test(uint64_t x1,uint64_t x2,uint64_t x3,uint64_t x4,uint64_t x5,uint64_t x6,
uint64_t x21,uint64_t x22,uint64_t x23,uint64_t x24,uint64_t x25,uint64_t x26,
uint64_t x31,uint64_t x32,uint64_t x33,uint64_t x34,uint64_t x35,uint64_t x36,
uint64_t x41,uint64_t x42,uint64_t x43,uint64_t x44,uint64_t x45,uint64_t x46){
    printf("start\n");
}
void test_(){
        test(0x7777777777777771,0x7777777777777772,0x7777777777777773,0x7777777777777774,0x7777777777777775,0x7777777777777776,
        0x7777777777777771,0x7777777777777772,0x7777777777777773,0x7777777777777774,0x7777777777777775,0x7777777777777776,
        0x7777777777777771,0x7777777777777772,0x7777777777777773,0x7777777777777774,0x7777777777777775,0x7777777777777776,
        0x7777777777777771,0x7777777777777772,0x7777777777777773,0x7777777777777774,0x7777777777777775,0x7777777777777776);
}
int main(int argc,char** argv){
    test_();
    test1();
}

, скомпилировал и выполнил его:

x86_64-w64-mingw32-g++ -O0 test.cpp && ./a.exe

Теперь наступает удивительная часть, вывод:

start output should be: 3 2 1 0 3333333333333333 but output is: 3 2 1 7777777700000000 3333333333333333

В приведенном выше примере я использую printf для создания и визуализации проблемы.

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

Например: void blah(a,b,...)

По какой-то причине компилятор делает эту неожиданную вещь. Поиск в Google не привел меня к правильному направлению. К сожалению,

Это приводит меня к вопросу, действительно ли это проблема с компилятором (у linux такой проблемы не было), или это ошибка программирования (например, забывание привести число 0).

Взглянув на разобранный код, я обнаружил ту часть, которая создает проблему:

objdump -M intel -S ./a.exe|egrep -A 30 'test1.+:'
0000000000401570 <_Z5test1v>:
  401570:       55                      push   rbp
  401571:       48 89 e5                mov    rbp,rsp
  401574:       48 83 ec 50             sub    rsp,0x50
  401578:       48 b8 33 33 33 33 33    movabs rax,0x3333333333333333
  40157f:       33 33 33
  401582:       48 89 45 f8             mov    QWORD PTR [rbp-0x8],rax
  401586:       48 c7 45 f0 01 00 00    mov    QWORD PTR [rbp-0x10],0x1
  40158d:       00
  40158e:       48 c7 45 e8 02 00 00    mov    QWORD PTR [rbp-0x18],0x2
  401595:       00
  401596:       48 c7 45 e0 03 00 00    mov    QWORD PTR [rbp-0x20],0x3
  40159d:       00
  40159e:       48 8d 0d 5b 7a 00 00    lea    rcx,[rip+0x7a5b]        # 409000 <.rdata>
  4015a5:       e8 a6 66 00 00          call   407c50 <_Z6printfPKcz>
  4015aa:       4c 8b 45 f0             mov    r8,QWORD PTR [rbp-0x10]
  4015ae:       48 8b 4d e8             mov    rcx,QWORD PTR [rbp-0x18]
  4015b2:       48 8b 45 e0             mov    rax,QWORD PTR [rbp-0x20]
  4015b6:       48 8b 55 f8             mov    rdx,QWORD PTR [rbp-0x8]
  4015ba:       48 89 54 24 28          mov    QWORD PTR [rsp+0x28],rdx
  4015bf:       c7 44 24 20 00 00 00    mov    DWORD PTR [rsp+0x20],0x0
  4015c6:       00
  4015c7:       4d 89 c1                mov    r9,r8
  4015ca:       49 89 c8                mov    r8,rcx
  4015cd:       48 89 c2                mov    rdx,rax
  4015d0:       48 8d 0d 59 7a 00 00    lea    rcx,[rip+0x7a59]        # 409030 <.rdata+0x30>
  4015d7:       e8 74 66 00 00          call   407c50 <_Z6printfPKcz>
  4015dc:       90                      nop
  4015dd:       48 83 c4 50             add    rsp,0x50
  4015e1:       5d                      pop    rbp
  4015e2:       c3                      ret

и я абсолютно не понимаю, почему он использует этот меч по смещению 4015bf. Может быть, кто-то может пролить свет на мою проблему или сможет протестировать ее с более новой версией mingw.

(я уже пробовал использовать образ ubuntu для «бионического бобра», но, к сожалению, с тем же результатом ... ну, в любом случае, он имеет ту же версию x86_64-w64-mingw32-g ++)

Ответы [ 3 ]

0 голосов
/ 02 мая 2018

Когда я компилирую в clang, я получаю это предупреждение:

varby.cpp: 12: 63: предупреждение: формат указывает тип «unsigned long long», но аргумент имеет тип «int» [-Wformat]

Вероятно, это источник вашей проблемы, поскольку 0 неверный тип аргумента.

Исправьте это, сделав его длинным-длинным, как вы хотите:

printf("but output is:\n%llx %llx %llx %llx %llx\n",d,c,b,0LL,a);

Хорошее практическое правило заключается в том, что один из миллиона ошибок будет вызван компилятором, поэтому всегда предполагайте, что это ваша ошибка, пока не сможете доказать обратное. В этом случае включение большего количества предупреждений или попытка воспроизвести их в другом компиляторе позволило бы решить проблему.

0 голосов
/ 02 мая 2018

У вас есть несоответствие типа аргумента:

 printf("but output is:\n%llx %llx %llx %llx %llx\n",d,c,b,0,a);

Значение 0 имеет тип int, но спецификатор формата %llx ожидает переменную типа unsigned long long int. Использование неправильного спецификатора формата вызывает неопределенное поведение .

Поскольку printf - это переменная функция, она не может автоматически преобразовать это значение в правильный тип. Поэтому вам нужно либо использовать правильный спецификатор формата:

 printf("but output is:\n%llx %llx %llx %d %llx\n",d,c,b,0,a);

Или приведите рассмотренный аргумент

 printf("but output is:\n%llx %llx %llx %llu %llx\n",d,c,b,(unsigned long long)0,a);

Или (в случае константы) использовать правильный суффикс типа

 printf("but output is:\n%llx %llx %llx %llu %llx\n",d,c,b,0ULL,a);
0 голосов
/ 02 мая 2018

0 в вашем printf имеет неправильный тип, это int, а не long long. Вместо этого попробуйте использовать 0ll для литерала.

...