优秀的编程知识分享平台

网站首页 > 技术文章 正文

指针进阶知识(上)(指针基本知识)

nanyue 2024-08-25 10:37:10 技术文章 6 ℃

前言:

在前面基础部分其实有很多内容没有涉及到,是因为我觉得有些知识,如果不摊开细讲的话,和没说差不多.所以就把一部分内容索性安排到了进阶部分.接下来的内容还接续上次的风格.增加了"思考一下",希望大家看到问题,可以多思考,拒绝思维惰性.

指针专题

指针"9铁律"

铁律1:指针也是一种数据类型

1)指针也是一种变量,占有内存空间,用来保存内存地址

2)*p操作内存

在指针声明时,*号表示所指明的变量为指针

在指针使用时,*号表示 操作 指针所指向的内存空间中的值

*p相当于通过地址找到一块内存,然后操作内存.

*p放在等号的左边赋值(给内存赋值)

*p放在等号的右边赋值(从内存获取值)

3) 指针变量和它指向的内存块是两个不同的概念

4)指针是一种数据类型,是它所指向的内存空间的数据类型

指针步长(p++),根据所致空间的数据类型来确定

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

指针指向谁,就把谁的地址赋值给指针

void main()
{   
	//测试指针变量占内存空间大小
	int a = 10;
	char *p1 = 100;
	char ****p2 = 100;
	printf("a:%d,p1:%d,p2:%d", sizeof(a), sizeof(p1), sizeof(p2));

	int *p3 = NULL;
	p3 = &a;
	*p3 = 20;//间接修改a的值 *就像一把钥匙,通过一个地址,去修改a变量的内存空间
	{
		int c = 0;
		c = *p3;
		//左边读内存,右边取内存
		printf("c:%d\n", c);
	}//不断的给指针赋值,就是不断的改变指针的指向
	{
		char *p4 = NULL;
		p4 = (char*)malloc(100);
		p4 = (char*)malloc(200);
	
	}
	printf("a:%d,p1:%d,p2:%d", sizeof(a), sizeof(p1), sizeof(p2));
	system("pause");//按任意键暂停,阻塞功能
	return 0;//正常退出值
}
char *getStr81()
{
	char *tmp = NULL;
	tmp= "asdfdffg";
	return tmp;
}
/*
int getABC1(char*p1);
int getABC2(char**p2);
int getABC3(char***p3);
int getABC4(char(*p4)[30]);
int getABC4(char p5[10][30]);
 指针做函数参数  形参有多级指针的时候
 站在编译器的角度,只需要分配4个字节的内存(32bit)
 当我们使用内存的时候,我们才关心指针所指向的内存,是一维的还的二维的
*/
void main()
{
	char *p = *getStr81();
	printf("p:%s\n", p);
	*(p + 2) = 'r';//保证所指的内存块,可以被修改
	system("pause");//按任意键暂停,阻塞功能
	return 0;//正常退出值
}

铁律1强化

/* 
向NULL地址处copy数据
void main()
{
char *p1 = NULL;
p1 = 0x00077;
strcpy(p1, "asdfgg");//指针所指向的内存空间拷贝数据
会发生冲突,原因:那块内存是系统保护的
system("pause");//按任意键暂停,阻塞功能
return 0;//正常退出值
}
*/

void main()//不断改变指针指向
{
	char  buf[128];//c语言可以在栈上分配内存
	int i;
	int j=0;
	char *p2 = NULL;//在做一个堆上的
	char *p1 = NULL;
	p1 = &buf[0];//数组首元素
	p1 = &buf[1];
	p1 = &buf[2];
	for (i = 0; i < 10; i++)//这也是不断的在改变指针指向
	{
		p1 = buf[i];
	}
	p2 = (char*)malloc(100);
	strcpy(p2, "abcdef213233213");
		for (i = 0; i < 10; i++)
		{
			p1 = p2[i];
			printf("%c", *p1);
		}
		system("pause");//按任意键暂停,阻塞功能
		return 0;//正常退出值
}

思考一下

int j=0;//0放在哪个区?

for (i = 0; i < 10; i++)//10放在哪个区?

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

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

2)间接赋值3条件

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

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

