指针知识体系搭建

指针强化

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

结论:指针的步长,根据所指内存空间类型来定。

        

注意:     建立指针指向谁,就把把谁的地址赋值给指针。图和代码和二为一。        

                   不断的给指针变量赋值,就是不断的改变指针变量(和所指向内存空间没有任何关系)。


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

void main81()
{
	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 *getStr81()
{
	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 ;
}


铁律2:间接赋值(*p)是指针存在的最大意义

1)两码事:指针变量和它指向的内存块变量

2)条件反射:指针指向某个变量,就是把某个变量地址否给指针

3*p间接赋值成立条件:3个条件               

a)2个变量(通常一个实参,一个形参)

b) 建立关系,实参取地址赋给形参指针

c)*p形参去间接修改实参的值


//abc这3个条件 写在有一个函数

//ab 写在一块   c 单独写在另外一个函数里面  =====>函数调用

//a         bc写在一块引用

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上分配内存




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

//一级指针的典型用法
//数组 int a[10]
//字符串 
//1 C语言的字符串 以零结尾的字符串
//2 在C语言中没有字符串类型  通过字符数组 来模拟字符串 
//3 字符串的内存分配  堆上 栈上 全局区 (很重要)


//字符数组 初始化
void main51()
{

	//1 指定长度  
	char buf2[100] = {'a', 'b', 'c', 'd'};  
	//1-1char buf3[2] = {'a', 'b', 'c', 'd'}; //如果初始化的个数大于内存的个数 编译错误
	//1-22//后面的buf2[4]-buf2[99] 0


	//2 不指定长度  C编译器会自动帮程序员 求元素的个数
	char buf1[] = {'a', 'b', 'c', 'd'};  //buf1是一个数组 不是一个以0结尾的字符串

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

	printf("buf2[88]:%d \n", buf2[88]);

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

	return ;
}

//用字符串 来 初始化字符数组
//strlen() 长度 不包括0
//sizeof() 内存块的大小
void main52()
{
	int size = 0;
	char buf3[] = "abcd"; // buf3 作为字符数组 应该是5个字节 //作为字符串 应该4个字节

	int len = strlen(buf3);
	printf("buf3字符的长度:%d \n", len); //4

	//buf3 作为数组 数组是一种数据类型 本质(固定小大内存块的别名)
	size = sizeof(buf3); //
	printf("buf3数组所占内存空间大小:%d \n", size); //5

	printf("hello....\n");

	{
		char buf4[128] = "abcd"; // buf
		printf("buf4[100]:%d \n", buf4[100]);
	}
	system("pause");
	return ;
}

//通过数组下标 和 指针
void main58()
{
	int		i = 0;
	char	*p = NULL;
	char buf5[128] = "abcdefg"; // buf

	for (i=0; i<strlen(buf5); i++)
	{
		printf("%c ", buf5[i]); 
	}
	
	p = buf5; //buf 代表数组首元素的地址

	for (i=0; i<strlen(buf5); i++)
	{
		p = p +i;
		printf("%c ", *p ) ;
	}

	//buf
	for (i=0; i<strlen(buf5); i++)
	{
		printf("%c ", *(buf5+i) ) ;
	}
        //本质
	//[] *的推导过程
	// buf5[i] ===> buf5[0+i]; ==> *(buf5+i);

	{
		//buf5 = buf5 + 1;
		//buf5 = 0x11;
	}
	printf("hello....\n");·
	system("pause");
}

// []的本质 :和*p 是一样 ,只不过是符合程序员的阅读习惯
// buf5 是一个指针,  只读的常量  buf5是一个常量指针  析构内存的时候,保证buf所指向的内存空间安全释放

// 理解  因为buf5[]在栈上分配内存,结束的时候会被退栈结束。
//为什么这么做?

//p普通指针和内存首地址区别





铁律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上分配内存


//指针数组
char *   p1[] = {"123", "456", "789"};




