免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
C 語言之四 編譯器(轉(zhuǎn))
C 語言之四 編譯器(轉(zhuǎn))
 
2008-01-24 11:09


基本解釋
本節(jié)主要探討C編譯器下面兩方面的特點(diǎn)所引發(fā)的一系列常見的編程問題。
對C文件進(jìn)行分別編譯:
C程序通常由幾個小程序(.c文件)組成,編譯器將這幾個小程序分別編譯,然后通過鏈接程序?qū)⑺鼈兘M合在一起形成一個目標(biāo)代碼。由于編譯器每次只能編譯一個文件,因此它不能立即檢查需要幾個源文件配合才能發(fā)現(xiàn)的錯誤。
對函數(shù)的參數(shù)和返回值建立臨時變量
C編譯器會對函數(shù)的參數(shù)建立臨時參數(shù),也可能會對函數(shù)的返回值隱含傳遞一個指針。因?yàn)檫@些臨時變量的隱含性存在,使得在某些情況下,特別是有指針存在的時候,會引發(fā)一系列的問題。
C文件中所包含的頭文件會和C語言一同編譯

C語言中被包含的頭文件是和.c文件一起編譯的,頭文件中的問題會反映到.c文件的編譯中。
  問題:C文件的分別編譯

  我有一個數(shù)組a定義在f1.c中,但是我想在f2.c中計(jì)算它的元素個數(shù),用sizeof可以達(dá)到這個目的嗎?

   答案與分析:

  答案是否定的,你沒有辦法達(dá)到目的,本質(zhì)原因是sizeof操作符只是在“編譯時(compile time)”起作用,而C語言的編譯單位是每次單個.c文件進(jìn)行編譯(其它語言也都如此)。因此,sizeof可以確定同一個源文件中某個數(shù)組的大小,但是對于定義在另一個源文件中的數(shù)組它無能為力了,因?yàn)槟且呀?jīng)是“運(yùn)行時(run time)”才能確定的事情了。

  一件事情要想做,總會有辦法的,下面提供有三種可選的辦法來解決這個問題:

  1)、定義一個全局變量,讓它記住數(shù)組的大小,在另外一個.c文件中我們通過訪問這個全局變量來得到數(shù)組的大小信息(好像有點(diǎn)小題大做得不償失^_^)。

  2)、在某個.h文件中用宏定義數(shù)組的大小,例如#define ARRAY_SIZE 50,然后在兩個源文件中都包含這個.h文件,通過直接訪問ARRAY_SIZE來得到定義在不同.c文件中的數(shù)組的大小。

  3)、設(shè)置數(shù)組的最后一個元素為特殊值,例如0,-1,NULL等,然后我們通過遍歷數(shù)組來尋找這個特殊的結(jié)尾元素,從而判斷數(shù)組的長度(這個辦法效率低,也是笨笨的)。

   問題:函數(shù)返回值隱含傳遞指針

  下面的代碼可以正常工作,但是在程序結(jié)束時會有一個致命錯誤產(chǎn)生。究竟是什么原因呢?

struct list
{
 char *item;
 struct list *next;
}

main (argc, argv)
{
 ...
}

  答案與分析:

  原因很簡單,稍微注意一點(diǎn)不難發(fā)現(xiàn),在定義結(jié)構(gòu)list的右花括弧后面加一個分號就可以解決這個問題:

struct list
{
 char *item;
 struct list *next;
};//缺了這個分號可不行!

  好了,問題是解決了,但,你知道這個錯誤究竟導(dǎo)致了什么致命問題嗎?問題不是表面上那么簡單的,OK,讓我們來看看事情背后的真相。

  首先看一看下面這段代碼:

VOID Func ( struct my_struct stX)
{
 .......
}
struct my_struct stY = {...};
Func (stY);

  當(dāng)調(diào)用函數(shù)Func的時候,是把結(jié)構(gòu)變量stY的值拷貝一份到調(diào)用棧中,從而作為參數(shù)傳遞給函數(shù)FUNC的,這個叫做C語言的參數(shù)值傳遞。我相信這個你一定很清楚,那么,你應(yīng)該知道:如果函數(shù)的返回值是結(jié)構(gòu)變量的話,函數(shù)應(yīng)該如何將值返回給調(diào)用者呢?且看下面這段代碼:

