c語言-指標陣列
指標
記憶體和地址怎麼理解呢?
機器中有一些位置,每一個位置被稱為【位元組】/byte,許多現代機器上,每個位元組包含8個位。更大記憶體單位【字】,通常包含2個或4個位元組組成。
一個字包含4個位元組,它的地址是什麼?
他仍然只有一個地址,是最左邊還是最右邊的那個位元組的位置,取決於機器。
機器事實-關於整型的起始位置:
在要求邊界對齊(boundary alignment)的機器上,整型儲存的起始位置只能是某些特定的位元組,通常是2或4的倍數。
變數名和地址關係?
所有高階語言的特性之一,就是通過名字而不是地址來訪問記憶體的位置。那麼名字和地址是怎麼關聯的呢?關聯關係不是硬體所提供的,它是由編譯器實現的,硬體仍然通過地址訪問記憶體位置。
儲存在記憶體的一個32位值,到底是整型還是浮點呢?
什麼型別取決於使用方式,如果使用整型算術指令,這個值就被解釋為整數,如果使用浮點型指令,他就是個浮點數。
指標變數他的地址對應的記憶體中存的是什麼內容?
儲存是一個值,這個值,通常被當做地址來使用。
對於int a=112; int *d = &a; 為什麼編譯器最終將a的地址寫入d對應的記憶體空間,而不是把112寫入d的記憶體空間呢?
原因應該是這樣設計靈活,且效率高,更重要的是硬體的設計如:RAM 有地址這個概念
符號* 什麼含義?
用來執行間接訪問操作。對一個變數間接訪問,宣告含義?就是不把這塊記憶體值直接使用,而是把它用作地址,把這個地址對應的內容取出。
指標未初始化是什麼含義,有什麼風險?對未初始化的指標訪問會發生什麼?如int *a ; *a=12;
這個操作可能會有下面情景發生
1. 運氣好a 的初始值是非法地址,出錯,終止程式,在unix系統上這個錯誤memory fault ,在windows出現一般保護性異常(general protection exception)
2.更嚴重的情況,這個指標偶爾可能包含一個合法地址,即是這樣的情況:引發錯誤的程式碼,與原先用於操作那個值得程式碼完全不相干。
編譯器能檢測出來初始化的指標p, *p = 12,這樣,並給出警告嗎?
標準定義的NULL指標什麼含義?我怎麼定義一個NULL指標變數?怎麼判斷一個指標是不是NULL指標。
表示不指向任何東西。定義一個變數賦值為零。與零值比較來判斷指標是不是NULL指標。
在機器層面NULL指標內容到底是什麼呢?
NULL 指標可能不是0,在此情況,編譯器將負責零值和內部值之間的翻譯和轉換。
對一個NULL指標間接訪問會出現什麼情況?
因編譯器而異,有的不報錯。有的發生錯誤,並終止程式。
怎麼定義一個指標變數才是好習慣?
如果你已經知道指標將被初始化為什麼地址,就把它初始化為該地址,否則把它初始化為NULL。
在對一個變數賦值時,編譯器會校驗,檢查什麼?
校驗,使用變數型別和應該使用的變數的型別不一致時,編譯器會報錯。
分析下*&a = 25; 非重點
指標常量是什麼能不能訪問操作並賦值?
*100 = 25; *100 就是指標常量。這條語句是非法的,需要強制型別轉換如:
*(int *) 100 = 25;
指標常量什麼樣情況才能用到?
如:與輸入輸出裝置控制器通訊。
指標的指標怎麼用示意圖畫明白?
指標定義和使用的分析:
定義(包括宣告和初始化)
宣告的分析:有* 就是指標;初始化,左值使用,*【變數】代表是將右邊的值寫入指標變數記憶體中(【變數】);
**【變數】左值使用,賦值,*【變數】表示地址記憶體開啟,開啟結果還是地址,**【變數】表示多次開啟,還是代表開啟地址,注意編譯器能知道這種變化,**兩次就是二級指標
使用:*【變數】是右值表示式,*代表間接訪問,把【】中當做地址使用,找到這個地址對應的記憶體單元,把值拿出來,就是*【】表示式的值了
注意* 是右結合性
Int a =12;
Int *b = &a;
Int **c = &b;
畫圖要點:
1.箭頭===》是地址
2. 虛線。表示關係
3. 實線。表示*訪問
粗橢圓 當做右值使用,使用表示式的值。
粗方框 當做左值使用,指地址。
#include <stdlib.h>
#include <stdio.h>
int
main(){
int a=12;
int *b=&a;
int **c= &b;
printf("%p %p %p \n",a,b,c);
printf("%d %d %d \n",sizeof(a),sizeof(b),sizeof(c));
// printf("%s %s %s \n",a,*b,*c);
printf("%d %d %d \n",a,*b,**c);
}
結果:
分析結果: 指標佔用八個位元組,地址佔用八個位元組,實際上用6個位元組表示地址(0x開頭)
例子 :指標要用對等的地址
#include <stdlib.h>
#include <stdio.h>
int
main(){
int a=12;
int *b= &a;
int **c= &a;
printf("%p %p \n",a,c);
printf("%d %g \n",a,*c);
printf("%d %d \n",sizeof(a),sizeof(c));
}
結果:編譯器報錯!
結果分析:指標要用對等的地址(或低一級指標賦值如&b, 或者同級別指標&d)來賦值,編譯器會檢驗出不符合的情況
為什麼&ch 不能用作左值?
&ch結果會儲存在某個地方/某個記憶體,但是我無法知道他位於何處,這個表示式,看不出他在機器中那個特定位置,所以它不是一個合法的左值。
什麼是左值,右值?
左值可以再“=”號左側,也可以在等號右側,而右值只能出現在等號右側。
分析清楚一個指標表示式是左值還是右值有什麼好處?
分析表示式?
Char ch = ‘a’;
Char *cp = &ch;
示意圖要求1. 標出求值的順序,2中間專題是虛線,橢圓,方框
表示式
&ch
cp
&cp
*cp
*cp+1
*(cp+1)
++cp
Cp++
*++cp
*cp++
++*cp
(*cp)++
++*++cp
++*cp++
例項,strlen.c 必會默寫
指標+/- 整數 在什麼情況下可以這樣?
只能用於指向陣列中某個元素的指標,也適用於使用malloc函式動態分配獲得的記憶體。
指標指向陣列最後一個元素的後面的那個記憶體位置,有什麼問題?允許這樣,一般不允許對指向這個位置的指標進行間接訪問。
指標-指標什麼情況下允許這樣?
只有兩個指標指向同一個陣列中的元素時,相減的結果是ptrdiff_t(有符號整數型別)
< <= > >= 可以比較的兩個指標有什麼限制?
他們都指向同一個陣列中的元素,但是兩個任意的指標可以執行相等或不相等測試。
陣列
陣列和指標的區別與聯絡?
陣列和指標有著本質的不同,指標是一個標量值。陣列名卻有很多屬性如:陣列元素的數量。只有當陣列名在表示式中使用時,編譯器才會為它產生一個指標常量。
先來搞明白,什麼是宣告,什麼是定義?
c 語言中的物件有且只有一個定義,但它可以有多個extern 宣告。這裡的物件,只是跟連結器有關的“東西”比如函式和變數。
定義只能出現一個地方,宣告可以出現多次,宣告相當於普通的宣告,它說明並非本身,而是描述其他地方建立的物件。定義相當於特殊的宣告,它為物件分配記憶體
左值在編譯時可知,左值標示儲存結果的地方。右值直到執行時才知,如無特殊說明,右值表示的內容。
C語言中有“可修改的左值”,很奇怪!意味著還有不可可以修改的左值,這個奇怪的術語是為與陣列名(是左值)區分。
陣列和指標是如何訪問的?
char a[9] = “abcdefgh”; ….. c= a[i];
編譯器符號表具有一個地址9980
執行時步驟1: 取i的值,將它與9980相加
執行時步驟2: 取地址(9980+i)的內容
char *p ; ……… c= *p;
編譯符號表有一個符號p, 它的地址為4624;
執行時步驟1: 取4624的內容,就是5081
執行時步驟2: 取地址5081 的內容
指定的訪問要靈活得多,但需要增加一次額外的提取。
定義為指標,但以陣列方式引用時會發生什麼?
指標當然可以p[i] 用,
char *p = “abcdefgh”; c=p[i];
編譯器符號表具有一個p, 地址為4624
執行步驟1 :取地址4624的內容,即5081,
執行步驟2: 取得i的值,並將它與5081相加
執行步驟3: 取得地址[ 5081 + i] 的內容
指定的訪問要靈活得多,但需要增加一次額外的提取。(和陣列a對比)
注意寫程式碼時,宣告與定義相匹配
指標 |
陣列 |
通常用於動態資料結構 |
通常用於儲存固定數目且資料型別相同的元素 |
相關的函式malloc(), free(). |
隱式分配和刪除 |
通常指向匿名資料 |
自身即為資料名 |
間接訪問 |
直接訪問 |
定義指標時,編譯器並不為指標指向的物件分配空間,它只是分配指標本身的空間,除非在定義時同時賦給指標一個字串常量進行初始化。如:
char *p = “break”;
float *pip = 3.14; //錯誤!!!!
什麼時候陣列和指標相同?
陣列宣告,包含三種
- extern , 如 extern char a[] ; 不能改寫成指標的形式
- 定義,如 char a[10]; 不能改寫成指標的形式
- 函式的引數,如func(char a[]); 選擇陣列形式,或者指標形式
陣列,在表示式中使用
如c = a[i]; 選擇陣列形式 或指標形式
規則1: 表示式中的陣列名被編譯器當作一個指向該陣列第一個元素的指標。
例外情況如: a 陣列作為sizeof()的運算元;b 使用& 操作符取陣列的地址;c陣列是一個字串,常量初始值。???
如:
int a[10], i=2;
a[i]; 相當於*(a+i); *(i+a);i[a]; 2[a];
規則2: 在函式引數的宣告中,陣列名被編譯器當作指向該陣列第一個元素的指標。
規則3: 下標總是與指標的偏移量相同
事實上,下標範圍檢查被認為並不值得加入C語言中。
指標總是有型別限制?p+2,p+i ; 是的,因為編譯器要直到步長啊。
陣列行參是如何被引用的?