c/c++培训C语言核心知识总结(二)
更新时间:2016年10月21日16时28分 来源:乐鱼播客C++培训学院 浏览次数:
二、函数参数的进栈顺序和运算顺序(引伸出各个平台编译器的不同)
1. 大端对齐和小端对齐:
unsigned int num = 0x12345678;
大端对齐:数值的高位字节存储在内存的低位地址上,数值的低位字节存储在内存的高位地址上。
地址: 0xff1100 0xff1101 0xff1102 0xff1103
数值: 0x12 0x34 0x56 0x78
小端对齐:数字的高位字节存储在内存的高位地址上,数值的低位字节存储在内存的低位地址上。
地址: 0xff1100 0xff1101 0xff1102 0xff1103
数值: 0x78 0x56 0x34 0x12
大端:IBM、SUN的服务器CPU都是大端对齐,最早的苹果电脑PowerPC也是大端。
小端:x86\AMD64(美国)架构CPU(复杂指令集)都是小端对齐,ARM(英国)架构CPU(精简指令集)都是小端对齐。
x86 intel
AMD64 AMD
2. 函数的进栈顺序
#include <stdio.h>
void func(int a, int b, int c) // 三个形参(本质是局部变量),接收实参的值
{
printf("a = %d : %p", a, &a);
printf("b = %d : %p", b, &b);
printf("c = %d : %p", c, &c);
}
int main(void)
{
func(100, 200, 300); // 三个实参
return 0;
}
// Ubuntu GCC 下编译结果
a = 100 : 0xbf8decb0 +4
b = 200 : 0xbf8decb4 +4
c = 300 : 0xbf8decb8
// Windows Visual C++ 下编译结果
a = 100 : 0x0018F720 +4
b = 200 : 0x0018F724 +4
c = 300 : 0x0018F728
// LLVM Clang 下编译结果
a = 100 : 0x7fff547d59e8 -4
b = 200 : 0x7fff547d59e4 -4
c = 300 : 0x7fff547d59e0
C程序在执行的时候,先入栈的数据是在栈底的,栈底是高地址,后入栈的数据在栈顶,栈顶为低地址。
从上面的例子看得出来:
GCC和MSVC下,参数的进栈顺序是"从右往左"。
在LLVM Clang下,参数的进栈顺序是"从左往右"。
3. 函数参数的计算顺序
//1.
#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;
}
// Windows Visual C++ 下编译结果
110, 40, 60
// Ubuntu GCC 下编译结果
110, 40, 60
// LLVM Clang 下编译结果
60, 40, 60
//2.
#include <stdio.h>
int a()
{
printf("a\n");
return 1;
}
int b()
{
printf("b\n");
return 2;
}
int main(void)
{
printf("%d, %d\n", a(), b());
return 0;
}
//MSVC 下编译结果
b
a
1, 2
// Ubuntu GCC 下编译结果
b
a
1, 2
// LLVM Clang 下编译结果
a
b
1, 2
4. 函数的默认参数
#include <stdio.h>
void func(int a, int b, int c = 300) // 三个形参(本质是局部变量),接收实参的值
{
printf("a = %d : %p", a, &a);
printf("b = %d : %p", b, &b);
printf("c = %d : %p", c, &c);
}
int main(void)
{
func(100, 200); // 三个实参
return 0;
}
上面的写法,在LLVM Clang下是可以编译通过的,而且c的值是300,func(100, 200)的值也给了a 和 b。
但是在 MSVC 和 GCC下不允许这么做,也不允许在函数参数列表里赋值。
C编译器:
Microsoft Visual C++ / GNU GCC /LLVM Clang / ICC / Turbo C
当一个函数的参数列表里有多个参数的时候,C语言没有规定实参的进栈顺序和计算顺序,而是由编译器自行决定的。
我们在写代码的时候,尽量不要写出UB(行为未定义、奇葩)语句:
"Undefined Behavior"简单来说就是:
如果你的程序违反了C标准中某些规则,程序具体执行结果会发生什么,C语言没有定义。
也就是说得到的结果可能是某种奇怪的情况,都是有可能发生的。
比如说,整数溢出就是一个"Undefined Behavior"语句。
"Unspecified Behavior"简单来说就是:
C标准提供了好多种可选方案,但是没有告诉你一定要用哪一种,
比如说,函数参数的计算顺序就是这种情况。
本文版权归乐鱼播客C++培训学院所有,欢迎转载,转载请注明作者出处。谢谢!
作者:乐鱼播客C/C++培训学院
首发:http://www.itcast.cn/c/