成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

C語(yǔ)言指針講解

boredream / 2248人閱讀

摘要:在語(yǔ)言中,允許用指針變量來(lái)存放指針,因此,一個(gè)指針變量的值就是某個(gè)內(nèi)存單元的地址或稱為某內(nèi)存單元的指針。指針詳解一基礎(chǔ)知識(shí)在語(yǔ)言中,定義變量時(shí),如果在變量名前加上一個(gè),那么這個(gè)變量就變成了對(duì)應(yīng)變量類型的指針變量。


前言

指針,是C語(yǔ)言中的一個(gè)重要概念及其特點(diǎn),也是掌握C語(yǔ)言比較困難的部分。作為一個(gè)C語(yǔ)言初學(xué)者,我對(duì)指針也有了一定的了解,正好這幾天在做一個(gè)C語(yǔ)言指針的知識(shí)點(diǎn)匯總,于是就有了這篇文章,向大家分享一些我的見解,與大家一起共勉。

為什么要學(xué)習(xí)指針

  1. 如果你想通過(guò)函數(shù)改變一個(gè)變量的值,就得用指針而不能用值傳遞,很多時(shí)候不同的函數(shù)存在很多不同的變量,當(dāng)程序的數(shù)據(jù)量十分龐大的時(shí)候,我們就需要用指針來(lái)作為形參,通過(guò)傳遞地址,達(dá)到傳遞變量的目的。
  2. 指針變量是用來(lái)存放內(nèi)存地址的變量,在同一CPU構(gòu)架下,不同類型的指針變量所占用的存儲(chǔ)單元長(zhǎng)度是相同的,而存放數(shù)據(jù)的變量因數(shù)據(jù)的類型不同,所占用的存儲(chǔ)空間長(zhǎng)度也不同。有了指針以后,不僅可以對(duì)數(shù)據(jù)本身,也可以對(duì)存儲(chǔ)數(shù)據(jù)的變量地址進(jìn)行操作。
  3. 同樣,指針也使得一些復(fù)雜的內(nèi)容變得簡(jiǎn)單,例如:鏈表等等;也有一些操作它必須使用指針,例如:申請(qǐng)內(nèi)存等等。

什么是指針

在計(jì)算機(jī)中,每一個(gè)數(shù)據(jù)都是存放在儲(chǔ)存器中的,而不同的數(shù)據(jù)類型所占的內(nèi)存空間不同(例如:int類型占用4個(gè)字節(jié),double類型占用8個(gè)字節(jié)等等),內(nèi)存空間又是以字節(jié)為單位的,每一個(gè)字節(jié)又對(duì)應(yīng)了一個(gè)編號(hào),這個(gè)編號(hào)我們稱為這一個(gè)內(nèi)存單元的地址。

而系統(tǒng)在內(nèi)存中又為變量分配了存儲(chǔ)空間的首個(gè)字節(jié)單元的地址即變量的地址。為了方便用戶對(duì)存儲(chǔ)空間進(jìn)行正確的訪問(wèn),指針便應(yīng)運(yùn)而生了。

指針相對(duì)于一個(gè)內(nèi)存單元來(lái)說(shuō),指的是單元的地址,該單元的內(nèi)容里面存放的是數(shù)據(jù)。在 C 語(yǔ)言中,允許用指針變量來(lái)存放指針,因此,一個(gè)指針變量的值就是某個(gè)內(nèi)存單元的地址或稱為某內(nèi)存單元的指針。

指針變量是存放一個(gè)內(nèi)存地址的變量,不同于其他類型變量,它是專門用來(lái)存放內(nèi)存地址的,也稱為地址變量。定義指針變量的一般形式為:類型說(shuō)明符*變量名。

指針詳解

一、基礎(chǔ)知識(shí)

在C語(yǔ)言中,定義變量時(shí),如果在變量名前加上一個(gè) “ * ”,那么這個(gè)變量就變成了對(duì)應(yīng)變量類型的指針變量。

1、取地址

對(duì)于一個(gè)指針變量,我們需要讓它來(lái)保存其他變量的地址的時(shí)候,就需要用 到 &運(yùn)算符。
下面舉個(gè)例子:

例1

#includeint main(){	int num = 10;//在內(nèi)存中開辟一塊空間	int* p = #//這里我們對(duì)變量num取地址,使用了&運(yùn)算符				  //將num的地址存放在p變量中,p就是一個(gè)指針變量。	return 0;}