形参去间接修改实参的值

间接赋值的应用场景

场景1: 三个条件写在一个函数里

场景2: 1,2写在一块,3单独写在一个函数里---->函数调用

场景3:1, 2,3写在一起----->c++会有

3)函数调用时,用n指针修改n-1指针的值

函数调用时,形参传给实参,用实参取地址,传给形参,在被调用函数里面用*p,来改变实参,把运算结果传出来

间接赋值从0级指针到1级指针的技术推演

int getFileLen(int *p)//改变值在函数内部也可以做,为什么要单独把它甩出来?
{//int getFileLen(int *p)//这个是指针做了函数参数,为什么括号里写int *p?
  *p=40;
}
int getFileLen02()//当我们要求2个或者2个以上的文件,return只能返回一个结果,那怎么办?
{
	int a = 100;
	return a;
}
int getFileLen(int *p)//p的值是a的地址
{
	*p = 41;
	
}
int getFileLen3(int b)
{
	b = 100;
}

//1级指针的技术推演

void main()
{
	int a = 10;
	int *p = NULL;
	a = 20;
	p = &a;
	*p = 30;
	printf("a:%d\n", a);
	{
		*p = 40;
		printf("a:%d\n", a);
	}
	getFileLen(&a);
		printf("getFileLen a:%d\n", a);
		getFileLen3(&a);
		printf("getFileLen a:%d\n", a);
	printf("hello...\n");
		system("pause");//按任意键暂停,阻塞功能
		return 0;//正常退出值
}

2级指针的用法

1)在被调用函数直接修改1级指针的值

2)做输出

//间接赋值从1级指针到2级指针的技术推演
void getMem(char **p2)
{
	*p2 = 400;//间接赋值p2是p1的地址
}
void getMem2(char *p2)//和写到这里是一样的

{//形参修改成千上万回,和
	char *p2;//形参写到这里
	p2 = 800;//间接赋值p2是p1的地址
}
void main()
{
	
	char *p1 = NULL;//形参修改成千上万回,和这里没关系
	char **p2 = NULL;
	p1 = 0x11;
	p2 = 0x22;
	p1 = 0x111;
	p2 = &p1;
	*p2 = 100;
	printf("p1:&d\n", p1); 
	{
		*p2 = 200;
		printf("p1:&d\n", p1);
	}
	getMem(&p1);
	printf("p1:&d\n", p1);

	printf("hello...\n");
		system("pause");//按任意键暂停,阻塞功能
		return 0;//正常退出值
}
//指针做函数参数

铁律3:理解指针必须和内存四区相结合

1)主调函数 被调函数

主调函数可把堆区,栈区,全局数据内存地址传给被调用函数

被调函数只能返回堆区 全局数据区

2)内存分配方式

指针做函数参数,是有输入和输出特性

void getMem3(char **myp1/*out*/  , int *mylen1/*out*/, char **myp2, int *mylen2)
{
	//1级指针改变实参的值
	//修改0级用1级指针,1级用2级指针
	int ret = 0;
	char  *tmp1, *tmp2;
	tmp1 = (char*)malloc(100);//分配内存
	strcpy(tmp1, "1112223");
	//间接赋值
	*mylen1 = strlen(tmp1);//1级指针
	*myp1 = tmp1;//2级指针间接赋值
	tmp2 = (char*)malloc(200);
	strcpy(tmp2, "aaaaaaddddd");
	*mylen2 = strlen(tmp2);
	*myp2 = tmp2;//2级指针间接赋值
	return  ret;
}
char *getMem42(int num)
{
	int ret = 0;
	char *tmp1;
	tmp1 = (char*)malloc(num);
	strcpy(tmp1, "112233");
	return tmp1;
}
int main()
{
	int		 ret = 0;
	char	*p1 = NULL;//定义一个1级指针
	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;
	}
	p1 = getMem42(100);
	printf("p1:%s\n", p1);
	if (p1 != NULL)
	{
		free(p1);
		p1 = NULL;
	}
	printf("p1:%d\n", p1);
	printf("hello...\n");
	system("pause");//按任意键暂停,阻塞功能
	return 0;//正常退出值
}

铁律4:应用指针必须和函数调用相结合(指针做函数参数)


