欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C語言學(xué)習(xí)之指針知識總結(jié)

 更新時間:2022年07月08日 10:46:31   作者:南森森  
想突破C語言的學(xué)習(xí),對指針的掌握是非常重要的,本文為大家總結(jié)了C語言中指針的相關(guān)知識點(diǎn),文中的示例代碼講解詳細(xì),感興趣的可以學(xué)習(xí)一下

一、地址

內(nèi)存中的最小單元是字節(jié),一個字節(jié)對應(yīng)一個編號,這里的編號就是對應(yīng)字節(jié)的地址。換句話說,地址就是內(nèi)存單元的編號。

二、指針與指針變量

指針與指針變量是兩個不同的概念,指針是某個普通變量的地址,所以可以理解為,指針就是地址,地址就是指針。指針變量是一種變量,它的作用是存儲其它變量的地址。

#include <stdio.h>

int main()
{
	int * p; // int *是指針類型,p是對應(yīng)的變量,定義的指針變量p只能用來存儲int類型變量的地址
	int i = 3, j;
	
	p = &i; // 指針變量只能用來存儲對應(yīng)類型變量的地址,所以這里需要對變量i進(jìn)行取地址操作,即&i
	/*
		指針變量p保存了變量i的地址,這樣的話,我們稱:p指向i。通俗地說,通過p可以找到i
		雖然p指向i,但變量p和變量i不是同一個變量
		更準(zhǔn)確地說,修改其中任何一個變量p值不會影響變量i,反之同理
	*/
	
	printf("%d %d\n", *p, i);
	/*
		如果一個指針變量指向某個普通變量,則在指針變量前加上*后就完全等同于指向的普通變量
		換句話說,可以通過指針變量前加*來找到那個指向的普通變量
		以本程序?yàn)槔@里的*p就是以p的內(nèi)容為地址的變量
	*/

	j = *p; // *p就是i,所以相當(dāng)于將i的值賦給了j
	printf("i = %d, j = %d\n", i, j); // 輸出結(jié)果為i = 3, j = 3

	return 0;
}

三、指針的作用

指針是C語言的靈魂

  • 通過指針可以表示一些復(fù)雜的數(shù)據(jù)結(jié)構(gòu),例如,鏈表、樹、圖等
  • 通過指針可以提高傳輸效率
  • 利用指針可以在被調(diào)函數(shù)中修改主調(diào)函數(shù)中的多個值
  • 利用指針可以直接訪問硬件
  • 通過指針可以更方便地處理字符串
  • 指針是理解面向?qū)ο笳Z言中引用的基礎(chǔ)

四、初學(xué)指針時常見的錯誤

錯誤1:指針變量無明確指向

#include <stdio.h>

int main()
{
	int * p;
	int i = 5;
	
	*p = i; // 出錯行
	printf("%d\n", *p);

	return 0;
}

錯誤原因:由于p未初始化,所以p的內(nèi)容是垃圾值。*p是以p的內(nèi)容為地址的變量,即以一個垃圾值為地址的變量,該變量是未知的,所以當(dāng)把i的值賦值給p指向的未知變量時,有可能會篡改內(nèi)存中其它變量的值,而這樣的操作是不允許的。

錯誤2:賦值時變量類型不一致

#include <stdio.h>

int main()
{
	int * p;
	int * q;
	int i = 5;
	
	p = &i;
	
	*q = p; // 出錯行
	printf("%d\n", *q);

	return 0;
}

錯誤原因:由于p是指針類型變量,而*q是int類型變量,所以不能相互復(fù)制。

五、通過調(diào)用函數(shù)修改主調(diào)函數(shù)中的值

思考1:在下述程序中,f函數(shù)中的變量i與main函數(shù)中的變量i是不是同一個變量?

f函數(shù)中的變量i與main函數(shù)中的變量i都屬于局部變量,只在自己對應(yīng)的函數(shù)中起作用,所以,f函數(shù)中的變量i與main函數(shù)中的變量i不是同一個變量。

#include <stdio.h>

void f(int i)
{
	i = 99;
}

int main()
{
	int i = 66
	printf("%d\n", i);
	
	f(i);
	printf("%d\n", i);

	return 0;
}

思考2:在上述程序中,能否通過調(diào)用f函數(shù)修改main函數(shù)中變量i的值?

