64位环境0和NULL的区别
0 & NULL
? ? 在C語(yǔ)言中將值為0的指針作為NULL,NULL通常被定義為0或((void *)0);
有很多應(yīng)該使用NULL的地方寫(xiě)0代替的程序,通常這樣的寫(xiě)法也不會(huì)發(fā)生問(wèn)題,如:
? char *p = 0;
? if (p != 0) ...
此時(shí)p為char *,進(jìn)行運(yùn)算前,會(huì)將操作數(shù)類(lèi)型轉(zhuǎn)換為與之有互換性的某個(gè)類(lèi)型上,所以默認(rèn)int型的0轉(zhuǎn)換為char *型的NULL,這樣一來(lái),下面的書(shū)寫(xiě)方法也沒(méi)有問(wèn)題:
? if (p)
在此時(shí)if會(huì)用布爾值來(lái)判斷,用表達(dá)式的布爾值是否為0來(lái)判斷。如下所示:
? if (p != 0)
相反的條件
? if (!p)
也跟
?if(p == 0)
等價(jià)
象這樣在多數(shù)情況下,用0代替NULL的處理方式也可以。但是也存在不通用的地方。
?
0和NULL不同的情況
? 如前所述,NULL并不是int型的0值,而是指針型的0值(值為0的指針);也就是說(shuō)編譯器必須要明白是用指針理性來(lái)解釋,
在x86那樣的ILP32環(huán)境中由于int long指針都是32位,所以int型的0可以等同于指針的NULL。但在IA64那樣的LP64環(huán)境中int仍然是32位的,但long和指針都是64位的,int的0便不等同于NULL了。使用可變長(zhǎng)參數(shù)時(shí),這個(gè)問(wèn)題很明顯。示例代碼:
struct s *foo(const char *name, ...) {va_list va;struct s *sp;char *p;va_start(va, fmt);sp = (struct s *)malloc(*sp);if (!sp) {return 0;}memset(sp, 0,sizeof(*sp));set_name(sp, name);while ((p = va_arg(va, char *))) {set_item(sp, p);}return sp; }... sp = foo("foo", "bar", 0); ...這里的foo()是取得可變長(zhǎng)參數(shù)后,利用name這個(gè)名稱(chēng)和剩下的參數(shù)表初始化item結(jié)構(gòu)體的函數(shù)。這個(gè)代碼在LP64環(huán)境下不能正常運(yùn)行。
調(diào)用foo("foo", "bar", 0)之后,參數(shù)會(huì)像下面這樣 堆積在棧上:
? ? 為0的int
? ? 指向"bar"的const char *
? ? 指向"foo"的const char *
在foo()運(yùn)行時(shí),形參const char *name指向第一個(gè)參數(shù)"foo", 其他變參通過(guò)va_list來(lái)訪(fǎng)問(wèn),在foo()中通過(guò)如下形式取va_list內(nèi)容:
p = va_arg(va, char *);
也就是參數(shù)作為char *來(lái)取出,第一個(gè)是指向"bar"的指針,所以沒(méi)問(wèn)題。而第二個(gè)變參傳的是int型(32位)的0,但讀取時(shí)是按64位的char *來(lái)取的,多讀取了4字節(jié)為初始化的內(nèi)存數(shù)據(jù),如果這四個(gè)字節(jié)不是全0,而導(dǎo)致進(jìn)入下一次while循環(huán),會(huì)發(fā)生什么后果呢?
這就是將0和NULL混用而導(dǎo)致的bug,正確調(diào)用foo()的方法:
sp = foo("foo", "bar", NULL);
也有自己編程使用類(lèi)似函數(shù)的情況,例如:使用類(lèi)似機(jī)制的execl(3)時(shí),必須用NULL來(lái)終止參數(shù)列表(見(jiàn)man手冊(cè)提示):
int execl(const char *path, const char *arg, .../*, (char *)0*/);
字面量 0 ,是int型,在64位系統(tǒng)里是32位。
宏 NULL, 定義為((void*)0),在64位系統(tǒng)里是64位。
在foo函數(shù)定義之后使用該函數(shù),或在使用foo函數(shù)之前聲明一下foo函數(shù),編譯器就會(huì)自動(dòng)將int型的0轉(zhuǎn)為void*型的NULL。
如果沒(méi)有定義或聲明foo函數(shù)就直接使用foo函數(shù),編譯器會(huì)根據(jù)你傳入的參數(shù)產(chǎn)生一個(gè)隱含的聲明。
調(diào)用foo("foo", "bar", 0),編譯器會(huì)認(rèn)為你的函數(shù)聲明為int foo(char*, char*, int),并且根據(jù)此聲明壓棧,共20字節(jié)。
而函數(shù)實(shí)際定義為foo(char*, char *, void*),函數(shù)會(huì)把傳入的第三個(gè)參數(shù)當(dāng)做void*解析,16~19字節(jié)是整數(shù)0,20~23字節(jié)數(shù)據(jù)未定義,函數(shù)會(huì)取到一個(gè)錯(cuò)誤的指針(在big-endian系統(tǒng)里,指針低位部分為0,高位部分未定義,即0x********00000000;在little-endian系統(tǒng)里,指針低位部分未定義,高位部分位0,即0x00000000********)
C++中還可以用nullptr。
?
轉(zhuǎn)自:https://www.phpfans.net/ask/MTEwNjM3OQ.html
總結(jié)
以上是生活随笔為你收集整理的64位环境0和NULL的区别的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux iptables用法与NAT
- 下一篇: cmake指定gcc版本