教育行业A股IPO第一股(股票代码 003032)

全国咨询/投诉热线:400-618-4000

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/ 
0 分享到:
和我们在线交谈!