由于f函數(shù)中的變量i與main函數(shù)中的變量i不是同一個變量,所以把實(shí)參i傳遞給形參i只會改變f函數(shù)中變量i的值,當(dāng)f函數(shù)執(zhí)行完畢后,分配給形參i的空間會被釋放,故而無法改變main函數(shù)中變量i的值。換句話說,f函數(shù)中的變量i與main函數(shù)中的變量i本質(zhì)上沒有任何關(guān)系,所以不管怎么修改f函數(shù)中變量i的值都不會影響main函數(shù)中變量i的值。

那要如何才能通過其它函數(shù)來修改主調(diào)函數(shù)中的值?

此時,指針就派上用場了,如下述程序:

#include <stdio.h>

void f(int * p) // 通過接收地址來確定要修改的變量
{
	*p = 99; // *p就是以p變量的內(nèi)容為地址的變量,也就是要通過該函數(shù)修改的變量
}

int main()
{
	int i = 66
	printf("%d\n", i); // 輸出結(jié)果為66
	
	f(&i); // 由于函數(shù)f的形參是指針變量,故需將變量i的地址發(fā)送過去
	printf("%d\n", i); // 輸出結(jié)果為99

	return 0;
}

上述程序可以實(shí)現(xiàn)在被調(diào)函數(shù)中修改主調(diào)函數(shù)中變量的值是因?yàn)橥ㄟ^向被調(diào)函數(shù)傳遞了需要修改的變量的地址,從而確定并指向了需要修改的變量,但如果不傳入地址,就會導(dǎo)致主調(diào)函數(shù)中的變量無法與被調(diào)函數(shù)產(chǎn)生關(guān)聯(lián),從而無法實(shí)現(xiàn)目的。

活學(xué)活用:自定義一個swap函數(shù),用該函數(shù)互換main函數(shù)中的兩個變量的值

常見錯誤:只傳入數(shù)值,不傳入地址

void swap(int a, int b)
{
	int t;
	
	t = a;
	a = b;
	b = t;
}

int main()
{	
	int a = 3, b = 5;
	
	swap(a, b);
	
	printf("a = %d, b = %d\n", a, b);

	return 0;
}

出現(xiàn)上述錯誤的原因是,main函數(shù)中的變量a和b與swap函數(shù)中的形參a和b無關(guān),導(dǎo)致的結(jié)果是,主函數(shù)將3和5發(fā)送給形參a和b后,swap函數(shù)只是對3和5進(jìn)行了操作,而未能對main函數(shù)中的變量a和b進(jìn)行操作,所以無法互換main函數(shù)中的變量a和b的值。

正確實(shí)現(xiàn)方法:傳入地址,定位需要互換的變量

void swap(int * p, int * q)
{
	int t;
	
	t = *p;
	*p = *q;
	*q = t;
}

int main()
{	
	int a = 3, b = 5;
	
	swap(&a, &b);
	
	printf("a = %d, b = %d\n", a, b);

	return 0;
}

思考:如下方法是否可以實(shí)現(xiàn)互換功能?

#include <stdio.h>

void swap(int * p, int * q)
{
	int * t;

	t = p;
	p = q;
	q = t;
}

int main()
{	
	int a = 3, b = 5;
	
	swap(&a, &b);
	
	printf("a = %d, b = %d\n", a, b);

	return 0;
}

答案是不行的,上述程序?qū)⒆兞縜和b的地址發(fā)送給了指針變量p和q,此時,變量p和q中儲存的是變量a和b的地址,然而,swap函數(shù)中的操作是互換變量p和q的內(nèi)容,也就是說,當(dāng)swap函數(shù)執(zhí)行完畢后,變量p中儲存的是變量b的地址變量q中儲存的是變量a的地址,言下之意,只是將變量p和q的內(nèi)容互換了而已,并沒有對main函數(shù)中的變量a和b進(jìn)行操作,所以無法實(shí)現(xiàn)互換功能,此外,幾乎所有的編程語言都無法通過互換兩個變量的地址實(shí)現(xiàn)互換變量中的值。

六、指針與一維數(shù)組

一維數(shù)組的數(shù)組名

一維數(shù)組的數(shù)組名是一個指針常量,該常量是一維數(shù)組中第一個元素的地址。

#include <stdio.h>

