0%

C/C++变长参数及64位机器未指定整数类型的情况

记2020.11.7

在看《程序员的自我修养 ——链接、装载与库》第11章的时候,看到了C语言变长参数的实现,于是按照它给的例程写了一个测试程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<stdio.h>

int sum(int num, ...)
{
int sum = 0;
int* p = &num+1;

while (num--)
{
printf("%d\n", (*p));
sum+=(*p);
p++;
}
return sum;
}

int main()
{
printf("%d", sum(3, 16, 38, 53));

return 0;
}

运行结果如下:

1
2
3
4
0
16
0
16

显然,不符合预期(预期是三个参数的和107)。

那么,这是为什么呢?跟据变长指令实现的原理(_cdecl调用惯例和栈的利用),猜测是由于我是64位的CPU(书中作者的CPU是32位),所以每次PUSH送入栈的数据位宽大小是64位,而int类型应该是32位的,指针一次只增加和读取4个字节,于是我将main函数中调用的第一个num的参数值改为6。再次运行,结果如下:

1
2
3
4
5
6
7
0
16
0
38
0
53
107

结果正确。猜测正确,同时也可以看出来我的CPU是大端模式。

为了使功能正确(num的值等于参数的数量),于是考虑把int类型改为long,谁知还是一样的结果。

经过@AgNo3的提醒,C语言中int、long这些基本整型变量的位宽没有确定的标准,也就是不同编译器给这些数据类型的位宽不一定一样。如果想要指定确定的位宽,只能使用头文件stdint.h中的int8_t,int_16_t、int32_t、uint8_t……这些类型(见https://www.gnu.org/software/libc/manual/html_node/Integers.html)。

于是将所有的int类型改为int64_t,结果如下:

1
2
3
4
16
38
53
107

结果正确。

同时还可以猜测,C程序在使用没有指定类型的整型常量(字面值)时,默认是使用与CPU字长相同的宽度。