&num就取得了num的地址,指針p指向的num的地址,就形成了一個(gè)簡(jiǎn)單的指針變量。

但是對(duì)于某些特殊情況,我們可以不需要用&運(yùn)算符,例如:數(shù)組,函數(shù)等等,我們后邊會(huì)講到。

2、解引用

上面我們了解了如何取得一個(gè)數(shù)據(jù)的地址,那么接下來(lái)我們可以嘗試運(yùn)用指針來(lái)解地址,從而得到這個(gè)變量的內(nèi)存數(shù)據(jù)。

在指針前加一個(gè)“ * "即解引用地址,也就是從指針指向的內(nèi)存中,取出這段內(nèi)存地址對(duì)應(yīng)的數(shù)據(jù)。

輸出例1中的指針p:

例2

#includeint main(){	int num = 10;	int* p = #	printf("%d", *p);	return 0;}

運(yùn)行結(jié)果為:10

3、指針變量

指針就是一個(gè)變量,用來(lái)存放地址的變量。(存放在指針中的值,均會(huì)被看作地址)

一個(gè)小的單元會(huì)有多大:(一個(gè)字節(jié))
那么地址是如何編寫的呢?

目前經(jīng)過(guò)仔細(xì)的技術(shù)及思考后,最合適的結(jié)論為:一個(gè)字節(jié)對(duì)應(yīng)了一個(gè)地址。

對(duì)于32位機(jī)器,可以看作有32根地址線,每一根地址線在尋址的時(shí)候都會(huì)產(chǎn)生一個(gè)電信號(hào)(正電1/負(fù)電0),所以它的地址從000……000(32個(gè)0)到111……111(32個(gè)1)共有2的32次方個(gè)地址。

在32位機(jī)器上,地址是32個(gè)0或1組成的二進(jìn)制序列,一個(gè)地址需要用4個(gè)字節(jié)的空間來(lái)存儲(chǔ),所以一個(gè)指針變量的大小就是4個(gè)字節(jié)。

同樣,可以類比推理得到:64位機(jī)器有2的64次方個(gè)地址,一個(gè)指針變量的大小位8個(gè)字節(jié)。

我們來(lái)做一個(gè)練習(xí):

例3

#includeint main(){	printf("%d/n", sizeof(char*));	printf("%d/n", sizeof(short*));	printf("%d/n", sizeof(double*));	printf("%d/n", sizeof(int*));	return 0;}

運(yùn)行結(jié)果為:
4
4
4
4

總結(jié)
(1)指針是一個(gè)變量,它存放的是地址。
(2)指針在32位機(jī)器中占4個(gè)字節(jié),在64位機(jī)器中占8個(gè)字節(jié),與類型無(wú)關(guān)。

二、指針與指針類型

1、指針類型

我們前邊提到,指針的定義方式是:type + * :
char* 類型的指針是為了存放char類型變量的地址;
int* 類型的指針是為了存放int類型變量的地址;
short* 類型的指針是為了存放short類型變量的地址;
double* 類型的指針是為了存放double類型變量的地址。

指針的類型決定了指針向前或者向后的空間有多大。

指針類型決定了指針進(jìn)行解引用操作的時(shí)候,能夠訪問(wèn)空間的大?。?br /> (1) int* p:* p能夠訪問(wèn)4個(gè)字節(jié)。
(2)char* p:* p能夠訪問(wèn)1個(gè)字節(jié)。
(3)double* p: *p能夠訪問(wèn)8個(gè)字節(jié)。

指針類型的意義

先來(lái)看一個(gè)例子:

例4

#includeint main(){	int a = 10;	char* p1 = (char*)&a;	int* p2 = &a;	printf("%p/n", &a);	printf("%p/n", p1);	printf("%p/n", p1+1);	printf("%p/n", p2);	printf("%p/n", p2+1);	return 0;}

輸出的結(jié)果為:
003CFC80
003CFC80
003CFC81
003CFC80
003CFC84

我們可以看到不同類型的指針p1和p2都指向了變量a的地址,但是p1+1指向的是003CFC81,而p2+1指向了003CFC84。一個(gè)地址向后移動(dòng)了1,一個(gè)地址向后移動(dòng)了4。

這是因?yàn)椋褐羔樀念愋蜎Q定了指針向前或者向后移動(dòng)一步有多大的距離。

2、野指針

(1)概念:
野指針就是指針指向的位置是不可知的(隨機(jī)的、不正確的、沒(méi)有明確限制的)。

