这是一个GCC(mingw)/glibc bug-scanf加短裤吗?

这是一个GCC(mingw)/glibc bug-scanf加短裤吗?,gcc,mingw,scanf,short,Gcc,Mingw,Scanf,Short,请看下面的简单代码 int main() { short x = 0, y = 0; scanf("%d", &x); scanf("%d", &y); printf("%d %d\n", x, y); return 0; } 如果你在这个程序中输入4和5,你会期望在输出中得到4和5。在windows(mingw)上使用GCC4.6.2时,它生成0和5作为输出。所以我挖了一点。这是生成的汇编代码 movw $0, 30(%esp) movw

请看下面的简单代码

int main() 
{
   short x = 0, y = 0;
   scanf("%d", &x);
   scanf("%d", &y);
   printf("%d %d\n", x, y);
   return 0;
}
如果你在这个程序中输入4和5,你会期望在输出中得到4和5。在windows(mingw)上使用GCC4.6.2时,它生成0和5作为输出。所以我挖了一点。这是生成的汇编代码

movw    $0, 30(%esp)
movw    $0, 28(%esp)
leal    30(%esp), %eax
movl    %eax, 4(%esp)
movl    $LC0, (%esp)
call    _scanf
leal    28(%esp), %eax
movl    %eax, 4(%esp)
movl    $LC0, (%esp)
call    _scanf
虽然我没有做太多汇编代码,但上面的代码看起来并不正确。似乎建议将x放置在esp的30字节偏移量处,将y放置在esp的28字节偏移量处,然后将它们的地址传递给scanf。因此,当x和y的地址作为长整数(4字节地址)处理时,应发生以下情况: 第一个调用将把字节[30,34]设置为0x00000004,第二个调用将把字节[28,32]设置为0x00000005。但是,由于这是一个小的endian机器,我们将把[0x04 0x00 0x00]从30设置为0,然后把[0x05 0x00 0x00]从28设置为0。这将导致字节数30重置为0

我尝试反转扫描顺序,结果成功了(输出结果是4和5),所以现在,先填充较小的偏移量,然后填充后一个(较大的)偏移量

GCC可能会把事情搞砸,这似乎很荒谬。因此我尝试了MSVC,它生成的程序集有一个显著的区别。变量被放置在偏移量-4和-8处(即,它们被认为是4字节长,尽管注释中说是2字节)。下面是代码的一部分:

_TEXT   SEGMENT
_x$ = -8    ; size = 2
_y$ = -4    ; size = 2
_main   PROC
    push    ebp
    mov ebp, esp
    sub esp, 8
    xor eax, eax
    mov WORD PTR _x$[ebp], ax
    xor ecx, ecx
    mov WORD PTR _y$[ebp], cx
    lea  edx, DWORD PTR _x$[ebp]
    push    edx
    push    OFFSET $SG2470
    call    _scanf
    add esp, 8
    lea eax, DWORD PTR _y$[ebp]
    push    eax
    push    OFFSET $SG2471
    call    _scanf
    add esp, 8
我的问题分为两部分:


  • 我没有一个个人的Linux机箱可供我使用。这是一个GCC问题,还是仅仅是一个mingw问题
但更重要的是,

  • 这到底是一个bug吗?编译器如何判断应该将“short”放在2字节偏移量还是4字节偏移量
要在
short
上使用
scanf()
,必须在格式字符串中指定
%hd

您正在引发溢出,因为您对scanf()撒谎。。打开警告(
-Wall
至少)。您应该会收到GCC关于不匹配的投诉。(当你学习C语言时,用
-Wall
捕捉你犯的愚蠢错误。当你像我一样用C语言编程超过四分之一个世纪时,你会添加更多的标志,以确保你仍然没有犯愚蠢的错误。并且你会始终确保代码用
-Wall
编译干净)

Mac OS X 10.7.5上的GCC 4.7.1规定:

ss.c:6:4: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘short int *’ [-Wformat]
ss.c:7:4: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘short int *’ [-Wformat]
要在
short
上使用
scanf()
,必须在格式字符串中指定
%hd

您正在引发溢出,因为您对scanf()撒谎。。打开警告(
-Wall
至少)。您应该会收到GCC关于不匹配的投诉。(当你学习C语言时,用
-Wall
捕捉你犯的愚蠢错误。当你像我一样用C语言编程超过四分之一个世纪时,你会添加更多的标志,以确保你仍然没有犯愚蠢的错误。并且你会始终确保代码用
-Wall
编译干净)

Mac OS X 10.7.5上的GCC 4.7.1规定:

ss.c:6:4: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘short int *’ [-Wformat]
ss.c:7:4: warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘short int *’ [-Wformat]

Jonathan Leffler的回答解释了
scanf
的问题。人们可能想知道
printf
如何工作得很好


printf
之所以有效,是因为它是一个可变函数,即接受可变数量参数的函数。在C标准中(因此在英特尔平台上实现的ABI中),所有整数类型的值都小于int(chars,shorts)作为堆栈上的整数传递给变量函数,所有
浮点值作为
双精度
传递。但是,此技巧不适用于
scanf
,它接收对象地址而不是实际值。即使是被视为“良性”的错误在
printf
的上下文中,会使
scanf
超出它应该分配给的对象。

乔纳森·莱夫勒的回答解释了
scanf
的问题。人们可能会想知道
printf
如何工作得很好


printf
之所以有效,是因为它是一个可变函数,即接受可变数量参数的函数。在C标准中(因此在英特尔平台上实现的ABI中),所有整数类型的值都小于int(chars,shorts)作为堆栈上的整数传递给变量函数,所有
浮点值作为
双精度
传递。但是,此技巧不适用于
scanf
,它接收对象地址而不是实际值。即使是被视为“良性”的错误在
printf
的上下文中,使
scanf
溢出了它应该分配给的对象。

Ha!所有关于汇编代码的挖掘都是洗眼术!在谷歌上快速搜索格式标识符,得到了(%hi)用于短整数。问题在于代码中的格式说明符,而不是代码本身

因此,当scanf被传递%d时,它向传递的地址写入了一个4字节的数字,然后问题中说明的所有问题开始出现


现在,只剩下一个问题了。为什么GCC和VC++在程序中变量的位置不同?这仅仅是一个迂腐的问题(GCC超过VC++),还是这有实际的后果?

Ha!所有关于汇编代码的挖掘都是洗眼水!在谷歌上快速搜索格式标识符得到了(%hi)用于短整数。问题在于代码中的格式说明符,而不是代码本身

因此,当scanf被传递%d时,它向传递的地址写入了一个4字节的数字,然后问题中说明的所有问题开始出现

现在,只剩下一个问题了。为什么GCC和VC++在程序中变量的位置上有所不同?这只是一个问题吗