指針知識體系搭建

鐵律1:指針是一種數據類型


1)指針也是一種變量,佔有內存空間,用來保存內存地址
      測試指針變量佔有內存空間大小

2)*p操作內存
在指針聲明時,*號表示所聲明的變量爲指針
在指針使用時,*號表示 操作 指針所指向的內存空間中的值
    *p相當於通過地址(p變量的值)找到一塊內存;然後操作內存
    *p放在等號的左邊賦值(給內存賦值)
    *p放在等號的右邊取值(從內存獲取值)

3)指針變量和它指向的內存塊是兩個不同的概念
//含義1 給p賦值p=0x1111; 只會改變指針變量值,不會改變所指的內容;p = p +1; //p++
//含義2 給*p賦值*p='a'; 不會改變指針變量的值,只會改變所指的內存塊的值  
//含義3 =左邊*p 表示 給內存賦值, =右邊*p 表示取值 含義不同切結!
//含義4 =左邊char *p 
//含義5 保證所指的內存塊能修改

4)指針是一種數據類型,是指它指向的內存空間的數據類型  
含義1:指針步長(p++),根據所致內存空間的數據類型來確定
p++=➜(unsigned char )p+sizeof(a);
結論:指針的步長,根據所指內存空間類型來定。
    
注意:建立指針指向誰,就把把誰的地址賦值給指針。圖和代碼和二爲一。    
          不斷的給指針變量賦值,就是不斷的改變指針變量(和所指向內存空間沒有任何關係)。

鐵律2:間接賦值(*p)是指針存在的最大意義 


1)兩碼事:指針變量和它指向的內存塊變量
2)條件反射:指針指向某個變量,就是把某個變量地址否給指針
3)*p間接賦值成立條件:3個條件 
a)2個變量(通常一個實參,一個形參)
b) 建立關係,實參取地址賦給形參指針 
c) *p形參去間接修改實參的值 

Int iNum = 0; //實參
int *p = NULL;
p = &iNum;
iNum = 1;
*p =2 ; //通過*形參 == 間接地改變實參的值
*p成立的三個條件:

4)引申: 函數調用時,用n指針(形參)改變n-1指針(實參)的值。
//改變0級指針(int iNum = 1)的值有2種方式
//改變1級指針(eg char *p = 0x1111 )的值,有2種方式
//改變2級指針的(eg char **pp1 = 0x1111 )的值,有2種方式

//函數調用時,形參傳給實參,用實參取地址,傳給形參,在被調用函數裏面用*p,來改變實參,把運算結果傳出來。
//指針作爲函數參數的精髓。

鐵律3:理解指針必須和內存四區概念相結合 


1) 主調函數 被調函數 
a)    主調函數可把堆區、棧區、全局數據內存地址傳給被調用函數
b)    被調用函數只能返回堆區、全局數據
2)  內存分配方式
a)    指針做函數參數,是有輸入和輸出特性的。


鐵律4:應用指針必須和函數調用相結合(指針做函數參數) 

編號

指針函數參數

內存分配方式(級別+堆棧)

主調函數

實參

被調函數

形參

備註

 

01

1級指針

(做輸入)

分配

使用

一般應用禁用

分配

使用

常用

Int showbuf(char *p);  

int showArray(int *array, int iNum)

02

1級指針

(做輸出)

使用

結果傳出

常用

int geLen(char *pFileName, int *pfileLen);

03

2級指針

(做輸入)

分配

使用

一般應用禁用

分配

使用

常用

int main(int arc ,char *arg[]); 指針數組

int shouMatrix(int [3][4], int iLine);二維字符串數組

04

2級指針

(做輸出)

使用

分配

常用,但不建議用,轉化成02

int getData(char **data, int *dataLen);

Int getData_Free(void *data);

Int getData_Free(void **data); //避免野指針

05

3級指針

(做輸出)

使用

分配

不常用

int getFileAllLine(char ***content, int *pLine);

int getFileAllLine_Free(char ***content, int *pLine);

                 

指針做函數參數,問題的實質不是指針,而是看內存塊,內存塊是1維、2維。

  1. 如果基礎類int變量,不需要用指針;
  2. 若內存塊是1維、2維。