(2)成因:
①指針未初始化
例5

#includeint main(){	int* p;//局部變量指針未初始化,默認(rèn)未隨機(jī)值。	*p = 10;	return 0;}

②指針越界訪問(wèn)
例6

#includeint main(){	int arr[10] = { 0 };	int* p = arr;	int i = 0;	for (i = 0; i <= 11; i++)	{		//當(dāng)指針指向的范圍超出數(shù)組arr的范圍時(shí),p就是野指針		*(p++) = i;	}	return 0;}

③指針指向的空間釋放
例7

#includeint* func(){	int a = 10;	return &a;}int main(){	int* p = func();	*p = 20;	return 0;}

這里的func函數(shù)雖然確實(shí)返回了地址,而p也確實(shí)接受到了返回的地址,但是當(dāng)返回的時(shí)候,已經(jīng)來(lái)不及保存了,因?yàn)閒unc函數(shù)一結(jié)束,函數(shù)申請(qǐng)的內(nèi)存等等就返回給操作系統(tǒng)了,已經(jīng)無(wú)法再通過(guò)指針p去訪問(wèn)a的地址了,后邊再用*p=20去訪問(wèn)的是已經(jīng)被釋放的a的地址。

(3)如何避免野指針
①指針初始化
例8

#includeint main(){	int a = 10;	int* p1 = &a;	int* p2 = NULL;//NULL:用來(lái)初始化指針的,給指針賦值。	return 0;}

②小心指針越界
③指針指向空間釋放即設(shè)置NULL
④指針使用之前檢查有效性
例9

#includeint main(){	int* p = NULL;	int a = 10;	p = &a;	if (p != NULL)	{		*p = 20;	}	return 0;}

3、指針運(yùn)算

(1)指針±整數(shù)

例10:輸出數(shù)組的每一個(gè)元素

#includeint main(){	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };	int i = 0;	int sz = sizeof(arr) / sizeof(arr[0]);	int* p = arr;	for (i = 0; i < sz; i++)	{		printf("%d ", *(p + i));	}	return 0;}

運(yùn)行結(jié)果為:1 2 3 4 5 6 7 8 9 10

(2)指針-指針
指針-指針得到的是中間的元素個(gè)數(shù)。(注意盡量大-?。?/p>

易錯(cuò)點(diǎn)提示:兩個(gè)指針不能進(jìn)行加法運(yùn)算,這是非法的?。?!兩個(gè)指針在進(jìn)行減法運(yùn)算時(shí),類型要相同,否則結(jié)果不可預(yù)知!??!

例11

#includeint main(){	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };	int* p1 = &arr[0];	int* p2 = &arr[9];	printf("%d/n", *p2 - *p1);	return 0;}

運(yùn)行結(jié)果為:9

我們?cè)谥皩W(xué)過(guò)了用遞歸和迭代兩種方法來(lái)實(shí)現(xiàn)strlen函數(shù),那么今天我們就學(xué)會(huì)了第三種方法——指針相減法:

例12:自己的strlen函數(shù)(指針相減法)

#includeint my_strlen(char* str){	char* ret = str;	while (*str != "/0")	{		str++;	}	return str - ret;}int main(){	char arr[] = "abcdef";	int len = my_strlen(arr);	printf("%d", len);	return 0;}

(3)指針的關(guān)系運(yùn)算——"<" “>” “<=” “>=” “==” “!=”
指針進(jìn)行關(guān)系運(yùn)算的前提是它們都指向同一個(gè)數(shù)組中的元素。

易錯(cuò)點(diǎn)提示:指針的關(guān)系運(yùn)算是相同類型的指針之間的關(guān)系運(yùn)算,不同類型的指針之間的關(guān)系運(yùn)算沒(méi)有意義,指針與非0整數(shù)的關(guān)系運(yùn)算也沒(méi)有意義。
例13

#includeint main(){	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };	int* p = &arr[0];	int* q = &arr[9];	p < q; //當(dāng)p所指的元素在q所指的元素之前時(shí),表達(dá)式的值為1;反之為0。	p > q; //當(dāng)p所指的元素在q所指的元素之后時(shí),表達(dá)式的值為1;反之為0。	p == q; //當(dāng)p和q指向同一元素時(shí),表達(dá)式的值為1;反之為0。	p != q; //當(dāng)p和q不指向同一元素時(shí),表達(dá)式的值為1;反之為0。	printf("%d %d %d %d", p < q, p > q, p == q, p != q);	return 0;}

