C C 學習筆記 基礎知識6

2021-06-23 08:18:35 字數 4620 閱讀 6878

1複雜表示式與序列點

序列點是乙個時間點(在整個表示式全部計算完畢之後或在 ||、 &&、 ? : 或逗號運算子處, 或在函式呼叫之前), 此刻塵埃落定, 所有的***都已確保結束.

ansi/iso c 標準這樣描述:

在上乙個和下乙個序列點之間, 乙個物件所儲存的值至多只能被表示式的計算修改一次。而且前乙個值只能用於決定將要儲存的值。

i++ 和 ++i 都不同於 i+1。如果你要使 i 自增 1, 使用 i=i+1, i+=1, i++ 或 ++i, 而不是任何組合。

例如   i = i+1 合法, 而 a[i] = i++  則非法。

2*p++ 自增 p 還是 p 所指向的變數?

字尾 ++ 和 -- 操作符本質上比字首一目操作的優先順序高, 因此 *p++ 和*(p++) 等價, 它自增 p 並返回 p 自增之前所指向的值

要自增 p 指向的值, 使用 (*p)++, 如果***的順序無關緊要也可以使用 ++*p。

3有乙個 char * 型指標正巧指向一些 int 型變數, 想跳過它們。為什麼如下的**((int *)p)++; 不行?

在 c 語言中, 型別轉換意味著 「把這些二進位制位看作另一種型別, 並作相應的對待」; 這是乙個轉換操作符, 根據定義它只能生成乙個右值 (rvalue)。而右值既不

能賦值, 也不能用 ++ 自增。

要達到目的可以用:

p = (char *)((int *)p + 1);

或者,因為 p 是 char * 型, 直接用

p += sizeof(int);

4能否用 void** 指標作為引數, 使函式按引用接受一般指標?

不可移植。c 中沒有一般的指標的指標型別。void* 可以用作一般指標只是因為當它和其它型別相互賦值的時候, 如果需要, 它可以自動轉換成其它型別; 但

是, 如果試圖這樣轉換所指型別為 void* 之外的型別的 void** 指標時, 這個轉換不能完成。

5空指標

語言定義中說明, 每一種指標型別都有乙個特殊值 —— 「空指標」 —— 它與同型別的其它所有指標值都不相同, 它 「與任何物件或函式的指標值都不相等」。

也就是說, 取位址操作符 & 永遠也不能得到空指標, 同樣對 malloc() 的成功呼叫也不會返回空指標, 如果失敗, malloc() 的確返回空指標, 這是空指標的典型用法:

表示 「未分配」 或者 「尚未指向任何地方」 的指標。

空指標在概念上不同於未初始化的指標。空指標可以確保不指向任何物件或函式; 而未初始化指標則可能指向任何地方。

如上所述, 每種指標型別都有乙個空指標, 而不同型別的空指標的內部表示可能不盡相同。儘管程式設計師不必知道內部值, 但編譯器必須時刻明確需要那種空指標, 以便在需要的時候加以區分。

6null 是什麼?

作為一種風格, 很多人不願意在程式中到處出現未加修飾的 0。

因此定義了預處理巨集 null (在 和其它幾個標頭檔案中) 為空指標常數, 通常是 0 或者((void *)0) 。

希望區別整數 0 和空指標 0 的人可以在需要空指標的地方使用 null。

使用 null 只是一種風格習慣; 預處理器把所有的 null 都還原回 0, 而編譯還是依照上文的描述處理指標上下文的 0。

特別是, 在函式呼叫的引數裡, null之前 (正如在 0 之前) 的型別轉換還是需要。 

帶修飾的 null 和帶修飾的 0 完全等價 ,null 只能用作指標常數;。

#define null ((void *)0)

除了潛在地幫助錯誤程式執行 (僅限於使用同樣型別指標的機器, 因此幫助有限) 以外, 這樣的定義還可以發現錯誤使用 null 的程式。

有兩條簡單規則你必須遵循:

(1)當你在原始碼中需要空指標常數時, 用 「0」 或 「null」。

(2)如果在函式呼叫中 「0」 或 「null」 用作引數, 把它轉換成被調函式需要的指標型別

7char a[ ] 和 char *a 是一樣的?

並非如此。 陣列不是指標。陣列定義 char a[6] 請求預留 6 個字元的位置, 並用名稱 「a」 表示。也就是說, 有乙個稱為 「a」 的位置, 可以放入 6 個字元。而指標申明 char *p, 請求乙個位置放置乙個指標, 用名稱 「p」 表示。這個指標幾乎可以指向任何位置: 任何字元和任何連續的字元, 或者**也不指。

根據 x 是陣列還是指標, 類似 x[3] 這樣的引用會生成不同的**。認識到這一點大有裨益。以上面的宣告為例, 當編譯器看到表示式 a[3] 的時候, 它生成**從 a 的位置開始跳過 3 個, 然後取出那個字元. 如果它看到 p[3], 它生成**找到「p」 的位置, 取出其中的指標值, 在指標上加 3 然後取出指向的字元。換言之, a[3]是名為 a 的物件 (的起始位置) 之後 3 個位置的值, 而 p[3] 是 p 指向的物件的 3 個位置之後的值. 在上例中, a[3] 和 p[3] 碰巧都是 』l』 , 但是編譯器到達那裡的途徑不盡相同。本質的區別在於類似 a 的陣列和類似 p 的指標一旦在表示式中出現就會按照不同的方法計算, 不論它們是否有下標。