鐵律5:一級指針典型用法(指針做函數參數)

一級指針做輸入

int showbuf(char *p)
int showArray(int *array, int iNum)

一級指針做輸出

int geLen(char *pFileName, int *pfileLen);

理解

主調函數還是被調用函數分配內存
被調用函數是在heap/stack上分配內存

鐵律6:二級指針典型用法(指針做函數參數)


二級指針做輸入

int main(int arc ,char *arg[]); 字符串數組
int shouMatrix(int [3][4], int iLine);

二級指針做輸出

int Demo64_GetTeacher(Teacher **ppTeacher);
int Demo65_GetTeacher_Free(Teacher **ppTeacher);
int getData(char **data, int *dataLen);
Int getData_Free(void *data);
Int getData_Free2(void **data); //避免野指針

理解

主調函數還是被調用函數分配內存
被調用函數是在heap/stack上分配內存

鐵律7: 三級指針輸出典型用法

三級指針做輸出

int getFileAllLine(char ***content, int *pLine);
int getFileAllLine_Free(char ***content, int *pLine);

理解

主調函數還是被調用函數分配內存
被調用函數是在heap/stack上分配內存

鐵律8:雜項,指針用法幾點擴充

1)野指針 2種free形式

int getData(char **data, int *dataLen);
int getData_Free(void *data);
int getData_Free2(void **data);

2)2次調用
主調函數第一次調用被調用函數求長度;根據長度,分配內存,調用被調用函數。
3)返回值char */int/char **
4)C程序書寫結構
商業軟件,每一個出錯的地方都要有日誌,日誌級別

鐵律9:一般應用禁用malloc/new

【王保明老師經典語錄】


1)指針也是一種數據類型,指針的數據類型是指它所指向內存空間的數據類型
2)間接賦值*p是指針存在的最大意義 
3)理解指針必須和內存四區概念相結合 
4)應用指針必須和函數調用相結合(指針做函數參數)
指針是子彈,函數是槍管;子彈只有沿着槍管發射才能顯示它的威力;指針的學習重點不言而喻了吧。接口的封裝和設計、模塊的劃分、解決實際應用問題;它是你的工具。
5)指針指向誰就把誰的地址賦給指針 
6)指針指向誰就把誰的地址賦給指針,用它對付鏈表輕鬆加愉快
7)鏈表入門的關鍵是分清楚鏈表操作和輔助指針變量之間的邏輯關係
8)C/C++語言有它自己的學習特點;若java語言的學習特點是學習、應用、上項目;那麼C/C++語言的學習特點是:學習、理解、應用、上項目。多了一個步驟吧。
9)學好指針才學會了C語言的半壁江山,另外半壁江山在哪裏呢?你猜,精彩剖析在課堂。
10) 理解指針關鍵在內存,沒有內存哪來的內存首地址,沒有內存首地址,哪來的指針啊。

代碼解析

#define  _CRT_SECURE_NO_WARNINGS 
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

void main00()
{
	int a = 10;
	char *p1 = 100;  //分配4個字節的內存
	char ****p2 = 100;
	int *p3 = NULL;
	p3 = &a;
	*p3 = 20; //間接的修改a的值
	//*就像一把鑰匙 通過一個地址(&a),去修改a變量的標示的內存空間

	{
		int c = 0;
		c = *p3;  //c=20
		//*p放在=號左邊 寫內存
		//*p放=號的右邊 讀內存
		printf("c:%d \n", c);
	}
	{
		char *p4 = NULL;
		p4 = (char *)malloc(100);
		p4 = (char *)malloc(200); //0xcc11
	}
	printf("a:%d , p1:%d , p2: %d", sizeof(a), sizeof(p1), sizeof(p2));
	printf("hello...\n");
	system("pause");
	return ;
}

char *getStr()
{
	char *tmp = NULL;
	tmp = "abcdefgf";
	return tmp;
}