運(yùn)行結(jié)果為:1 0 0 1

4、二級(jí)指針

例14

#includeint main(){	int a = 10;	int* p1 = &a;	int** p2 = &p1;//p2就是二級(jí)指針。	printf("%d/n", **p2);		**p2 = 20;//二級(jí)指針p2改變,其指向的a隨之改變。	printf("%d/n", **p2);	printf("%d/n", a);	return 0;}

運(yùn)行結(jié)果為:
10
20
20

三、指針的分類

1、字符指針

指向字符型數(shù)據(jù)的指針變量。每個(gè)字符串在內(nèi)存中都占用一段連續(xù)的存儲(chǔ)空間,并有唯一確定的首地址。即將字符串的首地址賦值給字符指針,可讓字符指針指向一個(gè)字符串。

下面舉一個(gè)例子:
例15

#includeint main(){    char arr[] = "abcdef";    char* p1 = arr;    printf("%s/n", arr);    printf("%s/n", p1);    //這里的字符指針p1指向的是arr[]中首位的地址,所以在打印時(shí)不用解引用,它表示的是從arr[]的首位開始打印至"/0"停止。    char* p2 = "abcdef";//"abcdef"是一個(gè)常量字符串。    printf("%c/n", *p);//說(shuō)明p存的只是首元素a的地址。    printf("%s/n", p);//同上    return 0;}

運(yùn)行結(jié)果為:
abcdef
abcdef
a
abcdef

易錯(cuò)點(diǎn)提示:在常量字符串前加一個(gè)“const”。

例16:常量字符串

#includeint main(){    const char* p = "abcdef";    //*p = "w";    printf("%s/n", p);    return 0;}

運(yùn)行結(jié)果為:abcdef

有了const以后,我們就不能對(duì)指針p進(jìn)行賦值修改了,這樣可以使其更加安全的儲(chǔ)存數(shù)據(jù)。

例17:經(jīng)典易錯(cuò)題

#includeint main(){    char arr1[] = "abcdef";    char arr2[] = "abcdef";    char* p1 = "abcdef";    char* p2 = "abcdef";    if (arr1 == arr2)        printf("1/n");    else        printf("0/n");    if (p1 == p2)        printf("1/n");    else        printf("0/n");    return 0;}

運(yùn)行結(jié)果為:
0
1
這是因?yàn)閍rr1和arr2是分別開辟的內(nèi)存,雖然元素相同,但所處內(nèi)存空間不同,所以arr1 != arr2;但是字符指針p1和p2都指向了字符串"abcdef",指向相同,所以p1 == p2。

2、指針和數(shù)組

(1)數(shù)組名

數(shù)組名表示的是數(shù)組首元素的地址。

例18

#includeint main(){	int arr[10] = { 0 };	printf("%p/n", arr);//arr:首元素的地址。	printf("%p/n", arr + 1);	printf("%p/n", &arr[0]);//&arr[i]:數(shù)組中對(duì)應(yīng)元素的地址。	printf("%p/n", &arr[0] + 1);	printf("%p/n", &arr);//&arr:整個(gè)數(shù)組的地址。	printf("%p/n", &arr + 1);	//1. &arr — &數(shù)組名:數(shù)組名不是首元素地址,數(shù)組名表示整個(gè)數(shù)組,&數(shù)組名-取出的是整個(gè)數(shù)組的地址。	//2.sizeof(arr) — sizeof(數(shù)組名):數(shù)組名表示的整個(gè)數(shù)組,sizeof(數(shù)組名)計(jì)算的是整個(gè)數(shù)組的大小。	return 0;}

運(yùn)行結(jié)果為:
00B3F828
00B3F82C
00B3F828
00B3F82C
00B3F828
00B3F850

也就是說(shuō),對(duì)于上述代碼,arr與&arr[0]通過(guò)加1運(yùn)算后發(fā)現(xiàn)地址加了4,而&arr通過(guò)加1運(yùn)算后發(fā)現(xiàn)地址加了40,從而說(shuō)明了&arr表示的是整個(gè)數(shù)組的地址。

(2)指針數(shù)組

概念:數(shù)組元素全為指針變量的數(shù)組稱為指針數(shù)組。
也就是指針數(shù)組是一個(gè)數(shù)組,用來(lái)存放指針的數(shù)組。
如:*p[10]是一個(gè)指針數(shù)組。