//二维数组
char p2[3][4]  = {"123", "456", "789"}; 不能用**p2来接,提可用用(*p)[4]接 具体原因看下



//手工二维内存
char **p3 = (char **)malloc(3 * sizeof(char *)); //int array[3];


二级指针做输入第一种内存模型

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

void main21()
{
	int		i = 0, j = 0;
	int		num = 0;
	char	*tmp = NULL;
	//数组 数组中的每一个元素是指针 指针数组
	char *myArray[] = {"aaaaaa", "ccccc", "bbbbbb", "111111"};

	//打印
	num = sizeof(myArray)/sizeof(myArray[0]);   //  16/4
        //理解:总的指针大小除以单个指针大小

	printf("排序之前\n");
	for (i=0; i<num; i++)
	{
		//printf("%s \n", myArray[i]);
		printf("%s \n", *(myArray+i) );
	}

	//排序
	for (i=0; i<num; i++)
	{
		for (j=i; j<num; j++)
		{
			if (strcmp(myArray[i], myArray[j]) > 0 )
			{
				tmp = myArray[i];  //注意  交换的是数组元素 交换的是指针的值
				myArray[i] = myArray[j];
				myArray[j] = tmp;
			}
		}
	}

	printf("排序之后\n");
	for (i=0; i<num; i++)
	{
		//printf("%s \n", myArray[i]);
		printf("%s \n", *(myArray+i) );
	}

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


void printMyArray11(char **myArray, int num)
{
	int i = 0;
	for (i=0; i<num; i++)
	{
		//printf("%s \n", myArray[i]);
		printf("%s \n", *(myArray+i) );
	}
}

void sortMyArray11(char **myArray, int num)
{
	int i =0 , j = 0;
	char *tmp = NULL;
	//排序
	for (i=0; i<num; i++)
	{
		for (j=i; j<num; j++)
		{
			if (strcmp(myArray[i], myArray[j]) > 0 )
			{
				tmp = myArray[i];  //注意  交换的是数组元素 交换的是指针的值 //改变指针的指向
				myArray[i] = myArray[j];
				myArray[j] = tmp;
			}
		}
	}
}

void main211()
{
	int		i = 0, j = 0;
	int		num = 0;
	char	*tmp = NULL;
	//数组 数组中的每一个元素是指针 指针数组
	char *myArray[] = {"aaaaaa", "ccccc", "bbbbbb", "111111"};

	//打印
	num = sizeof(myArray)/sizeof(myArray[0]);

	printf("排序之前\n");
	printMyArray11(myArray, num);


	sortMyArray11(myArray, num);


	printf("排序之后\n");
	printMyArray11(myArray, num);

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



二级指针做输入第二种内存模型

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


//打印  排序 
//封装成函数
void main31()
{
	int i = 0, j = 0;
	int num = 4;
	char myBuf[30];
	char tmpBuf[30];
	char myArray[10][30] = {"aaaaaa", "ccccc", "bbbbbbb", "1111111111111"};

	//打印 
	printf("排序之前\n");
	for (i=0; i<num; i++)
	{
		printf("%s\n", myArray[i]);
	}

	for (i=0; i<num; i++)
	{
		for (j=i+1; j<num; j++)
		{
			if (strcmp (myArray[i],  myArray[j]) > 0)
			{
				strcpy(tmpBuf, myArray[i]);  //交换的是内存块
				strcpy(myArray[i], myArray[j]);
				strcpy(myArray[j], tmpBuf);
			}
		}
	}

	//打印 
	printf("排序之后\n");
	for (i=0; i<num; i++)
	{
		printf("%s\n", myArray[i]);
	}


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

//问题的本质是:dm03_二级指针做输入_第2种内存模型 的 myArray + 1
				// dm03_二级指针做输入_第1种内存模型   myArray + 1 不一样 ;
//指针的步长不一样  指针所指向的内存空间的数据类不一样 。。。。
void printMyArray02_err(char **myArray, int num)
/*
理解
二维数组名即数组地址,指向首行地址,不是指针的指针。
表面上看,行地址一维数组指针,而数组名指向行就应该是指针的指针。

但是你考虑过没有,如果a[10][10],假设int **p=a; 那么要对行递增执行p++时,编译器如何知道列宽?因为int **是指指向一个 int *类型的指针,其数据宽度是4字节,内置宽度,因此p将指向下一个整数元素地址,也就是p递增了4字节,而不是10个int型数据的宽度,这就错位了。

所以a[10][10]的地址类型不是简单的指针的指针,而是行指针的指针,而行宽是由你定义的数组列数和元素类型所决定,int类型就是4*10=40个字节。这叫数据对齐。因此编译器在转换数组名时,会根据对齐要求而确定相应的指针类型,所以a的地址类型应该是int (*)[10],而不是int **。

所以应该这样
int (*p)[10]=a;

其含义为,p是一个指向(含10个int类型元素的一维数组或行的)指针,其实本质上任何指针都是4字节(32位系统),你完全可以将一种指针类型强制转为任何其他类型,那为什么还要区分指针类型,就是为了指针运算时实现数据对齐,准确定位。

*/
{
	int i = 0;
	for (i=0; i<num; i++)
	{
		//printf("%s \n", myArray[i]);
		printf("%s \n", *(myArray+i) );  //
	}
}

void printMyArray02(char myArray[10][30], int num)
{
	int i = 0;
	for (i=0; i<num; i++)
	{
		//printf("%s \n", myArray[i]);
		printf("%s \n", *(myArray+i) );  //
	}
}



//交换的是内存块。。。。。。。。
void sortMyArray02(char myArray[10][30], int num)
{
	int i, j = 0;
	char tmpBuf[30];

	for (i=0; i<num; i++)
	{
		for (j=i+1; j<num; j++)
		{
			if (strcmp (myArray[i],  myArray[j]) > 0)
			{
				strcpy(tmpBuf, myArray[i]);  //交换的是内存块
				strcpy(myArray[i], myArray[j]);
				strcpy(myArray[j], tmpBuf);
			}
		}
	}

}

//打印  排序 
//封装成函数
void main333()
{
	int i = 0, j = 0;
	int num = 4;
	char myBuf[30];
	char tmpBuf[30];
	char myArray[10][30] = {"aaaaaa", "ccccc", "bbbbbbb", "1111111111111"};

	//myArray: 编译器只会关心:有10行 ,每行30列。。。。。干什么?myArray+1  多维数组名的本质,

	{
		int len1 = sizeof(myArray);//300
		int len2 = sizeof(myArray[0]);//30
		int size = len1/len2;//10
		printf("len1:%d , len2:%d  size:%d \n", len1, len2, size);

	}
	

	//打印 
	printf("排序之前\n");

	printMyArray02(myArray, num);
	

	sortMyArray02(myArray, num);

	//打印 
	printf("排序之后\n");
	printMyArray02(myArray, num);

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




二级指针做输入的第三种内存模型


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

//
void main41()
{
	int i = 0, j = 0;
	char **p2 = NULL;
	int num = 5;
	char *tmp = NULL;
	char tmpbuf[100];
	p2 = (char **)malloc(sizeof(char *) * num);

	for (i=0; i<num; i++)
	{
		p2[i] = (char *)malloc(sizeof(char)  * 100  ); //char buf[100];
		sprintf(p2[i], "%d%d%d", i+1, i+1, i+1);
	}

	//排序之前 
	printf("排序之前\n");
	for (i=0; i<num; i++)
	{
		printf("%s \n", p2[i]);
	}


	//排序 交换的是 :指针
	/*
	for (i=0; i<num; i++)
	{
		for (j=i+1; j<num; j++)
		{
			if (strcmp( p2[i] , p2[j]) < 0)
			{
				tmp = p2[i];
				p2[i] = p2[j];
				p2[j] = tmp;
			}
		}
	}
	*/

	//排序 交换的时候内存
	
	for (i=0; i<num; i++)
	{
		for (j=i+1; j<num; j++)
		{
			if (strcmp( p2[i] , p2[j]) < 0)
			{
				strcpy(tmpbuf, p2[i]);
				strcpy( p2[i], p2[j]);
				strcpy( p2[j], tmpbuf);
			}
		}
	}
	

	//排序之前 
	printf("排序之后\n");
	for (i=0; i<num; i++)
	{
		printf("%s \n", p2[i]);
	}

	//释放内存
	for(i=0; i<num; i++)
	{
		if (p2[i] != NULL)
		{
			free(p2[i]);
			p2[i] = NULL;
		}
	}

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


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

//

//
char **getMem41(int num)
{
	int i = 0;
	char **p2 = NULL;
	p2 = (char **)malloc(sizeof(char *) * num);
	if (p2 == NULL)
	{
		return NULL;
	}

	for (i=0; i<num; i++)
	{
		p2[i] = (char *)malloc(sizeof(char)  * 100  ); //char buf[100];
		sprintf(p2[i], "%d%d%d", i+1, i+1, i+1);
	}
	return p2;
}





void printMyArray03(char **myArray, int num)
{
	int i = 0;
	for (i=0; i<num; i++)
	{
		//printf("%s \n", myArray[i]);
		printf("%s \n", *(myArray+i) );
	}
}

void sortMyArray03(char **myArray, int num)
{
	int i =0 , j = 0;
	char *tmp = NULL;
	//排序
	for (i=0; i<num; i++)
	{
		for (j=i; j<num; j++)
		{
			if (strcmp(myArray[i], myArray[j]) > 0 )
			{
				tmp = myArray[i];  //注意  交换的是数组元素 交换的是指针的值 //改变指针的指向
				myArray[i] = myArray[j];
				myArray[j] = tmp;
			}
		}
	}
}

void getMem41_Free(char **p2, int num)
{
	int i = 0;
	//释放内存
	for(i=0; i<num; i++)
	{
		if (p2[i] != NULL)
		{
			free(p2[i]);
			p2[i] = NULL;
		}
	}

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

}


void main444()
{
	int i = 0, j = 0;
	char **p2 = NULL;
	int num = 5;
	char *tmp = NULL;
	char tmpbuf[100];
	p2 = getMem41(num);


	//排序之前 
	printf("排序之前\n");
	printMyArray03(p2, num);

	sortMyArray03(p2, num);

	//排序 交换的是 :指针
	/*
	for (i=0; i<num; i++)
	{
		for (j=i+1; j<num; j++)
		{
			if (strcmp( p2[i] , p2[j]) < 0)
			{
				tmp = p2[i];
				p2[i] = p2[j];
				p2[j] = tmp;
			}
		}
	}
	*/
	

	//排序之前 
	printf("排序之后\n");
	printMyArray03(p2, num);

	getMem41_Free(p2,  num); //p2是一个野指针

	

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






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






指针的输入输出特性

//指针做输出:被调用函数分配内存  -----OK
//指针做输入:主调用函数 分配内存

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

//
int  getMem41(char **myp1/*out*/ , int *mylen1 /*out*/,  char **myp2 /*out*/, int *mylen2 /*out*/)
{
	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;
}

char *  getMem42(int num)
{
	int		ret = 0;
	char	*tmp1;

	tmp1 = (char *)malloc(num);
	strcpy(tmp1, "1132233");

	return tmp1;
}



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

	ret = getMem41(&p1, &len1, &p2, &len2);
	if (ret != 0)
	{
		printf("func getMem41() 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;
	}

	p1 = getMem42(100);
	printf("p1:%s \n", p1);
	if (p1 != NULL)
	{
		free(p1);
		p1 = NULL;
	}



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

理解:在这个程序中,要做输出时,函数的形参就必须是二级指针,因为当传入一级指针时,就没办法做输出。






  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值