/*
int  getABC1(char    *p1);  int getABC1(char*       p1);
int	getABC2(char **    p2);	int	getABC2(char *    *p2);   int	getABC2(char           **p2);
int	getABC3(char ***p3);
int	getABC4(char (*p4)[30]);  int	getABC4(char (*p4)            [30]);
int	getABC5(char p5[10][30]); int	getABC5(char p5[10][30]);

//指針做函數參數 形參有多級指針的時候, 
//站在編譯器的角度 ,只需要分配4個字節的內存(32bit平臺)
//當我們使用內存的時候,我們才關心指針所指向的內存 是一維的還是二維的
*/

void main()
{
	char *p = getStr81();
	printf("p:%s \n", p);
	*(p+2) = 'r';  //經常出現的錯誤 保證指針所指向的內存空間 可以被修改
	system("pause");
	return ;
}

 野指針產生的原因

//指針變量和它所指向的內存空間變量是兩個不同的概念

//避免方法: 
//1)定義指針的時候,指針變量*p1初始化成nuLL 2)釋放指針所指向的內存空間後,把它所指向的內存空間變量p1重置成NULL。
void main()
{
	char  *p1 = NULL;
	p1 = (char *)malloc(100);
	if (p1 == NULL)
	{
		return ;
	}
	strcpy(p1, "11112222");

	printf("p1:%s \n", p1);

	if (p1 != NULL)
	{
		free(p1);
		p1 = NULL;
	}
	printf("hello...\n");
	system("pause");
	return ;
}

一級指針技術推演

int  getFileLen(int *p)
{
	*p = 41;  //p的值是a的地址 *a的地址間接修改a的值 
	//在被調用函數裏面 通過形參 去 間接的修改 實參的值...
}

//形參的屬性
int  getFileLen3(int b)
{
	int  i = 0;
	b = 100;//p的值是a的地址 *a的地址間接修改a的值
}

//1級指針的技術推演
void main()
{
	int a = 10;  //條件1  定義了兩個變量(實參 另外一個變量是形參p)
	int *p = NULL;

	//修改a的值
	a = 20; //直接修改
	p = &a;  //條件2 建立關聯
	*p = 30; //p的值是a的地址 *就像一把鑰匙 通過地址 找到一塊內存空間 求間接的修改了a的值
	printf("a: %d \n", a);

	{
		*p = 40;  //  p的值是a的地址 *a的地址間接修改a的值  //條件3 *p
		printf("a: %d \n", a);
	}

	getFileLen(&a); //建立關聯: 把實參取地址 傳遞給 形參
	printf("getFileLen後 a: %d \n", a);
	getFileLen3(a); //無法修改
	printf("getFileLen3後 a: %d \n", a);

	printf("hello...\n");
	system("pause");
	return ;
}

二級指針技術推演

void getMem(char **p2)
{
	*p2 = 400; //間接賦值  p2是p1的地址
}

void getMem2(char *p2)
{
	p2 = 800; //間接賦值  p2是p1的地址
}

void main()
{
	char *p1 = NULL;
	char **p2 = NULL;
	p1 = 0x11;
	p2 = 0x22;

	//直接修改p1的值
	p1 = 0x111;
	//間接修改p1的值
	p2 = &p1; 
	*p2 = 100; //間接賦值  p2是p1的地址

	printf("p1:%d \n", p1);

	{
		*p2 = 200; //間接賦值  p2是p1的地址
		printf("p1:%d \n", p1);
	}

	getMem(&p1);
	getMem2(p1);
	printf("p1:%d \n", p1);
	system("pause");
	return ;
}
int  getMem3(char **myp1, int *mylen1,  char **myp2, int *mylen2)
{
	int		ret = 0;
	char	*tmp1, *tmp2;
	tmp1 = (char *)malloc(100);
	strcpy(tmp1, "1132233");
	//間接賦值 
	*mylen1 = strlen(tmp1);  //1級指針
	*myp1 = tmp1; //2級指針的間接賦值
	tmp2 = (char *)malloc(200);
	strcpy(tmp2, "aaaaavbdddddddd");
	*mylen2 = strlen(tmp2);  //1級指針
	*myp2 = tmp2; //2級指針的間接賦值
	return ret;
}