舉一個(gè)例子:
例19

#includeint main(){    int arr1[] = { 1,2,3,4,5 };    int arr2[] = { 2,3,4,5,6 };    int arr3[] = { 3,4,5,6,7 };    int* arr[] = { arr1,arr2,arr3 };    int i = 0;    //分別遍歷出arr1,arr2,arr3。    for (i = 0; i < 3; i++)    {        int j = 0;        //分別遍歷出arr1,arr2,arr3中的每個(gè)元素。        for (j = 0; j < 5; j++)        {            printf("%d ", *(arr[i] + j));        }        printf("/n");    }    return 0;}

運(yùn)行結(jié)果為:
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7

(3)數(shù)組指針

概念:指的是數(shù)組名的指針,即數(shù)組首元素地址的指針。
也就是數(shù)組指針是一個(gè)指針,指向數(shù)組的指針。
如:(*p)[10]是一個(gè)數(shù)組指針。

舉一個(gè)例子:
例20

#includevoid print1(int arr[3][5], int x, int y)//二維數(shù)組打印。{    int i, j = 0;    for (i = 0; i < x; i++)    {        for (j = 0; j < y; j++)        {            printf("%d ", arr[i][j]);        }        printf("/n");    }}void print2(int (*p)[5], int x, int y)//數(shù)組指針打印{    int i = 0;    for (i = 0; i < x; i++)    {        int j = 0;        for (j = 0; j < y; j++)        {            printf("%d ", *(*(p+i)+j));        }        printf("/n");    }}int main(){    int arr[3]
                 
               
              

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/123067.html

相關(guān)文章

  • C語(yǔ)言】超詳講解??指針是個(gè)什么針?(一次性搞定指針問(wèn)題)

    目錄 前言 一、 什么是指針? 引例 計(jì)算機(jī)是怎么對(duì)內(nèi)存單元編號(hào)的呢? 內(nèi)存空間的地址如何得到 想存地址怎么辦? ? 本質(zhì)目的不是為了存地址 ?二、指針和指針類型 為什么有不同類型的指針 1.指針的解引用 2.指針+-整數(shù) 三、野指針 造成野指針的原因 1.未主動(dòng)初始化指針 ?2.指針越界訪問(wèn) 3.指針指向的空間釋放 規(guī)避野指針 四、指針運(yùn)算 1.指針+-整數(shù) ?2.指針-指針 ?3.指針的關(guān)系運(yùn)...

    tigerZH 評(píng)論0 收藏0
  • C語(yǔ)言知識(shí)精講②】函數(shù)棧幀的創(chuàng)建和銷毀(全程圖解)

    摘要:這里分塊講解六函數(shù)棧幀的銷毀過(guò)程一解析的作用是將棧頂?shù)臄?shù)據(jù)彈出,彈出數(shù)據(jù)儲(chǔ)存到相應(yīng)寄存器中。 ?前言? 讀完這篇博客,你可以明白什么? ①局部變量到底是怎么在棧上創(chuàng)建的? ②為什么局部變量不初始化為隨機(jī)值? ③函數(shù)是怎么傳參的?傳參的先后順序是什么? ④形參和實(shí)參是什么關(guān)系? ⑤函數(shù)調(diào)用是怎...

    davidac 評(píng)論0 收藏0
  • ??整理2萬(wàn)字帶你走進(jìn)C語(yǔ)言(詳細(xì)講解+代碼演示+圖解)??(強(qiáng)烈建議收藏?。。。?/b>

    目錄 一、什么是C語(yǔ)言? 二、第一個(gè)C語(yǔ)言程序 代碼 程序分析 ?程序運(yùn)行 一個(gè)工程中出現(xiàn)兩個(gè)及以上的main函數(shù) 代碼 運(yùn)行結(jié)果 分析 三、數(shù)據(jù)類型 數(shù)據(jù)各種類型 為什么會(huì)有這么多的數(shù)據(jù)類型? 計(jì)算機(jī)單位 ?各個(gè)數(shù)據(jù)類型的大小 ?注意事項(xiàng) 數(shù)據(jù)類型的使用 四、變量和常量 變量的分類 變量的使用 變量的作用域和生命周期 ?常量 五、字符串+轉(zhuǎn)義字符+注釋 字符串 ?轉(zhuǎn)義字符 注釋 六、選擇語(yǔ)句 ?...

    邱勇 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

boredream

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<