指针做函数参数,问题的实质不是指针,而是看内存块.

//1级指针的典型用法
//数组
//字符串(在c中没有字符串类型 通过字符数组 来模拟字符串)
//字符串的内存分配 堆上 栈上 全局区(!!!)


//字符数组 初始化
void  main()
{	 //1指定长度
	char  buf2[100] = { 'a','b','c','d' };
	//后面的4-99全部置为0
	//char  buf2[2]= { 'a','b','c','d' };  太大了报错

	//2不指定长度  c编译器会自动求元素个数
	char  buf1[] = { 'a','b','c','d' };//是一个数组,不是字符串
	printf("buf2:%s\n", buf2);
	printf("hello...\n");
	system("pause");//按任意键暂停,阻塞功能
	return 0;//正常退出值
}
//用字符串来初始化字符数组
void main()
{
	int size = 0;

	char  buf3[] = "asdf";//作为字符数组是5个字节===作为字符串4个字节
	int len = strlen(buf3);
	printf("buf3长度:%d\n", len);
	size = sizeof(buf3);
	printf("buf3数组所占内存空间大小:%d\n", size);
	printf("hello..\n");
	system("pause");//按任意键暂停,阻塞功能
	return 0;//正常退出值
}
//用数组或指针操作字符串
void main()
{
	int i = 0;
	char *p = NULL;//辅助指针变量
	char buf5[128] = "asdff";
	for (i = 0; i < strlen(buf5); i++)
	{
		printf("%c", buf5[i]);
	}
	p = buf5;//代表数组首元素地址
	for (i = 0; i < strlen(buf5); i++)
	{
		printf("%c", *(p + i));
	}
	printf("hello..\n");
	system("pause");//按任意键暂停,阻塞功能
	return 0;//正常退出值
}
//[] 本质和 *p是一样的,只不过是符合程序员的阅读习惯
//buf5是一个指针  一个常量指针==?  编译器自定义为常量,不能轻易改变
//为了保证首地址的安全性,必须是常量指针.要不然没有办法完全析构掉内存空间,导致浪费
/*buf5[i]==>buf5[0+i]==>*(buf5+i);
{
	buf5=buf5+1;
	buf5=0x11;
}
*/
字符串内存模型
void main()
{
	char buf[20] = "aaaa";
	char buf2[] = "bbbb";
	char *p1 = "111111";
	char *p2 = malloc(100);
	strcpy(p2, "3333");

	printf("hello..\n");
	system("pause");//按任意键暂停,阻塞功能
	return 0;//正常退出值
}
void main01()
{
	char a[] = "i am a student";
	char b [64];
	int i = 0;
	for (i = 0; *(a + i) != '\0'; i++)
	{
		*(b + i) = *(a + i);
	}
	//0没有复制到b的buf中
	b[i] = '\0';
	printf("a:%s\n", a);
	printf("b:%s\n", b);
	printf("hello..\n");
	system("pause");//按任意键暂停,阻塞功能
	return 0;//正常退出值
}
//字符串拷贝函数的技术推演
void copy_str21(char *from, char *to)
{
	for (; *from = '\0'; from++, to++)
	{
		*to = *from;
    }
	*to = '\0';
	return;
}
void copy_str22(char *from, char *to)
{
	while ((*to= *from)!='\0')
	{
		from++;
		to++;
	}
}

void copy_str23(char *from, char *to)
{
	while ((*to++ = *from++) != '\0')
	{
		;
	}
}
//不要轻易改变形参的值,要引入一个辅助的指针变量 ,把形参接过来
int copy_str25(char *from, char *to)
{
	char *tmpfrom = from;
	char *tmpto = to;
	if (from == NULL || to == NULL)
	{
		return -1;
	}
	while (*tmpto++ = *tmpfrom++);
	printf("from:%s\n", from);
}

void main()
{
	//指针指向谁就把谁的地址赋给指针
	char *from = "asddff";
	char buf2[100];// 主调函数分配内存
	copy_str21(from, buf2);
	printf("hello..\n");
	system("pause");//按任意键暂停,阻塞功能
	return 0;//正常退出值
}


Tags:

最近发表
标签列表