天天看點

C語言指針,拿下!一. 引言二.正文

一. 引言

要說學習C語言的過程中什麼最頭疼,那必然是指針!咱能放棄嗎,咱不能!看完這篇,助你拿下指針~

首先來快速回顧一下指針的基礎知識:

  1. 指針也是一個變量,隻不過這個變量是用來存放位址的。要知道的是,位址唯一指向一塊記憶體空間
    C語言指針,拿下!一. 引言二.正文

在這個例子中,指針變量p中存放的是a的位址

  • 示意圖:
    C語言指針,拿下!一. 引言二.正文
  1. 指針是有類型的,指針的類型決定指針 + - 運算時候的步長及解引用操作時的權限大小
  2. 指針變量的大小無關指針變量的類型,固定為4/8個位元組(32位平台/64位平台)
C語言指針,拿下!一. 引言二.正文

(此測試是在32位平台下進行)

  1. 對于操作符 (&) 和(*) 可以了解為一對相反作用的符号:(pa = &a)(*pa = a)

& (取出變量的位址): &a --> 取出變量a的位址

* (取出位址對應的資料): *pa --> 得到指針變量pa(a的存儲位址)所指向的值

二.正文

1.字元指針

字元指針,就是一個指針變量存儲位址所指向的内容是char類型的資料。
  • 舉個栗子
char ch = 'w';
char* pch = &ch;
           

但要注意的是:字元指針在存儲字元位址的時候隻存儲首字元位址

  • 舉個栗子
int main()
{
	char str1[] = "hello bit.";
	char str2[] = "hello bit.";
	const char* str3 = "hello bit.";
	const char* str4 = "hello bit.";
	if (str1 == str2)
		printf("str1 and str2 are same\n");
	else
		printf("str1 and str2 are not same\n");
	if (str3 == str4)
		printf("str3 and str4 are same\n");
	else
		printf("str3 and str4 are not same\n");
	return 0;
}
           
  • 輸出結果
str1 and str2 are not same
str3 and str4 are same
           
這裡str3和str4指向的是一個同一個常量字元串。C/C++會把常量字元串存儲到單獨的一個記憶體區域,當幾個指針。指向同一個字元串的時候,他們實際會指向同一塊記憶體。但是用相同的常量字元串去初始化不同的數組的時候就會開辟出不同的記憶體塊。是以str1和str2不同,str3和str4不同。

2. 指針 & 數組

首先掌握以下三個必須爛熟于心的數組基礎知識

  1. 一般來講,數組名指的是數組首元素的位址
  2. 當數組名arr 與 sizeof() 結合時候 (如 sizeof ( arr ) ),數組名代表的是整個數組,而非首元素位址
  3. 當數組名arr 與 & 結合時候*(如 & arr )*,數組名代表的是整個數組,而非首元素位址

2.1 數組指針 vs 指針數組

首先要厘清楚 什麼是數組指針, 什麼是指針數組,二者并不一樣,不要混淆!

名稱 解釋 類型
指針數組 是一個存放指針的數組 數組
數組指針 一個指向數組的指針 指針
  • 舉個栗子
int *p1[10];  // 指針數組, 符号[]的優先級高于*

int (*p2)[10];  // 數組指針,通過()使得 * 和 p 先進行結合,然後指向一個大小為10個整形的數組
           

2.2 & 數組名 vs 數組名

思考以下,對于下面的數組, arr 和 &arr 分别是什麼意思?

int arr[10];
           

對于arr,很容易了解,但是&arr可能會有一些困惑。但可以明确的是,==1.數組名也是數組首元素的位址==,==2.&的含義是取出變量的位址==。看下面這段代碼:

int main()
{
int arr[10] = {0};
printf("%p\n", arr);
printf("%p\n", &arr);
return 0;
}
           
  • 輸出結果
    C語言指針,拿下!一. 引言二.正文

可以看到,二者的輸出結果是一緻的,但能說明二者完全相等嗎,答案是 不能

  • 驗證
int main()
{
int arr[10] = { 0 };
printf("arr = %p\n", arr);
printf("&arr= %p\n", &arr);
printf("arr+1 = %p\n", arr+1);
printf("&arr+1= %p\n", &arr+1);
return 0;
}
           
  • 輸出結果
C語言指針,拿下!一. 引言二.正文
含義 +1後的結果
&arr 表示數組的位址 跳過一個元素
arr 表示數組首元素的位址 跳過整個數組
  • 指針數組的示意圖
    C語言指針,拿下!一. 引言二.正文
  • 嘗試辨析下面代碼的意思
int arr[5];  // 數組
int *parr1[10];  // 指針數組 
int (*parr2)[10];  // 數組指針
int (*parr3[10])[5];  //數組指針(所接收的數組為指針數組)
           
是否對于最後一項 int (*parr3[10])[5] 仍然存疑?其實可以看作int (*p)[5] ,很明顯這是一個數組指針,因為存在()使得 * p 先進行了結合,然後 * p 代表的是 原式中的指針數組,該指針 指向的數組的示意圖如下:
C語言指針,拿下!一. 引言二.正文

2.3 補充 -- 數組傳參

  1. 正常數組傳參時候,如果傳遞的是數組名,其實是在傳遞首元素的位址

2.二維數組傳遞參數時, 函數的形參設計隻能省略第一個 [] 的數字,如下:

void test1(int arr[][5])
{}  // 正确
void test2(int arr[3][])
{}  // 錯誤 省略了列
           

3 函數指針

函數指針,就是接收函數位址的指針
  • 舉個栗子
int (*pf)(int, int);
// 有時候可以簡化為
int (*)();
           

有趣的是,函數和指針的結合規律同數組有些相似,如下:

void test()
{
printf("hehe\n");
}
// 下列二式中哪個有能力存放test函數的位址?
void (*pfun1)(); 
void *pfun2(); 
           
答案是 punf1 可以存放,是因為 * 和pfun1 先結合,是以是函數指針, 可以存放函數的位址

4. 大燴菜系列

4.1 函數 & 指針 & 數組 的結合體 —— 函數指針數組

看到三者,莫不是有點頭暈?别怕!将函數指針數組拆解為兩部分:函數指針和數組

  • 首先看數組:數組 是一個存放相同類型資料的存儲空間。
  • 而函數指針是一個指針,一個用來接收函數的指針。

它的用途是: 轉移表

比如編寫一個電腦,内有加減乘除四種運算函數,将這四種運算函數的位址存放在一個數組中,該數組即為函數指針數組。(具體代碼可在各大部落格平台找到,此處不貼)

4.2 套娃更深一層 ——指向函數指針數組的指針

首先可以明确,它是一個指針,該指針指向一個數組,且數組的元素都是函數指針
C語言指針,拿下!一. 引言二.正文
  • 舉個栗子
void test(const char* str)
{
printf("%s\n", str);
}
int main()
{
//函數指針pfun
void (*pfun)(const char*) = test;
//函數指針的數組pfunArr
void (*pfunArr[5])(const char* str);
pfunArr[0] = test;
//指向函數指針數組pfunArr的指針ppfunArr
void (*(*ppfunArr)[5])(const char*) = &pfunArr;
return 0;
}