有人稱指針是C語(yǔ)言的靈魂,有人又稱指針說(shuō)指針是C語(yǔ)言最難的部分,那么,指針對(duì)C語(yǔ)言來(lái)說(shuō)到底意味著什么?指針到底是不是C語(yǔ)言和C++經(jīng)久不衰的原因之一呢?
成都創(chuàng)新互聯(lián)公司是一家專業(yè)提供富民企業(yè)網(wǎng)站建設(shè),專注與網(wǎng)站建設(shè)、成都網(wǎng)站建設(shè)、H5技術(shù)、小程序制作等業(yè)務(wù)。10年已為富民眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)站設(shè)計(jì)公司優(yōu)惠進(jìn)行中。指針的問(wèn)題不是三言兩語(yǔ)就能表達(dá)清楚的,而在《C和指針》第六章中也僅僅是提到了指針有關(guān)的部分內(nèi)容,指針的精彩與巧妙之處,還需要在后續(xù)的章節(jié)和具體的項(xiàng)目代碼中細(xì)細(xì)體會(huì)。先來(lái)看看本章內(nèi)容的基本框架(思維導(dǎo)圖)。
思維導(dǎo)圖
從思維導(dǎo)圖里可以看出,關(guān)于指針的內(nèi)容還是比較多的,我們的重點(diǎn)需要放在指針表達(dá)式的理解上。
內(nèi)存和地址是一個(gè)很深刻的話題,為了方便地訪問(wèn)計(jì)算機(jī)的內(nèi)存數(shù)據(jù),我們會(huì)對(duì)其進(jìn)行編號(hào),就像現(xiàn)實(shí)中我們郵寄地址的郵政編碼一樣。書(shū)中提到,我們只對(duì)兩件事情感興趣。
在現(xiàn)實(shí)中我們需要保存各種各樣的數(shù)值,然而對(duì)于計(jì)算機(jī)的內(nèi)存來(lái)說(shuō),只有0
和1
,所以某內(nèi)存中的到底表示什么值,則需要看被解釋為什么值。
int a = 112, b = -1;
float c = 3.14;
int *d = &a;
float *e = &c;
然而在內(nèi)存中,確有著如下的數(shù)值。
所以書(shū)中特別強(qiáng)調(diào):不能簡(jiǎn)單地通過(guò)檢查一個(gè)值的位來(lái)判斷它的類型。
從6.2
小節(jié)的內(nèi)容可以看出,d和e中存儲(chǔ)的值恰好差8,也就是兩個(gè)int型變量的距離(因?yàn)橐粋€(gè)int型變量一般占4個(gè)字節(jié)),那么變量d
和e
的值到底是多少呢?究竟是其本身的值呢?還是其指向內(nèi)存地址內(nèi)所保存的值呢?
按照規(guī)定,變量的值就是分配給變量的內(nèi)存位置所存儲(chǔ)的數(shù)值,即使是指針變量也不例外。從這個(gè)規(guī)定來(lái)看,變量d
和e
的值應(yīng)該分別是100
和108
。
通過(guò)一個(gè)指針訪問(wèn)它所指向的地址的過(guò)程稱為間接訪問(wèn)或解引用指針。這個(gè)用于執(zhí)行間接訪問(wèn)的操作符是單目操作符*
。下面還是通過(guò)以上的一個(gè)變量看看間接訪問(wèn)操作符如何訪問(wèn)所需的數(shù)值。
int main()
{int a = 112, b = -1;
float c = 3.14;
int *d = &a;
float *e = &c;
printf("a = %d\n", *d);
printf("c = %f\n", *e);
system("pause");
}
我們來(lái)看看打印輸出:
確實(shí)獲取到了我們需要的數(shù)值。
書(shū)中有這樣的例子:
int *a;
*a = 12;
顯然這樣的表達(dá)是有問(wèn)題的,從程序中來(lái)看,a指向某地址的值為12,但是究竟12位于哪個(gè)地址是未知的,所以可以寫(xiě)成下面的形式。
int num = 12;
int *a = #
printf("結(jié)果為:%d\n", *a);
system("pause");
return 0;
雖然我們并不能直接知道num究竟保存在什么地方,但是可以通過(guò)指針a間接訪問(wèn)num中存儲(chǔ)的值。
6.6 NULL指針NULL
指針相當(dāng)于是指針初始化的一種方式,表示當(dāng)前指針并未指向任何內(nèi)存地址,以為有時(shí)候編程的時(shí)候會(huì)遇到,需要定義一個(gè)指針,但是暫時(shí)并未想好要指向的內(nèi)存地址,這個(gè)時(shí)候NULL
指針就有了用武之地。舉個(gè)例子。
劍指 Offer 50. 第一個(gè)只出現(xiàn)一次的字符
在字符串 s 中找出第一個(gè)只出現(xiàn)一次的字符。如果沒(méi)有,返回一個(gè)單空格。
s只包含小寫(xiě)字母。
typedef struct hash
{int key;
int val;
UT_hash_handle hh;
}hash;
char firstUniqChar(char* s) {int len = strlen(s);
hash *f = NULL;
for(int i = 0; i< len; i++)
{int ele = s[i];
hash *temp = NULL;
HASH_FIND_INT(f,&ele,temp);
if(temp == NULL)
{temp = malloc(sizeof(hash));
temp->key = ele;
temp->val = 1;
HASH_ADD_INT(f,key,temp);
}
else
temp->val++;
}
for(int i = 0; i< len; i++)
{int ele = s[i];
hash *temp = NULL;
HASH_FIND_INT(f,&ele,temp);
if(temp != NULL && temp->val == 1)
{return temp->key;
}
}
return ' ';
}
創(chuàng)建了一個(gè)哈希表。用來(lái)存放字符數(shù)組s中出現(xiàn)的各個(gè)字母及其出現(xiàn)的頻率,在剛剛定義的時(shí)候,還沒(méi)有開(kāi)始統(tǒng)計(jì),所以不是很好確定到底要指向何處,因此出現(xiàn)了下面的表達(dá)式。
hash *f = NULL;
6.7 指針、間接訪問(wèn)和左值當(dāng)指針變量作為左值的時(shí)候,會(huì)出現(xiàn)什么樣的情況呢?我們一起來(lái)看看書(shū)上給的例子(有改動(dòng))。
int a = 4;
int *d = &a;
*d = 10 - *d;
printf("結(jié)果為:%d\n", *d);
system("pause");
return 0;
打印輸出如下:
可以看出在語(yǔ)句*d = 10 - *d
中,該語(yǔ)句可以這樣理解:用10減去指針d
所指向地址的值,并保存在原地址。
書(shū)中提到了這樣的運(yùn)算:*&
細(xì)品之下好像并沒(méi)有什么作用,因?yàn)橄冗M(jìn)行了取地址運(yùn)算,再間接訪問(wèn)。舉個(gè)例子:
int a = 4;
*&a = 40;
printf("結(jié)果為:%d\n", a);
system("pause");
return 0;
打印輸出:
可以看出,這與直接對(duì)變量a進(jìn)行賦值并沒(méi)有什么不同。
關(guān)于指針常量的定義(還有指針常量和常量指針的區(qū)別),我們?cè)诖饲暗恼鹿?jié)中介紹過(guò),具體見(jiàn):
第3章 數(shù)據(jù)
書(shū)中介紹了這樣一種情況,如果我們想在指定位置存儲(chǔ)指定的值,應(yīng)該怎么辦呢?比方說(shuō)在地址100的地方存儲(chǔ)25,難道我們直接像下面這樣:
*100 = 25;
其實(shí)這是錯(cuò)誤的做法,因?yàn)殚g接訪問(wèn)操作符只能作用于指針,而100是具體的數(shù)值,所以就會(huì)出現(xiàn)問(wèn)題,正確的做法應(yīng)該是下面這樣,使用強(qiáng)制類型轉(zhuǎn)換。
*(int *)100 = 25;
雖然書(shū)上是這樣介紹的,但實(shí)際在VS的集成開(kāi)發(fā)環(huán)境中會(huì)報(bào)錯(cuò),可能在其他環(huán)境中不會(huì)報(bào)錯(cuò)。因?yàn)槲覀冎苯泳庉嫷膬?nèi)存地址有可能是系統(tǒng)不允許訪問(wèn)的地址,等等。這個(gè)表達(dá)式必須要知道,因?yàn)楹芏喙镜拿嬖囶}中會(huì)考到。
6.10 指針的指針聽(tīng)起來(lái)有點(diǎn)繞,但是這種操作(思想)在C語(yǔ)言的編程中非常常見(jiàn)。書(shū)上有個(gè)例子:
int a = 12;
int *b = &a;
int **c = &b;
printf("結(jié)果為:%d\n", **c);
system("pause");
return 0;
打印輸出:
將a
的地址保存在了b
中,然后再將b
的地址保存在了c
中,這樣的操作看著很無(wú)聊,但是在后面的章節(jié)中就會(huì)發(fā)現(xiàn)其中的奧妙。
這部分內(nèi)容非常非常重要?。?!因?yàn)槿际浅R?jiàn)的指針表達(dá)式,這部分內(nèi)容對(duì)理解指針有相當(dāng)大的幫助。
特別提示:
所謂的作為左值,可理解為往該變量寫(xiě)入數(shù)值,
作為的作為右值,可理解為從該變量讀出數(shù)值。
首先我們對(duì)ch
和cp
作了以下聲明:
char ch = 'a';
char *cp = &cp;
總結(jié)了書(shū)上提到的所有情況,并作了相應(yīng)的解釋(好辛苦啊啊啊啊啊啊啊?。?br />其中的虛線框表示中間的計(jì)算結(jié)果,實(shí)線框表示得到的結(jié)果或要寫(xiě)入的地址空間。
書(shū)上給出了一個(gè)求字符串長(zhǎng)度的函數(shù),其實(shí)這個(gè)函數(shù)本身庫(kù)里面也有。
size_t strlen(char *string)
{int length = 0;
while (*string++ != '\0')
length++;
return length;
}
int main()
{int res;
char a[] = "qwer";
res = strlen(a);
if (res)
printf("The string length is %d",res);
else
printf("The string length is zero!");
system("pause");
return 0;
}
毫無(wú)疑問(wèn)地,打印輸出如下。
這個(gè)例子很好理解,直接將字符串作為實(shí)參傳入strlen()
函數(shù)中,函數(shù)內(nèi)部進(jìn)行判斷,如果沒(méi)到字符串結(jié)尾,就將長(zhǎng)度值加一,并向后移動(dòng)一個(gè)元素的距離,直到訪問(wèn)完每個(gè)元素,然后返回字符串長(zhǎng)度即可。
int main()
{char ch = 'a';
char *cp = &ch;
int a[] = {0,1,2,3,4,5,6};
int *p = a;
printf("%d", *p + 3);
system("pause");
return 0;
}
打印輸出:
int main()
{char ch = 'a';
char *cp = &ch;
int a[] = {0,1,2,3,4,5,6};
int *p1 = &a[0];
int *p2 = &a[3];
printf("%d", p2 - p1);
system("pause");
return 0;
}
打印輸出:
指針之間也可以進(jìn)行部分關(guān)系運(yùn)算,可以執(zhí)行的關(guān)系運(yùn)算有<
、<=
、>
、>=
。
舉個(gè)例子:
int main()
{char ch = 'a';
char *cp = &ch;
int a[] = {0,1,2,3,4,5,6};
int *vp = NULL;
for(vp = &a[0]; vp<= &a[6]; vp++)
printf("%d\n", *vp);
system("pause");
return 0;
}
打印輸出如下:
其實(shí)這里的指針充當(dāng)了迭代器的角色,將數(shù)組中的數(shù)值一個(gè)個(gè)地取出來(lái)。
---------------------------------------------------------------------------------END------------------------------------------------------------------------------
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