C語言指標(1)嵌入式linux

2021-08-15 02:55:42 字數 4663 閱讀 4288

計算機中所有的資料都必須放在記憶體中,不同型別的資料占用的位元組數不一樣,例如 int占用4個位元組,char 占用1個位元組。為了正確地訪問這些資料,必須為每個位元組都編上號碼,就像門牌號、身份證號一樣,每個位元組的編號是唯一的,根據編號可以準確地找到某個位元組。

我們將記憶體中位元組的編號稱為

位址(address

)或指標(

pointer

)。位址從 0 開始依次增加,對於 32 位環境,程式能夠使用的記憶體為 4gb,最小的位址為 0,最大的位址為 0xffffffff。

#include

int main()

執行結果:

0x28ff3c, 0x28ff10

%#x表示以十六進製制形式輸出,並附帶字首0x。a是乙個變數,用來存放整數,需要在前面加&來獲得它的位址;str本身就表示字串的首位址,不需要加&。

c語言用變數來儲存資料,用函式來定義一段可以重複使用的**,它們最終都要放到記憶體中才能供 cpu 使用。

資料和**都以二進位制的形式儲存在記憶體中,計算機無法從格式上區分某塊記憶體到底儲存的是資料還是**。當程式被載入到記憶體後,作業系統會給不同的記憶體塊指定不同的許可權,擁有讀取和執行許可權的記憶體塊就是**,而擁有讀取和寫入許可權(也可能只有讀取許可權)的記憶體塊就是資料。cpu

只能通過位址來取得記憶體中的**和資料,程式在執行過程中會告知 cpu 要執行的**以及要讀寫的資料的位址。如果程式不小心出錯,或者開發者有意為之,在 cpu 要寫入資料時給它乙個**區域的位址,就會發生記憶體訪問錯誤。這種記憶體訪問錯誤會被硬體和作業系統攔截,強制程式崩潰,程式設計師沒有挽救的機會。cpu

訪問記憶體時需要的是位址,而不是變數名和函式名!變數名和函式名只是位址的一種助記符,當原始檔被編譯和鏈結成可執行程式後,它們都會被替換成位址。編譯和鏈結過程的一項重要任務就是找到這些名稱所對應的位址。假設變數 a、b、c 在記憶體中的位址分別是 0x1000、0x2000、0x3000,那麼加法運算c = a + b;將會被轉換成類似下面的形式:

0x3000 = (0x1000) + (0x2000);

( )表示取值操作,整個表示式的意思是,取出位址 0x1000 和 0x2000 上的值,將它們相加,把相加的結果賦值給位址為 0x3000 的記憶體變數名和函式名為我們提供了方便,讓我們在編寫**的過程中可以使用易於閱讀和理解的英文本串,不用直接面對二進位制位址,那場景簡直讓人崩潰。需要注意的是,雖然變數名、函式名、字串名和陣列名在本質上是一樣的,它們都是位址的助記符,但在編寫**的過程中,我們認為變數名表示的是資料本身,而函式名、字串名和陣列名表示的是**塊或資料塊的首位址。資料在記憶體中的位址也稱為指標,如果乙個變數儲存了乙份資料的指標,我們就稱它為指標變數。在c語言中,允許用乙個變數來存放指標,這種變數稱為指標變數。指標變數的值就是某份資料的位址,這樣的乙份資料可以是陣列、字串、函式,也可以是另外的乙個普通變數或指標變數。現在假設有乙個 char 型別的變數 c,它儲存了字元 'k'(ascii碼為十進位制數 75),並占用了位址為 0x11a 的記憶體(位址通常用十六進製制表示)。另外有乙個指標變數 p,它的值為 0x11a,正好等於變數 c 的位址,這種情況我們就稱 p 指向了 c,或者說 p 是指向變數 c 的指標。

定義指標變數與定義普通變數非常類似,不過要在變數名前面加星號*,格式為:

資料型別 *變數名;      

資料型別 *變數名 = 值 ;

*表示這是乙個指標變數,資料型別表示該指標變數所指向的資料的型別 。例如:

int *p1;

p1 是乙個指向 int 型別資料的指標變數,至於 p1 究竟指向哪乙份資料,應該由賦予它的值決定。再如:

int a = 100;

int *p_a = &a;

在定義指標變數 p_a 的同時對它進行初始化,並將變數 a 的位址賦予它,此時 p_a 就指向了 a。值得注意的是,p_a 需要的是乙個位址,a 前面必須要加取位址符&,否則是不對的。

和普通變數一樣,指標變數也可以被多次寫入,只要你想,隨時都能夠改變指標變數的值,請看下面的**:

//定義普通變數

float a = 99.5, b = 10.6;

char c = '@', d = '#';

//定義指標變數

float *p1 = &a;

char *p2 = &c;

//修改指標變數的值

p1 = &b;

p2 = &d;

*是乙個特殊符號,表明乙個變數是指標變數,定義 p1、p2 時必須帶*。而給 p1、p2 賦值時,因為已經知道了它是乙個指標變數,就沒必要多此一舉再帶上*,後邊可以像使用普通變數一樣來使用指標變數。也就是說,定義指標變數時必須帶

*,給指標變數賦值時不能帶*。

需要強調的是,p1、p2 的型別分別是float*和char*,而不是float和char,它們是完全不同的資料型別,要引起注意。

*pointer;

#include

int main()

執行結果:

15, 15

*p 代表的是 a 中的資料,它等價於 a,可以將另外的乙份資料賦值給它,也可以將它賦值給另外的乙個變數。

