指针知识体系搭建

铁律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 ;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值