函数参数的进栈顺序和参数计算顺序
大端对齐和小端对齐
unsigned int a = 0x12345678;
* 大端:就是把数值高位数值放在内存的低字节,把数值的低位数放在内存的高位字节(低存高,高存低)
案例:
地址 | 0x00c1 | 0x00c2 | 0x00c3 | 0x00c4 |
---|---|---|---|---|
数值 | 0x12 | 0x34 | 0x56 | 0x78 |
* 小端:就是把熟知的高位数值放在内存的高位字节,吧数值的地位数值放在内存的地位字节(高存高,低存低)
案例:
地址 | 0x00c1 | 0x00c2 | 0x00c3 | 0x00c4 |
---|---|---|---|---|
数值 | 0x78 | 0x56 | 0x34 | 0x12 |
大小端对齐应用:
* 大端:IBM、Sun的服务器是大端,初代的苹果电脑PowerMac也是大端;
* 小端:x86架构的CPU是小端,ARM(英国)架构大多也是小端;
函数参数的进栈顺序
#include <stdio.h>
void func(int a, int b, int c)
{
printf("a = %d : [%p]\n", a, &a);
printf("b = %d : [%p]\n", b, &b);
printf("c = %d : [%p]\n", c, &c);
}
int main(void)
{
func(100,200,300);
return 0;
}
Win32下的编译结果:
a = 100 : [0x5fcad0] // +8
b = 200 : [0x5fcad8] // +8
c = 300 : [0x5fcae0]
Ubuntu Linux下编译结果:
a = 100 : [0x5fcad0] // +4
b = 200 : [0x5fcad8] // +4
c = 300 : [0x5fcae0]
C程序在执行时:” 先入栈的数据在栈底,为高地址;后入栈的数据在栈顶,为地地址 ”
* 可以总结为 ” 先进后出,后进先出 ”
Mac OS X下的编译结果:
a = 100 : [0x5fcae0] // -4
b = 200 : [0x5fcad8] // -4
c = 300 : [0x5fcad0]
Mac OS X 使用的编译期是LLVM Clang,这个编译器会优化一些代码,导致函数参数的进栈顺序是” 从左往右 “
函数参数计算顺序
#include <stdio.h>
int main(void)
{
int a = 10, b = 20, c = 30;
printf("%d, %d, %d\n",a + b + c, (b = b * 2),(c = c * 2));
return 0;
}
Win32 MSVC 下编译结果是
110, 40, 60
Ubuntu Linux GUN GCC 下编译结果是
110, 40, 60
Mac OS X LLVM Clang下编译结果
60, 40, 60
由此可知:
* 在MSVC 和GCC编译器下,参数的计算顺序使” 从右往左 “,和其参数进栈顺序相同
* 在LLVM Clang编译器下,参数的计算顺序使” 从左往右 “,和其参数进栈顺序相同
* 所以,当一个函数参数有多个的时候,C/C++语言设计者没有规定函数参数的进栈顺序和计算顺序是哪一种,这些是由编译器厂商自己规定的
函数的默认参数
#include <stdio.h>
void func(int a, int b, int c = 300)
{
printf("a = %d : [%p]\n", a, &a);
printf("b = %d : [%p]\n", b, &b);
printf("c = %d : [%p]\n", c, &c);
}
int main(void)
{
func(100, 200);
return 0;
}
上面的写法,在Ubuntu Linux和MSVC下是不允许,会报语法错误,实参数量要匹配型参数量,
也不允许函数参数在参数列表内赋值。
但是在LLVM Clang下是允许的,这是由于编译器支持这么干。
C编译器有很多,除了GCC、MSVC、Clang外,
还有一种编译叫ICC,这个编译器是Intel公司做的,会优化代码在Intel CPU上的效率。
还有Turbo C,是美国borland公司的产品,现在估计已经被淘汰了。TC
重点:
由于C语言编译器厂商规范不统一,所以我们在写代码的时候,不要写类似的”UB语句”。
“Undefined Behavior” 代码行为未定义:
如果你的程序违反了C标准的某些规则,那么具体会发生什么,C语言没有定义,
也就是说会得到一些奇怪的结果,都是有可能的。比如说整数溢出就是这种情况。
“Unspeakable Behavior”方案未定义:
C标准可能给你的代码提供了好几种可选方案,但是具体用哪一种,C标准没有定义。
比如说上面的例子,函数参数的计算顺序和进栈顺序不一致,而且任何计算方式都行。
写程序的态度要严谨,我们的代码是给人看的,所以为了将来的代码维护和升级,尽量不要用表达过于笼统的语句。
反面教材:
#include <stdio.h>
int main(void)
{
int i = 0;
int a = i + ++i;
printf("%d\n", a);
return 0;
}
上述代码最后的值会因为编译器不同而不同。