int main()
{
	int a[5];
	int b[5];
	
	a = b; // 錯誤,因?yàn)閍和b都是常量,所以無法進(jìn)行賦值操作
	a = &a[2]; // 錯誤,因?yàn)閍是常量,無法對一個常量進(jìn)行賦值操作
	
	return 0;
}
#include <stdio.h>

int main()
{
	int a[5];

	printf("%#X", &a[0]);
	printf("%#X", a); // 與上一行輸出結(jié)果相同,因?yàn)橐痪S數(shù)組名就是數(shù)一維組中第一個元素的地址

	return 0;
}

引用一維數(shù)組中的元素

通過下標(biāo)引用:如a[i]表示第i+1個元素

通過指針引用:如*(a+i)表示第i+1個元素

#include <stdio.h>

int main()
{
	int a[5];
	int i;
	
	for (i = 0; i < 5; i++) // 向一維數(shù)組中讀入元素
		scanf("%d", &a[i]); // 通過下標(biāo)引用數(shù)組中的元素

	for (i = 0; i < 5; i++) // 輸出一維數(shù)組的內(nèi)容
		printf("%d ", *(a + i)); // 通過指針引用數(shù)組中的元素

	return 0;
}

指針變量的運(yùn)算

指針變量不能相加,相乘以及相除,只能進(jìn)行相減。如果兩個指針變量指向同一塊連續(xù)空間的不同存儲單元,則這兩個指針變量才可以進(jìn)行相減運(yùn)算。兩個指針變量相減得到的結(jié)果是兩個指針變量間相隔的元素個數(shù)。

#include <stdio.h>

int main()
{
	int a[5];
	int * p = &a[1];
	int * q = &a[4];

	printf("%d\n", q - p); // 輸出結(jié)果為3,證明相隔3個元素

	return 0;
}

七、使用函數(shù)操作一維數(shù)組

使用函數(shù)對一維數(shù)組進(jìn)行操作,首先要將數(shù)組名傳遞給函數(shù),因?yàn)橐痪S數(shù)組名是函數(shù)第一個元素的地址,傳遞數(shù)組名就相當(dāng)于傳遞起始位置,其次,普通數(shù)組不同于字符數(shù)組,它們沒有結(jié)束的標(biāo)志,所以還需要向函數(shù)傳遞數(shù)組長度以確定數(shù)組何時結(jié)束。故想要在另外一個函數(shù)中對一維數(shù)組進(jìn)行操作需要向該函數(shù)傳入兩個參數(shù),數(shù)組名和數(shù)組長度。

定義函數(shù)時的形參有兩種寫法,第一種是(int a[], int length),第二種是(int * a, int length)??梢詫懙诙N的原因是一位數(shù)組名本身就是指針常量,所以可以直接用指針變量來接收。

定義一個函數(shù),該函數(shù)的功能是對一維數(shù)組的內(nèi)容進(jìn)行輸出

#include <stdio.h>

// 自定義的print函數(shù),其功能是將一維數(shù)組輸出
void print(int a[], int length)
{
	int i;

	for (i = 0; i < length; i++)
		printf("%d ", a[i]); // 也可以寫成printf("%d ", *(a+i));
	
	printf("\n");
}

int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int b[6] = { -1, -2, -3, -4, -5, -6 };
	int c[100] = { 23, 88, 99, 44 };

	print(a, 5);
	print(b, 6);
	print(c, 100);

	return 0;
}

八、指針變量所占字節(jié)數(shù)

預(yù)備知識:sizeof運(yùn)算符的用法

sizeof(數(shù)據(jù)類型):其值為對應(yīng)數(shù)據(jù)類型所占的字節(jié)數(shù)

例如:sizeof(int)值為4;sizeof(double)值為8;sizeof(char)值為1

sizeof(變量):其值為對應(yīng)變量所占的字節(jié)數(shù)

#include <stdio.h>

int main()
{
	char c = 'A';
	int i = 99;
	double x = 66.66;

	char * p = &ch;
	int * q = &i;
	double r = &x;

	printf("%d %d %d\n", sizeof(c), sizeof(i), sizeof(x)); // 輸出結(jié)果為1 4 8
	printf("%d %d %d\n", sizeof(p), sizeof(q), sizeof(r)); // 輸出結(jié)果均為4

	return 0;
}

上述程序證明,盡管普通類型變量所占的空間大小不一致,但它們對應(yīng)的指針變量都占四個字節(jié)。