*在不同的場景下有不同的作用:*可以用在指標變數的定義中,表明這是乙個指標變數,以和普通變數區分開;使用指標變數時在前面加*表示獲取指標指向的資料,或者說表示的是指標指向的資料本身。

也就是說,定義指標變數時的*和使用指標變數時的*意義完全不同。以下面的語句為例:

int *p = &a;

*p = 100;

第1行**中*用來指明 p是乙個指標變數,第2行**中*用來獲取指標指向的資料。

需要注意的是,給指標變數本身賦值時不能加*。修改上面的語句:

int *p;

p = &a;

*p = 100;

第2行**中的 p前面就不能加*。

指標變數也可以出現在普通變數能出現的任何表示式中,例如:

int x = 10;

int y = 20;

int *px = &x;

int *py = &y;

y = *px + 5;   //表示把x的內容加5並賦給y,*px+5相當於(*px)+5

y = ++*px;    //px的內容加上1之後賦給y,++*px相當於++(*px)

y = *px++;    //相當於y=*(px++)

py = px;        //把乙個指標的值賦給另乙個指標

#include

int main()

else

return 0;

}執行結果:

&a=0x28ff44, &b=0x28ff30, &c=0x28ff2b

pa=0x28ff44, pb=0x28ff30, pc=0x28ff2b

pa=0x28ff48, pb=0x28ff38, pc=0x28ff2c

pa=0x28ff40, pb=0x28ff28, pc=0x28ff2a

2686784

從運算結果可以看出:pa、pb、pc每次加 1,它們的位址分別增加 4、8、1,正好是int、double、char型別的長度;減 2 時,位址分別減少 8、16、2,正好是 int、double、char型別長度的 2 倍。

這很奇怪,指標變數加減運算的結果跟資料型別的長度有關,而不是簡單地加 1或減 1,這是為什麼呢?

以 a和pa為例,a 的型別為 int,占用 4個位元組,pa 是指向 a 的指標,如下圖所示:

剛開始的時候,pa 指向 a 的開頭,通過 *pa 讀取資料時,從 pa 指向的位置向後移動 4 個位元組,把這 4 個位元組的內容作為要獲取的資料,這 4 個位元組也正好是變數 a 占用的記憶體。

這個時候 pa 指向整數 a 的中間,*pa 使用的是紅色虛線畫出的 4 個位元組,其中前 3 個是變數 a 的,後面 1 個是其它資料的,把它們「攪和

」在一起顯然沒有實際的意義,取得的資料也會非常怪異。

如果pa++;使得位址加 4的話,正好能夠完全跳過整數 a,指向它後面的記憶體,如下圖所示:

我們知道,陣列中的所有元素在記憶體中是連續排列的,如果乙個指標指向了陣列中的某個元素,那麼加 1就表示指向下乙個元素,減 1就表示指向上乙個元素,這樣指標的加減運算就具有了現實的意義。

不過c語言並沒有規定變數的儲存方式,如果連續定義多個變數,它們有可能是挨著的,也有可能是分散的,這取決於變數的型別、編譯器的實現以及具體的編譯模式,所以對於指向普通變數的指標,我們往往不進行加減運算,雖然編譯器並不會報錯,但這樣做沒有意義,因為不知道它後面指向的是什麼資料。

#include

int main()

return 0;

}在vs2010 debug 模式下的執行結果為:

3, -858993460, -858993460, 2, -858993460, -858993460, 1, -858993460,

可以發現,變數 a、b、c並不挨著,它們中間還參雜了別的輔助資料。

指標變數除了可以參與加減運算,還可以參與比較運算。當對指標變數進行比較運算時,比較的是指標變數本身的值,也就是資料的位址。如果位址相等,那麼兩個指標就指向同乙份資料,否則就指向不同的資料。

上面的**(第乙個例子)在比較pa 和 paa 的值時,pa 已經指向了 a的上乙份資料,所以它們不相等。而 a 的上乙份資料又不知道是什麼,所以會導致 printf()輸出乙個沒有意義的數,這正好印證了上面的觀點,

不要對指向普通變數的指標進行加減運算。

另外需要說明的是,不能對指標變數進行乘法、除法、取餘等其他運算,除了會發生語法錯誤,也沒有實際的含義。

嵌入式學習(二) 嵌入式系統C 語言

1 從 cpu 復位時的指定位址開始執行 2 跳轉至彙編 startup 處執行 3 跳轉至使用者主程式 main 執行,在 main 中完成 a.初試化各硬體裝置 b.初始化各軟體模組 c.進入死迴圈 無限迴圈 d呼叫各模組的處理函式 下面是幾個 著名 的死迴圈 1 作業系統是死迴圈 2 win3...

嵌入式C語言總結

這幾天花了兩天時間看了一些嵌入式c語言方面的內容,以下是一些讀書筆記,記錄一下。1 不能有返回值 2 不能向isr 傳遞引數 3 isr盡可能的短 4 printf有重入問題 1 中斷服務程式isr 2 硬體初始化 1 某io 晶元被定為在 cpu的儲存空間而非 io空間,而且暫存器對應於某特定位址...

嵌入式c語言優化

一.演算法和資料結構優化 1.針對應用的演算法優化,比如,在音訊編譯碼中,用fft實現時頻變換。2.c語言級別的優化,如排序演算法的選擇。3.資料結構的選擇,如果需要隨機訪問,則盡量選擇陣列,如果需要隨機插入刪除,則可以選擇鍊錶。4.盡量用指標代替資料操作,大部分編譯對於指標會生成更小更快的 5.盡...