当参数为unsigned long long时,-m32给出了无法解释的问题

时间:2017-04-10 14:31:59

标签: c printf format-specifiers

给出以下代码,用于参数的错误printf语句' a':

#include <stdio.h>
void call(unsigned long long a, int b)
{
    printf("%lu,%d\n",a,b);
    printf("%llu,%d\n",a,b);
}
void main()
{
    call(0,1);
}

正常编译时,你得到:

$ gcc m32.c
m32.c: In function ‘call’:
m32.c:4:12: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘long long unsigned int’ [-Wformat=]
     printf("%lu,%d\n",a,b);
            ^
$ ./a.out
0,1
0,1

但是当你用-m32编译它时,你得到以下输出:

$ gcc -m32 m32.c
m32.c: In function ‘call’:
m32.c:4:12: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘long long unsigned int’ [-Wformat=]
     printf("%lu,%d\n",a,b);
            ^
$ ./a.out
0,0
0,1

显然,第一个printf是错误的,但正如你所看到的,在printf之后,它是打印中的第二个参数是错误的,而我不希望也会发生这种情况。我无法解释这一点。这怎么可能?

3 个答案:

答案 0 :(得分:4)

答案很好@Attie!另外,因为你引发了我的低级激情:)我会试着给出另一个观点。

如您所知,在 x86架构中,函数参数通过stack发送,在 x64架构中,函数参数通过寄存器发送(RDI,RSI,RDX,RCX,R8,R9,按此顺序)。

因此,与您的问题相关的重要事项是,当您编译32位时,printf调用的参数将通过堆栈发送。这是两个printf调用之前的堆栈的样子:

stack before printf call

堆栈中的每个矩形块由32位数字表示(因为您在x86架构中)。您想发送一个64位数作为printf的第一个参数!为此,编译器将无符号长整数分成两个32位部分,并在堆栈上单独推送它们。这就是为什么你在堆栈上得到两个零以及整数中的一个值。

现在让我们分析printf的第一个电话。

0,0

由于它具有“%lu,%d \ n”格式说明符,因此必须从堆栈中取一个unsigned long和一个int。 %lu 在x86架构中是32位,因此printf只占用堆栈中的一个块。在此之后,为整数再取一个块(因为我们只用“%”消耗了两个零中的一个,我们将得到%d的另一个零。)

printf的第二次调用输出正常值。

0,1

此调用使用“%llu,%d \ n”格式说明符完成。 %llu在x86架构中是64位,因此printf从堆栈中获取两个块,因此打印为零。在此之后,它将从堆栈中再获取一个整数块(这是具有一个值的块)。

您必须非常小心发送到printf函数的字符串格式说明符! format string attack是一种众所周知的攻击类型,它基于您在问题中展示的问题。

答案 1 :(得分:2)

你最好停在这里

 printf("%lu,%d\n",a,b);

提供与转换说明符的预期类型不匹配的参数会导致undefined behavior。在那之后,无论发生什么,没有人负责。

引用C11,章节§7.21.6.1

  

[...]如果有任何论据   不是相应转换规范的正确类型,行为是   未定义。

答案 2 :(得分:2)

理解你在说什么很重要。

app.controller("mainController", function($rootScope, $scope, $state, $timeout, $filter) {

  $scope.flags = {};
  $scope.go = function(route) {
    $state.go(route);
  };

 //Additionally added below method
 $scope.onSelectTab = function(t) {  
     if (t.heading == 'tab2') {  
         $scope.flags.disableTab = true;  
      }  
 };   
//End

  $scope.active = function(route) {
    return $state.is(route);
  };

  $scope.tabs = [{
    heading: "tab1",
    route: "main.tab1",
    active: false
  }, {
    heading: "tab2",
    route: "main.tab2",
    active: false
  }, {
    heading: "tab3",
    route: "main.tab3",
    active: false
  }, {
    heading: "tab4",
    route: "main.tab4",
    active: false
  }, ];

  $scope.$on("$stateChangeSuccess", function() {
    $scope.tabs.forEach(function(tab) {
      tab.active = $scope.active(tab.route);
    });
  });
});

printf("%lu,%d\n",a,b); 解释为:

  • 有一个printf()
  • 有一个long unsigned

在这种情况下,你撒谎 - 这不是真的。 在你的情况下,这个特别糟糕的是你的系统在34位和64位之间改变int的大小(就像我的一样)。

unsigned long
#include <stdio.h>

int main(void) {
        printf("sizeof(unsigned long): %zd\n", sizeof(unsigned long));
        printf("sizeof(unsigned long long): %zd\n", sizeof(unsigned long long));
        return 0;
}

所以,是的,它适用于64位(错误地),但是32位$ gcc ll.c -o ll -m32 && ./ll sizeof(unsigned long): 4 sizeof(unsigned long long): 8 $ gcc ll.c -o ll && ./ll sizeof(unsigned long): 8 sizeof(unsigned long long): 8 正在从堆栈中取出错误大小的值。

确保格式字符串与参数匹配非常重要。

我们可以测试一下(忽略那些有用的警告......):

printf()
#include <stdio.h>

int main(void) {
    unsigned long long x;
    int y;

    x = 0x8A7A6A5A4A3A2A1ALLU;
    y = 0x4B3B2B1B;

    printf("%lx - %x\n", x, y);
    printf("%llx - %x\n", x, y);

    return 0;
}

你可以看到,在32位运行中,$ gcc ll.c -o ll -m32 && ./ll ll.c: In function ‘main’: ll.c:10:2: warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘long long unsigned int’ [-Wformat=] printf("%lx - %x\n", x, y); ^ 4a3a2a1a - 8a7a6a5a 8a7a6a5a4a3a2a1a - 4b3b2b1b $ gcc ll.c -o ll && ./ll ll.c: In function ‘main’: ll.c:10:2: warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘long long unsigned int’ [-Wformat=] printf("%lx - %x\n", x, y); ^ 8a7a6a5a4a3a2a1a - 4b3b2b1b 8a7a6a5a4a3a2a1a - 4b3b2b1b 的值在休息时被分割! x采用了'第一个'32位,然后是'下一个'32位,实际上我们为它提供了一个64位值 - 字节顺序使得它更加混乱。 / p>

如果您想对您的可变尺寸做出规定,请在此处查看我的答案:https://stackoverflow.com/a/43186983/1347519