九、靜態(tài)數(shù)組的缺陷

數(shù)組長度必須事先指定,且只能是常整數(shù),不能是變量

數(shù)組的長度在長度不能在函數(shù)執(zhí)行過程中動態(tài)的增減,數(shù)組一旦定義,其長度就無法改變

程序員無法手動釋放靜態(tài)數(shù)組的內(nèi)存,數(shù)組一旦定義,操作系統(tǒng)為該數(shù)組分配的存儲空間就會一直存在,直到該數(shù)組所在的函數(shù)執(zhí)行完畢后,該數(shù)組的空間才會被操作系統(tǒng)釋放

在某個函數(shù)內(nèi)定義的靜態(tài)數(shù)組在該函數(shù)執(zhí)行期間可以被其它函數(shù)使用,但當(dāng)該函數(shù)執(zhí)行完畢后,該函數(shù)中定義的數(shù)組就無法在其它函數(shù)中使用,這是因?yàn)樵摵瘮?shù)執(zhí)行完畢后,靜態(tài)數(shù)組的內(nèi)存就被會被釋放

十、malloc函數(shù)

malloc這個詞是由memory(內(nèi)存)與allocate(分配)這兩個單詞合成的,顧名思義,malloc函數(shù)就是用來分配內(nèi)存的函數(shù)。

#include <stdio.h>
#include <malloc.h>

int main()
{
	int i; // 靜態(tài)分配了4個字節(jié)的存儲空間

	int* p = (int*)malloc(4);
	/*
		1.要使用malloc函數(shù),需添加malloc.h頭文件
		2.malloc函數(shù)只有一個形參,其類型為整型
		3.實(shí)參中的4表示請求操作系統(tǒng)為本程序分配4個字節(jié)的動態(tài)存儲空間
		4.malloc的返回值是分配給該系程序的第一個字節(jié)的地址
		5.由于第一個字節(jié)的地址不能確定具體的變量類型,所以需要強(qiáng)制類型轉(zhuǎn)換
		6.第7行代碼一共分配了8個字節(jié)的存儲空間,其中變量p占4個字節(jié),p指向的空間也占是4個字節(jié)
		7.變量p所占的內(nèi)存是靜態(tài)分配的,p所指向的內(nèi)存是動態(tài)分配的
	*/

	free(p); 
	/*
		free(p)表示釋放p所指向的內(nèi)存
		free函數(shù)只能用來釋放動態(tài)內(nèi)存,不能用來釋放靜態(tài)內(nèi)存,靜態(tài)內(nèi)存只能由操作系統(tǒng)自動釋放
		free(p)只是釋放了p對應(yīng)的內(nèi)存空間,但p的內(nèi)容依舊存在
	*/

	return 0;
}

十一、動態(tài)數(shù)組的構(gòu)造

#include <stdio.h>
#include <malloc.h>

int main()
{
	int a[10]; // 靜態(tài)構(gòu)造一維數(shù)組
	int length;
	int* pArray;
	int i;
	
	scanf("%d", &length);

	pArray = (int*)malloc(sizeof(int) * length);
	/*
		動態(tài)構(gòu)造一維數(shù)組
		該動態(tài)數(shù)組數(shù)組名為pArray,數(shù)組長度為length,數(shù)組中每個元素都是int類型
	*/

	// 對該動態(tài)一維數(shù)組手動賦值
	for (i = 0; i < length; i++)
		scanf("%d", &pArray[i]);

	// 輸出該動態(tài)一維數(shù)組的內(nèi)容
	for (i = 0; i < length; i++)
		printf("%d ", *(pArray + i));
	
	printf("\n");

	free(pArray); // 釋放該動態(tài)數(shù)組

	return 0;
}

十二、靜態(tài)內(nèi)存與動態(tài)內(nèi)存的對比

靜態(tài)內(nèi)存是由操作系統(tǒng)自動分配,自動釋放的。靜態(tài)內(nèi)存是在棧中分配的;動態(tài)內(nèi)存是由程序員手動分配,手動釋放,但如果只分配不釋放就會導(dǎo)致內(nèi)存泄露,也就是內(nèi)存越用越少。動態(tài)內(nèi)存是在堆中分配的。

十三、多級指針

#include <stdio.h>

