字符指針變量

在指針的類型中我們知道有一種指針類型為字符指針char* ;
一般使用:

char arr[] = "abcdef";
char* p = arr;

char* pa = "abcdef";//常量字符串,無法修改

char* pa = “abcdef” 這樣編寫的時候,字符的內容是不能修改的,這也變成了常變量,如果強行修改,程序會奔潰(寫入衝突),所有一般使用的時候,會用:

const char* pa ="abcdef";//這樣編寫,如果不小心寫成要修改內部內容時,編譯器會報錯的(提醒)

#include <stdio.h>

int main()
{
	char arr[] = "abcdef";
	printf("%c\n", arr[2]);

	printf("%c\n", "abcdef"[2]);

	const char* pstr = arr;
	printf("%s\n", pstr);

	return 0;
}//輸出結果:
//c
//c
//abcdef

代碼const char* pstr = arr; 特別容易讓人以為是把字符串hello bit 放到字符指針pstr 裏了,但是本質是把字符串arr. 首字符的地址放到了pstr中。

數組指針變量是什麼?

之前我們學習了指針數組,指針數組是一種數組,數組中存放的是地址(指針)
數組指針變量是指針變量?還是數組?
答案是:指針變量

我們已經熟悉:
• 整形指針變量: int * pint; 存放的是整形變量的地址,能夠指向整形數據的指針。
• 浮點型指針變量: float * pf; 存放浮點型變量的地址,能夠指向浮點型數據的指針。
數組指針變量應該是:存放的應該是數組的地址,能夠指向數組的指針變量。

數組指針變量:

int arr[10] = 0;
int (*parr)[10] = &arr;
//數組的地址,指向的是數組  --指向的是所有的元素,並不僅僅是第一個元素

解釋:parr先和*結合,説明parr是一個指針變量,然後指針指向的是一個大小為10個整型的數組。所以p是一個指針,指向一個數組,叫 數組指針。

這裏要注意:[]的優先級要高於號的,所以必須加上()來保證p先和結合。

用數組指針訪問數組:

#include <stdio.h>

int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int (*p)[10] = &arr;

	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);

	for (i = 0;i < sz;i++)
	{
		printf("%d ", (*p)[i]);//用數組指針訪問數組
	}

	//p     ==      &arr
	//*p    ==      *&arr  -> arr
	//*P    ==      arr  
	//於此類似
	//p     ==      arr
	//*p    ==      *arr ->   arr[0]

	return 0;
}

類比 arr 訪問數組,但一般不這麼用

二維數組傳參的本質

有了數組指針的理解,我們就能夠講一下二維數組傳參的本質了。
過去我們有一個二維數組的需要傳參給一個函數的時候,我們是這樣寫的:

#include <stdio.h>

