给出以下代码,用于参数的错误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之后,它是打印中的第二个参数是错误的,而我不希望也会发生这种情况。我无法解释这一点。这怎么可能?
答案 0 :(得分:4)
答案很好@Attie!另外,因为你引发了我的低级激情:)我会试着给出另一个观点。
如您所知,在 x86架构中,函数参数通过stack发送,在 x64架构中,函数参数通过寄存器发送(RDI,RSI,RDX,RCX,R8,R9,按此顺序)。
因此,与您的问题相关的重要事项是,当您编译32位时,printf
调用的参数将通过堆栈发送。这是两个printf
调用之前的堆栈的样子:
堆栈中的每个矩形块由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