《C和指针》读书笔记第三章数据
寫在前面,由于學過C語言,導致想要跳躍式地翻閱《C和指針》,后來發現這實為錯誤,對于這本經典著作,要做的是從頭到尾保持體系的完整性。
《C和指針》配套代碼請移步網站:Pointers on C
作者的個人網站:Kenneth A. Reek
文章目錄
- 3.1 基本數據類型
- 3.1.1 整型家族
- 3.1.2 浮點類型
- 3.1.3指針
- 3.2 基本聲明
- 3.2.1 初始化
- 3.2.2 聲明簡單數組
- 3.2.3 聲明指針
- 3.2.4 隱式聲明
- 3.3 typedef
- 3.4 常量
- 3.5 作用域
- 3.5.1 代碼塊作用域
- 3.5.2 文件作用域
- 3.5.3 原型作用域
- 3.5.4 函數作用域
- 3.6 鏈接屬性
- 3.7 存儲類型
- 3.8 static關鍵字
- 3.9 作用域、存儲類型實例
- 3.10 總結
3.1 基本數據類型
在C語言中,僅有4中基本數據類型----整型、浮點型、指針和聚合類型(如數組和結構等)。所有其他的類型都是從這4種基本類型的某種組合派生而來。
3.1.1 整型家族
整型家族包括字符、短整型、整型和長整型,它們都分為有符號(signed)和無符號(unsigned) 兩種版本。
聽上去”長整型“所能表示的值應該比”短整型“所能表示的值要大,但這個假設并不一定正確。規定整型值相會之間大小的規則很簡單:
長整型至少應該和整型一樣長,而整型至少應該和短整型一樣長。
注意: 標準并沒有規定長整型必須比短整型長,只是規定它不得比短整型短。ANSI標準加入一個規范,說明了各種整型值的最小允許范圍。如下表所示
| char | 0~127 |
| signed char | -127~127 |
| unsigned char | 0~255 |
| short int | -32767~32767 |
| unsigned short int | 0~65535 |
| int | -32767~32767 |
| unsigned int | 0~65535 |
| long int | -2147483647~2147483647 |
| unsigned long int | 0~4294967295 |
3.1.2 浮點類型
3.1.3指針
指針是C語言為什么如此流行的一個重要原因。指針可以有效地實現諸如tree和list這類高級數據結構。其他有些語言,比如Pascal和Modula-2,也實現了指針,但是它們不允許在指針上執行算數或比較操作,也不允許以任何形式創建指向已經存在的數據對象的指針。正是由于不存在這些方面的限制,用C語言可以比使用其他語言編寫出更為緊湊和有效的程序。同時,C對指針使用的不加限制正是許多令人欲哭無淚和咬牙切齒的錯誤的根源。
變量的值存儲于計算機的內存中,每個變量都占據一個特定的位置。每個內存位置都由地址唯一確定并引用,就像一條街道上的房子由它們的門牌號碼標識一樣。指針只是地址的另一個名字罷了。指針變量就是一個其值為另一個(一些)內存地址的變量。C語言擁有一些操作符,你可以獲得一個變量的地址,也可以通過一個指針變量獲得它所指向的值或數據結構。
一,指針常量(pointer constant)
指針常量與非指針常量在本質上是不同的,因為編譯器負責把變量賦值給計算機內存中的位置,程序員事先無法知道某個特定的變量將存儲在內存中的哪個位置。因此,你通過操作符獲得一個變量的地址,而不是直接把它的地址寫成字面值常量的形式。例如,我們想知道變量xyz的地址,我們無法書寫一個類似oxff2044ec這樣的字面值,因為我們不知道這是不是編譯器實際存放這個變量的內存位置。事實上,當一個函數每次被調用時,它的自動變量(局部變量)可能每次分配的內存位置都不相同。因此,把指針常量表達為數值字面值的形式幾乎沒有用處。
二,字符串常量(string literal)
許多人對C語言不存在字符串類型感到奇怪,不過C語言提供了字符串常量。事實上,C語言存在字符串這個概念:它就是一串以NUL字節結尾的零個或多個字符。字符串通常存儲在字符數組中,這也是C語言沒有顯示的字符串類型的原因。由于NUL字節是用于終結字符串的,所以在字符串內部不能有NUL字節。不過,在一般情況下,這個限制并不會造成問題。之所以選擇NUL字節作為字符串的終止,是因為它不是一個可打印的字符。
字符串常量的書寫方式是一對雙引號包圍一串字符,如下所示
"Hello" "\aWarning!\a" "Line1 \nLine2" ""最后一個例子說明字符串常量(不像字符常量)可以是空的。盡管如此,即使是空字符串,依然存在作為終止符的NUL字節。
ANSI C聲明如果對一個字符串常量進行修改,其效果是未定義的。它也允許編譯器把一個字符串常量存儲于一個地方,即使它在程序中多次出現。這就使得修改字符串常量變得極為危險,因為對一個常量進行修改可能殃及程序中其他字符串常量。因此,許多ANSI 編譯器不允許修改字符串常量,或者提供編譯時選項,讓你自行選擇是否允許修改字符串常量。在實踐中,請盡量避免這樣做。如果你需要修改字符串,請把它存儲于數組中。
之所以把字符串常量和指針放在一起討論,是因為在程序中使用字符串常量會生成一個“指向字符的常量指針”。當一個字符串常量出現于一個表達式中時,表達式所使用的值就是這些字符所存儲的地址。因此,你可以把字符串常量賦值給一個“指向字符的指針”,后者指向這些字符所存儲的地址。但是,你不能把字符串常量賦值給一個字符數組,因為字符串常量的直接值是一個指針,而不是這些字符本身。
3.2 基本聲明
變量聲明的基本形式是:
說明符(一個或多個) 聲明表達式列表對于簡單的類型,聲明表達式列表就是被聲明的標識符的列表。 對于更為復雜的類型,聲明表達式列表中的每個條目實際上是一個表達式。
說明符(specifier) 包含了一些關鍵字,用于描述被聲明的標識符的基本類型。說明符也可以用于改變標識符的缺省存儲類型和作用域。
同時,在聲明整型變量時,如果聲明中已經至少有了一個其他的說明符,關鍵字int可以省略。因此,下面兩個聲明的效果是相等的:
unsingned short int a; unsigned short a;
表3.3 顯示了所有這些變量聲明的變型。 同一個框內的所有聲明都是等同的。signed關鍵字一般只用于char 類型, 因為其他整數類型在缺省情況下都是有符號數。 至于char是否是signed,則因編譯器而異。 所以,char可能等同于signed char,也可能等同于 unsigned char。
3.2.1 初始化
在一個聲明中,你可給一個標量變量指定一個初始值,方法是在變量名后面跟一個等號(賦值號),后面時你想要賦給變量的值。 例如:
int j=15;3.2.2 聲明簡單數組
為了聲明一個一維數組,在數組名后面要跟一對方括號,方括號里面是一個整數,指定數組中元素的個數。 考慮下面這個聲明:
int values[20];對于這個聲明, 顯而易見的解釋是:我們聲明了一個整型數組,數組包含20個整形元素。這種解釋是正確的,但我們有一種更好的方法來閱讀這個聲明。 名字values加一個下標,產生一個類型為int 的值(共有20個整型值)。這個”聲明表達式“顯示了一個表達式中的標識符產生了一個基本類型的值,在本例中是int。
數組下標總是從0開始,最后一個元素的下標是元素的數目減1.我們沒有辦法修改這個屬性,但是如果你一定要讓某個數組的下標從0開始,只要在實際引用時把下標減去10即可。
3.2.3 聲明指針
聲明表達式也可用于聲明指針。在C語言的聲明中,先給出一個基本類型,緊隨其后的是一個標識符里欸包,這些標識符組成表達式,用于產生基本類型的變量。
例如
int *a;這條語句表示表達式 *a 產生的結果類型是int。知道了 * 操作符執行是間接訪問操作以后,我們可以推斷a肯定是一個指向int 的指針。
警告:
C在本質上是一種形式自由的語言,這很容易誘使你把星號寫在靠近類型的一側,如下所示:
int* a;這個聲明和前面一個聲明具有相同的意思,而且看上去更為清晰,a被聲明為int* 的指針。 但是,這并不是一個好技巧,原因如下:
int* b,c,d;人們很自然地以為這條語句把所有三個變量聲明為指向整型的指針,但事實上并非如此。我們被它的形式愚弄了。 星號實際上是表達式*b的一部分,只對這個標識符有用。b是一個指針, 但其余兩個變量只是普通的整型 。要聲明三個指針,正確的語法如下:
int *b ,*c, *d;在聲明指針變量時,你可以可為它指定初始值。這里有一個例子,它聲明了一個指針,并用一個字符串常量對其進行初始化:
char * message = "Hello World!";這條語句把message 聲明為指向字符的指針,并用字符串常量中第一個字符的地址對該指針進行初始化。
警告:
這種類型的聲明所面臨的一個危險是你容易誤解它的意思。在前面一個聲明中,看上去初始值似乎是賦給表達式*message ,事實上它是賦給message本身的。換句話說, 前面一個聲明相當于:
char *message; message = "Hello World!";3.2.4 隱式聲明
C語言中有幾種聲明,它的類型名可以省略。例如,函數如果不顯示地聲明返回值的類型,它就默認返回整型。當你使用舊風格聲明函數的形式參數時,如果省略了參數的類型,編譯器就默認它們是整型。最后,如果編譯器可以得到充足的信息,推導出一條語句實際上是一個聲明時,如果它缺少類型名,編譯器會假定它為整型。
考慮下面這個程序:
int a[10]; int c; b[10]; d;f(x){return x+1; }這個程序的前兩行都很尋常,但第3、4行在ANSI C中卻是非法的。第2行缺少類型名,但對于K&R 編譯器而言,它擁有足夠的信息判斷這條語句是一個聲明。 函數f缺少返回類型,于是編譯器就默認它返回整型。參數x也沒有類型名,同樣被默認為整型。
提示:
依賴隱式聲明不是一個好主意。
3.3 typedef
C語言支持一種叫做typedef的機制,它允許你為各種數據類型定義新名字。typedef聲明的寫法和普通的聲明基本相同,只是把typedef這個關鍵字出現在聲明的前面。例如,下面這個聲明:
char *ptr_to_char;把變量ptr_to_char 聲明為一個指向字符的指針。但是,在你添加typedef后,聲明變為:
typedef char *ptr_to_char;這個聲明把標識符ptr_to_char 作為指向字符的指針類型的新名字。 你可以像使用任何預定義名字一樣在下面的聲明中使用這個新名字。 例如:
ptr_to_char a;聲明a是一個指向字符的指針。
提示:
應該使用typedef而不是 #define 來創建新的類型名,因為后者無法正確地處理指針類型。例如
#define d_ptr_to_char char * d_ptr_to_char a,b;正確的聲明了 a,但是b卻被聲明為一個字符。 在定義更為復雜的類型名字時,如函數指針或者指向數組的指針,使用typedef更為合適。
3.4 常量
ANSI C允許你聲明常量,常量的樣子和變量完全一樣,只是它們的值不能修改。你可以使用const關鍵字來聲明常量。 如下面的例子:
int const a; const int a;這兩條語句都把a聲明為一個整數,它的值不能被修改。
當然, 由于a的值無法被修改,所以你無法把任何東西賦值給它。 如此一來,怎樣才能讓它在一開始擁有一個值呢? 有兩種方法:首先,可以在聲明時對它進行初始化,如下所示:
int const a=15;其次,在函數聲明中為const 的形參在函數被調用時會得到實參的值。
當涉及指針變量時,情況就會變得更有趣,因為有兩樣東西都有可能變成常量------指針變量和它所指向的實體。下面是幾個聲明的例子:
int *pi;pi是一個普通的指向整型的指針。
而變量:
則是一個指向整型常量的指針。 你可以修改指針的值,但是不能修改它所指向的值。
相比之下:
則聲明pci是一個指向整型的常量指針。此時指針是常量,它的值無法被修改,但可以修改它所指向的整型的值。
int const * const cpci;最后,在cpci這個例子里,無論是指針本身還是它所指向的值都是常量,不允許修改。
3.5 作用域
當變量在程序的某個部分被聲明時,它只有在程序的一定區域才能被訪問。這個區域由標識符的作用域(scope) 決定。標識符的作用域就是程序中該標識符可以被使用的區域。例如,函數的局部變量的作用域局限于該函數的函數體。這個規則意味著兩點。首先,其他函數都無法通過這些變量的名字訪問它們,因為這些變量在它們的作用于之外便不再有效。其次,只要分屬于不同的作用域,你可以給不同的變量起相同的名字。
編譯器可以確定4種不同類型的作用域—文件作用域、函數作用域、代碼塊作用域、原型作用域。
標識符聲明的位置決定它的作用域。
3.5.1 代碼塊作用域
位于一對花括號之間的所有語句稱為一個代碼塊。任何在代碼塊的開始位置聲明的標識符都具有代碼塊作用域(block scope) ,表示它們可以被這個代碼塊中的所有語句訪問。圖3.1種標識為6、7、9、10的變量都具有代碼塊作用域。
當代碼塊處于?嵌套狀態時,聲明于內層代碼塊的標識符的作用域到達該代碼塊的尾部便告終止。然而,如果內層代碼塊有一個標識符的名字與外層代碼塊的一個標識符同名,內層的那個標識符就隱藏外層的標識符----?外層的那個標識符無法在內層代碼塊中通過名字訪問。 聲明9的f和聲明6的f是不同的變量,后者無法在內層代碼塊中通過名字來訪問。
提示:
你應該避免在嵌套的代碼塊中出現相同的變量名,我們并沒有很好的理由使用這種技巧,它們只會在程序的調試或維護期間引起混淆。
不是嵌套的代碼塊則稍有不同。聲明于每個代碼塊的變量無法被另一個代碼塊訪問,因為它們的作用域并無重疊之處。由于兩個代碼塊的變量不可能同時存在,所以編譯器可以把它們存儲于用一個內存地址。例如,聲明10中的i可以和聲明9中的任何一個變量共享同一個內存地址。這種共享并不會帶來任何危害,因為在任何時刻,兩個非嵌套的代碼塊最多只有一個處于活動的狀態。
在 K&R C中,函數形參的作用域開始于形參的聲明處,位于函數體之外。如果在函數體內部聲明了名字與形參相同的局部變量,它們就將隱藏形參。這樣一來,形參便無法被函數的任何部分訪問。換句話說,如果在聲明6的地方聲明了一個局部變量e,那么函數體只能訪問這個局部變量,形參e就無法被函數體所訪問。當然,沒人會有意隱藏形參。 ANSI C 遏止了這種錯誤的可能性,它把形參的作用域設定為函數最外層的那個作用域(也就是真個函數體)。這樣,聲明于函數最外層作用域的局部變量無法和形參同名,因為它們的作用域相同。
3.5.2 文件作用域
任何在所有代碼塊之外聲明的標識符都具有文件作用域(file scope) ,它表示這些標識符從它們的聲明之處知道它所在的源文件結尾處都是可訪問的。圖3.1的聲明1和聲明2就是這一類。在文件中定義的函數名也具有文件作用域,因為函數名本身并不屬于任何代碼塊(如聲明4)。這里應該指出,在頭文件中編寫并通過#include 指令包含到其他文件中的聲明就好像它們是直接寫在那些文件中一樣。它們的作用域并不局限于頭文件的文件尾。
3.5.3 原型作用域
原型作用域(prototype scope) 只適用于在函數原型中聲明的參數名,如圖3.1中的聲明3和聲明8.在原型中(與函數的定義不同),參數的名字并非必需。但是,如果出現參數名,你可以隨便給它們取任何名字,它們不必與函數定義中的形參名匹配,也不必 與函數實際調用時所傳遞的實參匹配。 原型作用域防止這些參數名與程序其他部分的名字沖突。事實上,唯一可能出現的沖突就是在同一個原型中不止一次地使用同一個名字。
3.5.4 函數作用域
最后一種作用域的類型是函數作用域(function scope)。它只適用于語句標簽,語句標簽用于goto語句。基本上,函數作用域可以簡化為一條規則-----一個函數中的所有語句標簽必須唯一。我希望你永遠不要用到這個知識。
3.6 鏈接屬性
當組成一個程序的各個源文件分別被編譯之后,所有的目標文件以及那些從一個或多個函數庫中引用的函數鏈接在一起,形成可執行程序。然而,如果相同的標識符出現在幾個不同的源文件中,它們像Pascal那樣表示一個實體?還是表示不同的實體?標識符的鏈接屬性(linkage) 決定如何處理在不同文件中出現的標識符。標識符的作用域與它的鏈接屬性有關,但這兩個屬性并不相同。
鏈接屬性一共有三種:external(外部)、internal(內部)和none(無)。沒有鏈接屬性的標識符(none)總是被當作單獨的個體,也就是說該標識符的多個聲明被當作獨立的不同的實體。屬于internal鏈接屬性的標識符在用一個源文件內的所有聲明中都指同一個實體,但位于不同源文件的多個聲明則分屬于不同的實體。 最后,屬于external鏈接屬性的標識符不論生命多少次、位于幾個源文件都表示同一個實體。
| none | 總是被當作單獨的個體 |
| internal | 同一個源文件中都是同一個實體;在不同源文件中是不同的實體。 |
| external | 不論在哪,都是同一個實體 |
圖3.2的程序骨架通過展示名字聲明的所有不同方式,描述了鏈接屬性。在缺省的情況下,標識符b、c和f的連接屬性為external,其余標識符的鏈接屬性則為none。因此,如果另一個源文件也包含了標識符b的類似聲明并調用函數c,它們實際上訪問呢的是這個源文件所定義的實體。f的鏈接屬性之所以是external,是因為它是個函數名。在這個源文件中調用函數 f,它實際上將鏈接到其他源文件所定義的函數,甚至這個函數的定義可能出現在某個函數庫。
自己的理解:這里b之所以默認是external,是因為它是全局變量;這里c之所以是external,是因為這是函數的定義。
關鍵字extern和static用于在聲明中修改標識符的鏈接屬性。如果某個聲明在正常情況下具有external鏈接屬性,在它前面加上static關鍵字可以使它的鏈接屬性變成internal。例如,如果第2個聲明像下面這樣書寫
static int b;那么變量b就將變為這個源文件所私有。在其他源文件中,如果也鏈接到一個叫做b的變量,那么它所引用的是另一個不同的變量。類似,你也可以把函數聲明為static,如下:
static int c(int d)這可以防止它被其他源文件調用。
static只對缺省鏈接屬性為external的聲明才有改變鏈接屬性的效果。例如,盡管你可以在聲明5前面加上static關鍵字,但它的效果完全不一樣,因為e的缺省鏈接屬性并不是external。
extern關鍵字的規則更為復雜。一般而言,它為一個標識符指定external鏈接屬性,這樣就可以訪問在其他任何位置定義的這個實體。請考慮圖3.3的例子。聲明3為k指定external鏈接屬性。這樣一來,函數就可以訪問在其他源文件聲明的外部變量了。
提示:
從技術上說,這兩個關鍵字(extern 和 external)只有在聲明中才是必需的,如圖3.3中的聲明3(它的缺省鏈接屬性并不是external)。當用于具有文件作用域的聲明時,這個關鍵字是可選的。然而,如果你在一個地方定義變量,并在使用這個變量的其他源文件的聲明中添加external關鍵字,可以使讀者更容易理解你的意圖。
當extern關鍵字用于源文件中一個標識符的第1次聲明時,它指定該標識符具有external鏈接屬性。但是,如果它用于該標識符的第2次或以后的聲明時,它并不會更改由第一次聲明所指定的鏈接屬性。例如,圖3.3中的聲明4并不修改由聲明1所指定的變量i的鏈接屬性。
3.7 存儲類型
(未完待續)
3.8 static關鍵字
當用于不同的上下文環境時,static關鍵字具有不同的意思,確實很不幸,因為這總是給C程序員新手帶來混淆。本節對static關鍵字作了總結。
?當它用于函數定義時,或用于代碼塊之外的變量聲明時,static關鍵字用于修改標識符的鏈接屬性,從external變成internal,但標識符的存儲類型和作用域不受影響。\color{red}{static關鍵字用于修改標識符的鏈接屬性,從external變成internal,但標識符的存儲類型和作用域不受影響。}static關鍵字用于修改標識符的鏈接屬性,從external變成internal,但標識符的存儲類型和作用域不受影響。用這種方式聲明的函數或變量只能在聲明它們的源文件中訪問。
?當它用于代碼塊內部的變量聲明時,static關鍵字用于修改變量的存儲類型,從自動變量修改為靜態變量,但變量的鏈接屬性和作用于不受影響。\color{red}{static關鍵字用于修改變量的存儲類型,從自動變量修改為靜態變量,但變量的鏈接屬性和作用于不受影響。}static關鍵字用于修改變量的存儲類型,從自動變量修改為靜態變量,但變量的鏈接屬性和作用于不受影響。用這種方式聲明的變量在程序執行之前創建,并在程序的整個執行期間一直存在,而不是每次在代碼塊開始執行時創建,在代碼塊執行完畢后銷毀。
3.9 作用域、存儲類型實例
這里很重要,好好理解這個例子。
屬于文件作用域的聲明在缺省情況下為external鏈接屬性,所以第一行的a的鏈接屬性為external。如果b的定義在其他地方,第2行的extern關鍵字在技術上并非必需,但在風格上卻是加上這個關鍵字為好。第3行的static關鍵字修改了c的缺省鏈接屬性,把它改為internal。聲明了變量a和b(具有external鏈接屬性)的其他源文件在使用這兩個變量時實際所訪問的是聲明于此處的這兩個變量。 但是,變量c只能由這個源文件訪問,因為它具有internal鏈接屬性。
變量a、b和c的存儲類型為靜態,表示它們并不是存儲在堆棧中。 因此,這些變量在程序執行之前創建,并一直保持它們的值,直到程序結束。 當程序開始執行時,變量a被初始化為5.
這些變量的作用域一直延伸到這個源文件結束為止,但第7行和第13行聲明的局部變量a和b在那部分程序中將隱藏同名的靜態變量。因此,這3個變量的作用域為:
a : 第1行到12行 , 第17行到29行 b: 第2到6行, 第25行到29行 c : 第3到29行第4行聲明了2個標識符。d的作用域從第4行直到文件結束。 函數d的定義對于這個源文件中任何以后想要調用它的函數而言起到了函數原型的作用。 作為函數名,d在缺省情況下具有external鏈接屬性,所以其他源文件只要在文件上存在d的原型,就可以調用d。 如果我們將函數聲明為static,就可以把它的鏈接屬性從external改為internal,但這樣做將使其他源文件不能訪問這個函數。 對于函數而言, 存儲類型并不是問題, 因為代碼總是存儲在靜態內存中。
參數e不具有鏈接屬性, 所以我們只能從函數內部通過名字訪問它。 它具有自動存儲類型,所以它在函數被調用時被創建,當函數返回時消失。由于和局部變量沖突,它的作用域局限在第6行到11行,17行到19行以及第23到24行。
第6-8行聲明局部變量,所以它們的作用域到函數結束為止。它們不具有鏈接屬性,所以它們不能在函數的外部通過名字訪問。f的存儲類型是自動,當函數每次被調用時,它通過隱式賦值被初始化為15.b的存儲類型使寄存器類型,所以它的初始值是垃圾。g 的 存儲類型是靜態,所以它在程序的整個執行過程中一直存在。當程序開始執行時,它被初始化為20.當函數每次被調用時,它并不會被重新初始化。
第9行的聲明并不需要。這個代碼塊位于第1行聲明的作用域之內。
第12行和13行為代碼塊聲明局部變量。 它們都具有自動存儲類型,不具有鏈接屬性,它們的作用域延伸到第16行。這些變量和先前聲明的a和e不同,而且由于名字沖突,在這個代碼塊中,以前聲明的同名變量是不能被訪問的。
第14行使全局變量h在這個代碼塊中可以被訪問。 它具有external鏈接屬性,存儲于靜態內存中。這是唯一一個必須使用extern關鍵字的聲明, 如果沒有它,h將變成另一個局部變量。
第25行聲明了函數i,它具有靜態鏈接屬性。靜態鏈接屬性可以防止它被這個源文件之外的任何函數使用。事實上,其他源文件也可能聲明它自己的函數i,它與這個源文件的i是不同的函數。 i的作用域從它聲明的位置直到這個源文件結束。函數d不可以調用函數i,因為在d之前不存在i的原型。
3.10 總結
具有external鏈接屬性的實體在其他語言術語里稱為全局實體,所有源文件的所有函數都可以訪問它。只要變量并非聲明于代碼塊或函數定義內部,它在缺省情況下的鏈接屬性就是external。如果一個變量聲明在代碼塊內部,在它前面添加extern關鍵字 將使它所應用的是全局變量而不是局部變量。
具有external鏈接屬性的實體總是具有靜態存儲類型。全局變量在程序開始執行前創建,并在程序整個執行過程中始終存在。從屬于函數的局部變量在函數開始執行時創建,在函數執行完成后銷毀,但用于執行函數的機器指令在程序的生命期內一直存在。
局部變量由函數內部使用,不能被其他函數通過名字引用。 它在缺省情況下的存儲類型為自動,這是基于兩個原因:其一,當這些變量需要時才為它們分配存儲,這樣可以減少內存的總需求量。 其二,在堆棧上為它們分配存儲可以有效地實現遞歸。如果你覺得讓變量的值在函數的多次調用中始終保持原先的值非常重要的話,可以修改它的存儲類型,把它從自動變量改為靜態變量。
總結
以上是生活随笔為你收集整理的《C和指针》读书笔记第三章数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 标准模板库之容器-《C++标准库(第二版
- 下一篇: Leetcode1688. 比赛中的配对