void Print(int arr[3][5],int r,int c)
{
	for (int i = 0;i < r;i++)
	{
		for (int j = 0;j < c;j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}

int main()
{
	int arr[3][5] = { 1,2,3,4,5,0,1,2,3,4,5,6,7,8,9 };
	
	Print(arr, 3, 5);

	return 0;
}//輸出結果:
//1 2 3 4 5
//0 1 2 3 4
//5 6 7 8 9

這裏實參是二維數組,形參也寫成二維數組的形式,那還有什麼其他的寫法嗎?

首先我們再次理解一下二維數組,二維數組其實可以看做是每個元素是一維數組的數組,也就是二維數組的每個元素是一個一維數組。那麼二維數組的首元素就是第一行,是個一維數組。

如下圖:

c語言中的指針詳解_#數據結構


這是上篇博客中的圖片,這裏可以類比一下:

二維數組的每一個元素其實就是一維數組

所以二維數組的首元素的地址其實就是一整個一維數組的地址

所以,根據數組名是數組首元素的地址這個規則,二維數組的數組名錶示的就是第一行的地址,是一維數組的地址。根據上面的例子,第一行的一維數組的類型就是int [5] ,所以第一行的地址的類型就是數組指針類型int(*)[5] 。那就意味着二維數組傳參本質上也是傳遞了地址,傳遞的是第一行這個一維數組的地址,那麼形參也是可以寫成指針形式的。如下:

#include <stdio.h>

void Print(int (*p)[5], int r, int c)
{
	for (int i = 0;i < r;i++)
	{
		for (int j = 0;j < c;j++)
		{
			printf("%d ", p[i][j]);
		}
		printf("\n");
	}
}

int main()
{
	int arr[3][5] = { 1,2,3,4,5,0,1,2,3,4,5,6,7,8,9 };

	Print(arr, 3, 5);

	return 0;
}//輸出結果:
//1 2 3 4 5
//0 1 2 3 4
//5 6 7 8 9

形參變化: int arr[3][5] -> (*p)[5]
使用變化: arr[i][j] -> p[i][j]

怎麼理解?
把 arr[] 看成一個數組指針 p
p[i][j] 怎麼理解?p先在二維數組上偏移 i -> p[i] 這樣就得到一個一維數組的數組指針
然後一維數組的數組指針怎麼使用?看上面可知 p[i] 在一維數組的內部上偏移 j 即 p[i][j]。
p[i][j]是二維數組中,int個單位的指針,對此解引用即可。。。

p -> p[i] -> (p+i) -> *(p+i) -> *(p+i)+j -> *(*(p+i)+j)

用這個理解怎麼重新寫這個上面代碼呢?

#include <stdio.h>

void Print(int (*p)[5], int r, int c)
{
	for (int i = 0;i < r;i++)
	{
		for (int j = 0;j < c;j++)
		{
			printf("%d ", *(*(p + i) + j));//p[i][j]  完全等價
		}
		printf("\n");
	}
}

int main()
{
	int arr[3][5] = { 1,2,3,4,5,0,1,2,3,4,5,6,7,8,9 };

	Print(arr, 3, 5);

	return 0;
}//輸出結果:
//1 2 3 4 5
//0 1 2 3 4
//5 6 7 8 9

二維數組傳參的本質就是傳遞一維數組的數組指針,(【5】是提示一維數組有幾個元素)。

函數指針變量

函數指針變量的創建

什麼是函數指針變量呢?
函數指針變量應該是用來存放函數地址的,未來通過地址能夠調用函數的。
那麼函數是否有地址呢?
我們做個測試:

#include <stdio.h>

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	printf("%p\n", &Add);
	printf("%p\n", Add);


	return 0;
}//輸出結果:
//00CD13B6
//00CD13B6

確實打印出來了地址,所以函數是有地址的,函數名就是函數的地址,當然也可以通過&函數名的方式獲得函數的地址。
&函數名 和 函數名 都是函數的地址。
如果我們要將函數的地址存放起來,就得創建函數指針變量咯,函數指針變量的寫法其實和數組指針非常類似。如下:

#include <stdio.h>

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	//printf("%p\n", &Add);
	printf("%p\n", Add);

	int (*pf)(int, int) = &Add;//pf 就是函數指針變量

	printf("%p\n", pf);

	return 0;
}//輸出結果:
//002913B6
//002913B6

pf)為一個整體,不然變成int * ,就變成普通 int類型 的指針變量了。(int,int)是表示該函數的參數。他是什麼類型?去掉名字的就是該指針的類型 -> int ()(int,int)。

*是優先跟着後面的變量名,提示後面的變量名是一個指針

int *p,q;//p 是int*類型,q是int類型。

函數指針變量的使用

函數的指針變量怎麼使用呢?直接看代碼:

#include <stdio.h>

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int (*pf)(int, int) = &Add;
	int ret = (*pf)(2,3);//函數指針的使用  -> * + 指針變量名 + 參數

	printf("%d\n", ret);

	//正常調用
	ret = Add(23, 27);
	printf("%d\n", ret);

	//剛剛提到,函數名 就是函數地址,那麼能否不用 解引用 直接調用?
	ret = pf(29, 46);
	printf("%d\n", ret);

	//發現 * 是擺設,那麼能否多寫?原則上不行,不方便閲讀,實際上可以。
	ret = (*****************************************pf)(12, 7);
	printf("%d\n", ret);

	
	return 0;
}//輸出結果:
//5
//50
//75
//19

c語言中的指針詳解_數組_02

1.有趣的代碼

c語言中的指針詳解_#算法_03

2.有趣的代碼

c語言中的指針詳解_數組_04

函數指針數組

數組是一個存放相同類型數據的存儲空間,那要把函數的地址存到一個數組中,那這個數組就叫函數指針數組,那函數指針的數組如何定義呢?如:

int Add(int x,int y)
{
	return x + y;
}

int Sub(int x, int y)
{
	return x - y;
}


int main()
{
	int (*pf_A)(int, int) = Add;
	int (*pf_S)(int, int) = Sub;

	int (*pf_arr[2])(int, int) = { Add,Sub };

	return 0;
}

函數指針數組的 類型 必須是一致的(int (*)(int,int))
怎麼使用?

#include <stdio.h>

int Add(int x,int y)
{
	return x + y;
}

int Sub(int x, int y)
{
	return x - y;
}

int main()
{
	int (*pf_A)(int, int) = Add;
	int (*pf_S)(int, int) = Sub;

	int (*pf_arr[2])(int, int) = { Add,Sub };
	
	int c = pf_arr[0](8,2);
	printf("%d ", c);
	
	c = pf_arr[1](8, 2);
	printf("%d ", c);

	return 0;
}

使用的時候,先用數組存儲起來,然後調用對應的元素,並且傳參,他會有返回值