教育行業(yè)A股IPO第一股(股票代碼 003032)

全國咨詢/投訴熱線:400-618-4000

c/c++培訓(xùn)C語言核心知識總結(jié)(二)

更新時間:2016年10月21日16時28分 來源:傳智播客C++培訓(xùn)學(xué)院 瀏覽次數(shù):

二、函數(shù)參數(shù)的進(jìn)棧順序和運算順序(引伸出各個平臺編譯器的不同)
 
1. 大端對齊和小端對齊:

unsigned int num = 0x12345678;
 
大端對齊:數(shù)值的高位字節(jié)存儲在內(nèi)存的低位地址上,數(shù)值的低位字節(jié)存儲在內(nèi)存的高位地址上。
 
地址:  0xff1100 0xff1101 0xff1102 0xff1103
 
數(shù)值: 0x12 0x34 0x56 0x78
 
小端對齊:數(shù)字的高位字節(jié)存儲在內(nèi)存的高位地址上,數(shù)值的低位字節(jié)存儲在內(nèi)存的低位地址上。
 
地址:  0xff1100 0xff1101 0xff1102 0xff1103
 
數(shù)值: 0x78 0x56 0x34 0x12
 
 
大端:IBM、SUN的服務(wù)器CPU都是大端對齊,最早的蘋果電腦PowerPC也是大端。
小端:x86\AMD64(美國)架構(gòu)CPU(復(fù)雜指令集)都是小端對齊,ARM(英國)架構(gòu)CPU(精簡指令集)都是小端對齊。
x86 intel
AMD64 AMD  
 
2. 函數(shù)的進(jìn)棧順序
 
#include <stdio.h>
void func(int a, int b, int c) // 三個形參(本質(zhì)是局部變量),接收實參的值
{
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 下編譯結(jié)果
a = 100 : 0xbf8decb0  +4
b = 200 : 0xbf8decb4  +4
c = 300 : 0xbf8decb8
 
// Windows Visual C++ 下編譯結(jié)果
a = 100 : 0x0018F720  +4
b = 200 : 0x0018F724  +4
c = 300 : 0x0018F728
 
// LLVM Clang 下編譯結(jié)果
a = 100 : 0x7fff547d59e8 -4
b = 200 : 0x7fff547d59e4 -4
c = 300 : 0x7fff547d59e0
 
C程序在執(zhí)行的時候,先入棧的數(shù)據(jù)是在棧底的,棧底是高地址,后入棧的數(shù)據(jù)在棧頂,棧頂為低地址。
 
從上面的例子看得出來:
GCC和MSVC下,參數(shù)的進(jìn)棧順序是"從右往左"。
在LLVM Clang下,參數(shù)的進(jìn)棧順序是"從左往右"。
 
 
 
3. 函數(shù)參數(shù)的計算順序
 
//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++ 下編譯結(jié)果
110, 40, 60
 
// Ubuntu GCC 下編譯結(jié)果
110, 40, 60
 
// LLVM Clang 下編譯結(jié)果
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 下編譯結(jié)果
b
a
1, 2
 
// Ubuntu GCC 下編譯結(jié)果
b
a
1, 2
 
// LLVM Clang 下編譯結(jié)果
a
b
1, 2
 
 
4. 函數(shù)的默認(rèn)參數(shù)
 
#include <stdio.h>
void func(int a, int b, int c = 300) // 三個形參(本質(zhì)是局部變量),接收實參的值
{
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下不允許這么做,也不允許在函數(shù)參數(shù)列表里賦值。
 
C編譯器:
Microsoft Visual C++ / GNU GCC /LLVM Clang / ICC / Turbo C
 
當(dāng)一個函數(shù)的參數(shù)列表里有多個參數(shù)的時候,C語言沒有規(guī)定實參的進(jìn)棧順序和計算順序,而是由編譯器自行決定的。
 
我們在寫代碼的時候,盡量不要寫出UB(行為未定義、奇葩)語句:
 
"Undefined Behavior"簡單來說就是:
如果你的程序違反了C標(biāo)準(zhǔn)中某些規(guī)則,程序具體執(zhí)行結(jié)果會發(fā)生什么,C語言沒有定義。
也就是說得到的結(jié)果可能是某種奇怪的情況,都是有可能發(fā)生的。
比如說,整數(shù)溢出就是一個"Undefined Behavior"語句。
 
"Unspecified Behavior"簡單來說就是:
C標(biāo)準(zhǔn)提供了好多種可選方案,但是沒有告訴你一定要用哪一種,
比如說,函數(shù)參數(shù)的計算順序就是這種情況。
 
本文版權(quán)歸傳智播客C++培訓(xùn)學(xué)院所有,歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明作者出處。謝謝!
作者:傳智播客C/C++培訓(xùn)學(xué)院
首發(fā):http://oisangadgets.com/c/ 
0 分享到:
和我們在線交談!