struct my_structFunc (VOID)
{
 .......
}
struct my_struct stY = Func();

  此時函數(shù)Func的返回值是一個結(jié)構(gòu)類型的值,這個返回值被放在內(nèi)存中一個陰暗恐怖的地方,同時安排了一個指針指向這個地方(暫時稱為“神秘指針”),而這個指針會由C語言的編譯器作為一個隱藏參數(shù)傳遞給函數(shù)Func。當(dāng)函數(shù)Func返回時,編譯器生成的代碼將這個由隱藏指針指向的內(nèi)存區(qū)的值拷貝到返回結(jié)構(gòu)stY中,從而完成將結(jié)構(gòu)變量值返回給調(diào)用者。

  你明白了上述所講的東東,那么今天問題的真正原因也就呼之欲出了:

  因?yàn)閟truct list {...}的定義后面沒有加分號,導(dǎo)致主函數(shù)main (argc, argv)被編譯器理解為是一個返回值為結(jié)構(gòu)變量的函數(shù),從而期望得到除了argc和argv以外的第三個參數(shù),也就是我們上面提到的那個隱含傳入的“神秘指針”??墒?,大家知道,這里函數(shù)是main函數(shù),main函數(shù)的參數(shù)是由程序中的啟動代碼(startup code)提供的。而啟動代碼當(dāng)然認(rèn)為main()天生就應(yīng)該只得到兩個參數(shù),要“神秘指針”,當(dāng)然沒有,如此一來, main()在返回時自作主張地去調(diào)用棧中訪問它的那個并不存在的第三個參數(shù)(即神秘指針),這樣導(dǎo)致非法訪問,產(chǎn)生致命問題。這才是這個問題的真正根源。

  建議:

   1)、盡量將結(jié)構(gòu)變量的指針而不是結(jié)構(gòu)本身作為函數(shù)參數(shù),否則函數(shù)調(diào)用時內(nèi)存拷貝的開銷可不小,尤其是對那些調(diào)用頻繁、結(jié)構(gòu)體大的情況。

   2)、結(jié)構(gòu)定義的后面一定要加分號,經(jīng)過上面我的大段講述,我相信你不會犯相同的錯誤

 

問題:編譯器會給函數(shù)的參數(shù)隱含制造臨時副本

  請問運(yùn)行下面的Test函數(shù)會有什么樣的結(jié)果?

void GetMemory2(char **p, int num)
{
 *p = (char *)malloc(num);
}

void Test(void)
{
 char *str = NULL;
 GetMemory(&str, 100);
 strcpy(str, hello);
 printf(str);
}

  答案與分析:

  這是林銳的《C/C++高質(zhì)量編程指南》上面的例子,拿來用一下。

  這樣調(diào)用會產(chǎn)生如下兩個后果:

  1)、能夠輸出hello

  2)、內(nèi)存泄漏

   另一個相關(guān)問題:

  請問運(yùn)行Test函數(shù)會有什么樣的結(jié)果?

void GetMemory(char *p)
{
 p = (char *)malloc(100);
}

void Test(void)
{
 char *str = NULL;
 GetMemory(str);
 strcpy(str, hello world);
 printf(str);
}

  答案與分析:

  后果嚴(yán)重,運(yùn)行的結(jié)果是程序崩潰,通過運(yùn)行調(diào)試我們可以看到,經(jīng)過GetMemory后,Test函數(shù)中的 str仍舊是NULL??上攵?,一調(diào)用

strcpy(str, hello world);

  程序必然崩潰了事。

  原因分析:

   C編譯器總是會為函數(shù)的每個參數(shù)制作臨時副本,指針參數(shù)p的副本是 _p,編譯器使 _p = p。如果函數(shù)體內(nèi)的程序修改了_p的內(nèi)容,就導(dǎo)致參數(shù)p的內(nèi)容作相應(yīng)的修改。這就是指針可以用作輸出參數(shù)的原因。在本例中,_p申請了新的內(nèi)存,只是把_p所指的內(nèi)存地址改變了,但是p絲毫未變。所以函數(shù)GetMemory并不能輸出任何東西,如果想要輸出動態(tài)內(nèi)存,請使用指向指針的指針,或者,使用指向引用的指針。

  問題:頭文件和包含它的.c文件一同編譯問

  下面的代碼非常短小,看起來毫無問題,但編譯器會報(bào)告一個錯誤,請問問題可能出現(xiàn)在什么地方?

#include someheader.h
int myint = 0;

  答案與分析:

  不用盯著int myint = 0;看,這一句賦值應(yīng)該是C語言中最簡單的語句,問題肯定不會出在它身上,那么問題只可能出現(xiàn)在someheader.h中,最常見的就是該頭文件的最后一行的聲明(函數(shù)也好,變量也好)沒有用分號;結(jié)尾,那么編譯器會將它和myint變量結(jié)合起來考慮,自然就會出錯了。

  這個問題主要是提醒你,在定位問題時思路要拓寬一點(diǎn),可能要考慮一下所包含的頭文件是否有問題。

  結(jié)論:被包含的頭文件是和.c文件一起編譯的,頭文件中的問題會反映到.c文件編譯中去的,切記。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
C++比C牛逼的七個點(diǎn)
c++中__declspec用法總結(jié)
匠人的百寶箱--C語言重要概念!
原創(chuàng) :徹底了解指針數(shù)組,數(shù)組指針以及函數(shù)指針
為什么指針被譽(yù)為 C 語言靈魂?
【C 編程語言】之程序的內(nèi)存模型
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服