int main()
{
	int i = 10;
	int * p = &i;
	int ** q = &p;
	int *** r = &q;
	
	r = &p; // 錯誤,因?yàn)閞是int *** 類型,它只能用來存儲int ** 類型變量的地址
	
	printf("i = %d\n", ***r);

	return 0;
}

表解上述程序:

變量名變量地址變量內(nèi)容
i1000H10
p2000H1000H
q3000H2000H
r4000H3000h
變量名對應(yīng)變量
*rq
**rp
***ri
#include <stdio.h>

void g(int ** q) // 由于p的類型是int *,所以q的類型必須是int **,因?yàn)閝要用來存放p的地址
{
	
}

void f()
{
	int i;
	int * p;
	
	p = &i;
	
	g(&p); // 要通過g函數(shù)修改p的內(nèi)容,則必須發(fā)送p變量的地址
}

int main()
{
	f();

	return 0;
}

十四、跨函數(shù)使用內(nèi)存

由于靜態(tài)內(nèi)存是在棧中分配的,而函數(shù)執(zhí)行完畢后,棧中的靜態(tài)內(nèi)存就會全部出棧,而動態(tài)內(nèi)存是在堆中分配的,當(dāng)函數(shù)執(zhí)行完畢后,堆中分配的內(nèi)存并不會像棧中分配的內(nèi)存一樣直接被釋放掉,所以

靜態(tài)內(nèi)存是不能跨函數(shù)使用的,而動態(tài)內(nèi)存是可以的。

思考:下述程序是否有語法錯誤?是否有邏輯錯誤?

#include <stdio.h>

void f(int** q)
{
	int i = 5;

	*q = &i; // 由于q儲存了p的地址,所以*q就是p,這行代碼實(shí)質(zhì)是將i的地址賦給了p
}

int main()
{
	int* p;

	f(&p);

	printf("i = %d\n", *p); // 由于p儲存了i的地址,所以*p就是i

	return 0;
}

上述程序沒有語法錯誤,但是有邏輯上的錯誤,這是因?yàn)?,?dāng)f函數(shù)執(zhí)行完畢后,f函數(shù)中所有的靜態(tài)變量的內(nèi)存都會被釋放掉,所以當(dāng)執(zhí)行到printf("i = %d\n", *p);時,p所指向的變量空間的訪問權(quán)限已經(jīng)返還給了操作系統(tǒng),這樣就會導(dǎo)致*p訪問了不屬于該程序的空間。這個程序說明了在一個函數(shù)內(nèi)部定義的靜態(tài)變量在該函數(shù)中執(zhí)行完畢后就不再可以垮函數(shù)使用。

思考:對比上一程序,下述程序是否有語法錯誤?是否有邏輯錯誤?

#include <stdio.h>

void f(int** q)
{
	*q = (int*)malloc(sizeof(int));
	
	**q = 5;
}

int main()
{
	int* p;

	f(&p);

	printf("%d\n", *p);

	return 0;
}

上述程序是完全沒有語法錯誤的,因?yàn)楫?dāng)f函數(shù)執(zhí)行完畢后,其中分配的動態(tài)內(nèi)存不會自動釋放,所以在main函數(shù)中依然可以使用這段內(nèi)存。這個程序體現(xiàn)出,在一個函數(shù)中分配的動態(tài)存儲空間在該函數(shù)執(zhí)行完之后,仍然可以在另外一個函數(shù)中使用。

趁熱打鐵:下列四個程序中,哪個程序能夠通過調(diào)用fun函數(shù)使main函數(shù)中的指針變量p指向一個合法的整型單元?

#include <stdio.h>

void fun(int * p)
{
	int s;
	p = &s;
}

int main()
{
	int * p;

	fun(p);

	return 0;
}
#include <stdio.h>

void fun(int ** p)
{
	int s;
	*p = &s;
}

int main()
{
	int * p;

	fun(&p);

	return 0;
}
#include <stdio.h>
#include <malloc.h>

void fun(int * p)
{
	p = (int *)malloc(sizeof(int));
}

int main()
{
	int * p;

	fun(p);

	return 0;
}
#include <stdio.h>
#include <malloc.h>

void fun(int ** p)
{
	*p = (int *)malloc(4);
}

int main()
{
	int * p;

	fun(&p);

	return 0;
}

以上就是C語言學(xué)習(xí)之指針知識總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于C語言指針的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論