int  main()
{
	int		ret = 0;
	char	*p1 = NULL;
	int		len1 = 0;
	char	*p2 = NULL;
	int		len2 = 0; 

	ret = getMem3(&p1, &len1, &p2, &len2);
	if (ret != 0)
	{
		printf("func getMem3() err:%d \n", ret);
		return ret;
	}
	printf("p1:%s \n", p1);
	printf("p2:%s \n", p2);
	if (p1 != NULL)
	{
		free(p1);
		p1 = NULL;
	}
	if (p2 != NULL)
	{
		free(p2);
		p2 = NULL;
	}

	printf("p1:%d \n", p1);
	system("pause");
	return ret;
}

間接賦值成立的三個條件

/* 
	條件1  //定義1個變量(實參) //定義1個變量(形參)
	條件2  //建立關聯:把實參取地址傳給形參
	條件3://*形參去間接地的修改了實參的值。
*/

//間接賦值的應用場景
void main()
{
	//1 2 3 這3個條件 寫在有一個函數
	//12 寫在一塊   3 單獨寫在另外一個函數裏面  =====>函數調用
	//1         23寫在一塊 ===>拋磚 ====C++會有,到時候,你別不認識......
	char from[128];
	char to[128] = {0};
	char *p1 = from;
	char *p2 = to;

	strcpy(from, "1122233133332fafdsafas");
	while (*p1 != '\0')
	{
		*p2 = *p1;
		p2 ++;
		p1 ++;
	}
	printf("to:%s \n", to);
	system("pause");
	return ;
}

指針的輸入與輸出特性

//指針做輸出:被調用函數分配內存  -----OK
//指針做輸入:主調用函數 分配內存
//求文件中的兩段話的長度
int getMem(char **myp1, int *mylen1, char **myp2, int *mylen2)
{
	char *tmp1 = NULL;
	char *tmp2 = NULL;
	tmp1 = (char *)malloc(100);
	if (tmp1 == NULL)
	{
		return -1;
	}
	strcpy(tmp1, "abcdefg");
	*mylen1 = strlen(tmp1);

	*myp1 = tmp1; //間接修改實參p1的值

	tmp2 = (char *)malloc(100);
	if (tmp2 == NULL)
	{
		return -2;
	}
	strcpy(tmp2, "11122233333");
	*mylen2 = strlen(tmp2);

	*myp2 = tmp2; //間接修改實參p1的值
	return 0;
}

int getMem_Free(char **myp1)
{

	/*
	if (myp1 == NULL)
	{
		return ;
	}
	free(*myp1);  //釋放完指針變量 所致的內存空間
	*myp1 = NULL;  //把實參修改成nULL
	*/
	char *tmp = NULL;
	if (myp1 == NULL)
	{
		return -1;
	}
	tmp = *myp1;
	free(tmp);  //釋放完指針變量 所致的內存空間
	*myp1 = NULL;  //把實參修改成nULL
	return 0;
}


void main00()
{
	char  *p1 = NULL;
	int len1 = 0;
	char *p2 = NULL;
	int len2 = 0;
	int ret = 0;
	ret  = getMem(&p1, &len1, &p2, &len2 );

	printf("p1: %s \n", p1);
	printf("p2: %s \n", p2);
	getMem_Free(&p1);
	getMem_Free(&p2);  
	system("pause");
	return ;
}

int getMem_Free0(char *myp1)
{
	if (myp1 == NULL)
	{
		return -1;
	}
	free(myp1);  //釋放完指針變量 所致的內存空間
	myp1 = NULL;
	return 0;
}

void main01()
{
	char  *p1 = NULL;
	int len1 = 0;
	char *p2 = NULL;
	int len2 = 0;
	int ret = 0;
	ret  = getMem(&p1, &len1, &p2, &len2 );
	printf("p1: %s \n", p1);
	printf("p2: %s \n", p2);

	if (p1 != NULL)
	{
		free(p1);
		p1 = NULL;
	}
	if (p2 != NULL)
	{
		free(p2);
		p2 = NULL;
	}

	getMem_Free0(p1);  //在被調用函數中  把p1所指向的內存給釋放掉 ,但是 實參p1不能被修改成NULLL 有野指針現象
	getMem_Free0(p2);  
	system("pause");
	return ;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章