陣列和指標 「等價」不表示它們相同, 甚至也不能互換。它的意思是說陣列和指標的演算法定義可以用指標方便的訪問陣列或者模擬陣列。

特別地, 等價的基礎來自這個關鍵定義:

乙個 t 的陣列型別的左值如果出現在表示式中會蛻變為乙個指向陣列第乙個成員的指標(除了三種例外情況); 結果指標的型別是 t 的指標。

這就是說, 一旦陣列出現在表示式中, 編譯器會隱式地生成乙個指向陣列第乙個成員地指標, 就像程式設計師寫出了 &a[0] 一樣。

例外的情況是, 陣列為 sizeof 或 &操作符的運算元, 或者為字元陣列的字串初始值。

作為這個這個定義的後果, 編譯器並那麼不嚴格區分陣列下標操作符和指標。在形如 a[i] 的表示式中, 根據上邊的規則, 陣列蛻化為指標然後按照指標變數的方式如 p[i] 那樣定址,  儘管最終的記憶體訪問並不一樣。

如果你把陣列位址賦給指標:

p = a;

那麼 p[3] 和 a[3] 將會訪問同樣的成員。

8為什麼作為函式形參的陣列和指標申明可以互換呢?

由於陣列會馬上蛻變為指標, 陣列事實上從來沒有傳入過函式。允許指標引數宣告為陣列只不過是為讓它看起來好像傳入了陣列, 因為該引數可能在函式內當作陣列使用。

特別地, 任何宣告 「看起來象」 陣列的引數, 例如

void f(char a)

在編譯器裡都被當作指標來處理, 因為在傳入陣列的時候,那正是函式接收到的.

void f(char *a)

這種轉換僅限於函式形參的宣告, 別的地方並不適用。如果這種轉換令你困惑, 請避免它

9陣列和指標地區別是什麼?

陣列自動分配空間, 但是不能重分配或改變大小。指標必須明確賦值以指向分配的空間 (可能使用 malloc), 但是可以隨意重新賦值 (即, 指向不同的物件), 同時除了表示乙個記憶體塊的基址之外, 還有許多其它的用途。

由於陣列和指標所謂的等價性(參見問題 6.3), 陣列和指標經常看起來可以互換, 而事實上指向 malloc 分配的記憶體塊的指標通常被看作乙個真正的陣列(也可以用 [ ] 引用)。

但是, 要小心 sizeof。

可以用指向 malloc 分配的記憶體的指標來模擬陣列。執行

#include

int *dynarray;

dynarray = malloc(10 * sizeof(int));

以後 (如果 malloc 呼叫成功), 你可以象傳統的靜態分配的陣列那樣引用dynarry[i] (i 從 0 到 9)。唯一的區別是 sizeof 不能給出 「陣列」 的大小。

陣列和下標在 c 語言中可以互換。

這個奇怪的事實來自陣列下標的指標定義, 即對於任何兩個表示式 a 和 e, 只要其中乙個是指標表示式而另乙個為整數, 則 a[e] 和 *((a)+(e)) 完全一樣。

陣列蛻化為指標的規則 (參見問題 6.3) 不能遞迴應用。陣列的陣列 (即 c 語言中的二維陣列) 蛻化為陣列的指標, 而不是指標的指標。

陣列指標常常令人困惑, 需要小心對待; 

如果你向函式傳遞二位陣列:

int array[nrows][ncolumns];

f(array);

那麼函式的宣告必須匹配:

void f(int a[ncolumns]) 或者

void f(int (*ap)[ncolumns]) /* ap 是個陣列指標 */

在第乙個宣告中, 編譯器進行了通常的從 「陣列的陣列」 到 「陣列的指標」 的隱式轉換; 第二種形式中的指標定義顯而易見。因為被調函式並不為陣列分配位址, 所以它並不需要知道總的大小, 所以行數 nrows 可以省略。但陣列的寬度依然重要, 所以列維度 ncolumns (對於三維或多維陣列, 相關的維度) 必須保留。

如果乙個函式已經定義為接受指標的指標, 那麼幾乎可以肯定直接向它傳入二維陣列毫無意義。

10當陣列是函式的引數時, 為什麼 sizeof 不能正確報告陣列的大小?

編譯器把陣列引數當作指標對待 , 因而報告的時指標的大小。

C C 學習筆記 基礎知識3

1 和 和 有什麼區別 1 和 對運算元進行求值運算,和 只是判斷邏輯關係。2 和 在在判斷左側運算元就能確定結果的情況下就不再對右側運算元求值。注意 在程式設計的時候有些時候將 或 替換成 或 沒有出錯,但是其邏輯是錯誤的,可能會導致不可預想的後果 比如當兩個運算元乙個是 1 另乙個是 2 時 2...

C C 學習筆記 基礎知識7

1字元和字串 在 c 語言中字元用它們的字符集值對應的小整數表示。因此,你不需要任何轉換函式 如有你有字元,你就有它的值。數字字元和它們對應的 0 9 的數字之間相互轉換時,加上或減去常數 0 也就是說,0 的字元值。字串用字元陣列表示 通常你操作的是字元陣列的第乙個字元的指標,c語言從來不會把陣列...

C C 學習筆記 基礎知識10

1棧 堆和靜態區 一般來說,可以簡單的理解為記憶體分為三個部分 靜態區,棧,堆。其實堆疊就是棧,而不是堆。堆的英文是heap 棧的英文是stack,也翻譯為堆疊。堆和棧都有自己的特性。記憶體也是這樣,記憶體的三個部分,不是所有的東西都能存進去的。靜態區 儲存自動全域性變數和static變數 包括st...