指针传参深入研究

1 关于C语言指针传参问题深入研究的结论

1.普通指针变量传参
当传入指针变量到另一个函数时,形参只是起到拷贝的作用,如果在形参上对这个指针变量动手脚,是改变不了这个指针变量所指向的内容,因为你地址都变了,看代码:

#include<stdio.h>
#include<string.h>
#include<malloc.h>

void InitP(char *p);

int main()
{
	char *p = NULL;
	p = (char *)malloc(50);
	if(!p)
	{
		return -1;
	}
	InitP(p);
	printf("p = %s\n",p);
	return 0;
}


void InitP(char *p)
{
	p = "sdfsds";
	
}

运行结果如下:

gec@ubuntu:826$ gcc text2.c  -o text2
gec@ubuntu:826$ ./text2
P主函数(p) = 0x8c18008
P(sdfsds) = 0x80485df
P传入初始化(p) = 0x80485df
P初始化之后主函数(p) = 0x8c18008
p = 

解析:在这里可以看到,主函数里面的p的地址并没有改变!随便你在形参里面怎么对指针变量动手脚,都是没用的,要想这样改变指针变量指向的内容,有两种办法:
1.1 传入指针变量,返回被改变的指针变量
代码如下:

int main()
{
	char *p = NULL;
	p = (char *)malloc(50);
	if(!p)
	{
		return -1;
	}
	printf("P主函数(p) = %p\n",p);
	p = InitP(p);
	printf("P初始化之后主函数(p) = %p\n",p);
	printf("p = %s\n",p);
	return 0;
}


char *InitP(char *p)
{
	p = "sdfsds";
	printf("P(sdfsds) = %p\n","sdfsds");
	printf("P传入初始化(p) = %p\n",p);
	return p;
}

解析:这一种很好解析,你把改变了的指针变量指向的地址返回给主函数的指针变量接收,这当然可以改变初始的指针变量所指向的内容。
运行结果如下

gec@ubuntu:826$ gcc text2.c  -o text2
gec@ubuntu:826$ ./text2
P主函数(p) = 0x9b82008
P(sdfsds) = 0x80485df
P传入初始化(p) = 0x80485df
P初始化之后主函数(p) = 0x80485df
p = sdfsds

1.2 传入指针变量的地址
代码如下:

int main()
{
	char *p = NULL;
	p = (char *)malloc(50);
	if(!p)
	{
		return -1;
	}
	printf("P主函数(p) = %p\n",p);
	InitP(&p);
	printf("P初始化之后主函数(p) = %p\n",p);
	printf("p = %s\n",p);
	return 0;
}

void InitP(char **p)
{
	if(*p == NULL)
	{
		assert(0);
	}
	*p = "sdfsds";
	printf("P(sdfsds) = %p\n","sdfsds");
	printf("P传入初始化(p) = %p\n",p);
}

解析:首先要弄清楚一点

char *p;
p的地址与&p的地址之间的联系;
是没有联系的!
p根据指针的定义,表示存放的是数据对象,比如变量的地址或者数组名,而&p是指针变量本身自己的地址,该地址是存放在二级指针变量中

所以,你既然把指针变量本身的地址传出去了,别人就可以对你这块地址进行操作,操作就是解引用呀,初始化它之后,就不用传入主函数,多此一举。
总结:指针变量的本身传入到其它函数其内容是不能被改变的,指针变量的地址传入到其它函数其内容是能被改变的。当你的程序模块多时,指针又传入了很多函数,要是不注意这一点,很容易出现问题!

2 关于结构体指针变量传参问题深入研究的结论

2.1 结构体指针变量传参
前面1.1已经说的很清楚了,直接看代码:

#include<stdio.h>
#include<string.h>
#include<malloc.h>
#include<assert.h>

typedef struct student{
	int age;
	char *name;
}STUDENT;

void GetMallocMen(STUDENT **info);
void InitStudent(STUDENT *info);
void PrintfStudent(STUDENT *info);

int main()
{
	STUDENT *info = NULL;
	GetMallocMen(&info);
	printf("主函数前:P(info) = %p\n",info);
	printf("主函数前:&P(info) = %p\n",&info);
	printf("主函数前:P(info->age) = %p\n",&info->age);
	printf("主函数前:P(info->name) = %p\n",info->name);
	printf("主函数前:P&(info->name) = %p\n",&info->name);
	InitStudent(info);
	printf("主函数后:P(info) = %p\n",info);
	printf("主函数后:&P(info) = %p\n",&info);
	printf("主函数后:P(info->age) = %p\n",&info->age);
	printf("主函数后:P(info->name) = %p\n",info->name);
	printf("主函数后:P&(info->name) = %p\n",&info->name);
	PrintfStudent(info);
	return 0;
}

void GetMallocMen(STUDENT **info)
{
	if(*info)
	{
		free(*info);
	}
	*info = (STUDENT *)malloc(sizeof(STUDENT));
	(*info)->name = (char *)malloc(16);

	if(*info == NULL || (*info)->name == NULL)
	{
		assert(0);
	}
	else
	{
		printf("申请成功!\n");
	}
}

void InitStudent(STUDENT *info)
{
	if(info == NULL)
	{
		printf("没有空间1!\n");
		assert(0);
	}
	printf("初始化之前:P(info->name) = %p\n",info->name);
	printf("初始化之前:P(info->age) = %p\n",&info->age);
	info->age = 10;
	info->name = "zhangsan";
	printf("P(zhangsan) = %p\n","zhangsan");
	printf("初始化之后:P(info->name) = %p\n",info->name);
	printf("初始化之后:P(info->age) = %p\n",&info->age);
}


void PrintfStudent(STUDENT *info)
{
		if(info == NULL)
	{
		printf("没有空间2!\n");
		assert(0);
	}
	printf("info->age = %d\n",info->age);
	printf("info->name = %s\n",info->name);
}

运行结果如下:

gec@ubuntu:826$ gcc text3.c  -o text3
gec@ubuntu:826$ ./text3
申请成功!
主函数前:P(info) = 0x8664008
主函数前:&P(info) = 0xbff281fc
主函数前:P(info->age) = 0x8664008
主函数前:P(info->name) = 0x8664018
主函数前:P&(info->name) = 0x866400c
初始化之前:P(info->name) = 0x80488e8
初始化之前:P(info->age) = 0x8664008
P(zhangsan) = 0x8048a0d
初始化之后:P(info->name) = 0x8048a0d
初始化之后:P(info->age) = 0x8664008
主函数后:P(info) = 0x8664008
主函数后:&P(info) = 0xbff281fc
主函数后:P(info->age) = 0x8664008
主函数后:P(info->name) = 0x8048a0d
主函数后:P&(info->name) = 0x866400c
info->age = 10
info->name = zhangsan



解析这个之前先说一下结构体成员的地址是怎么存放的。在给结构体指针变量申请内存是注意,申请时,由外到内,释放时,由里到外,这个不解析了。在此函数我给结构体变量中的成员指针变量申请了16个空间。这16个空间是这样分配的:
int age的地址空间是:0X08~0X0B四个字节
char *name 的地址空间是:0X0C~0X1C

0x08090A0B0C0D0E101112131415161718191A1B1C
age&namename

解析:因此结构体中的成员地址是连续的。前面age的地址没什么好解析的,&name的地址也没什么要解析的,看name的地址,为什么在0X18,这个你就要去问栈区系统是怎么给他分配的,因为你只要定义了一个变量,这个变量就已经被系统分配了空间,四个字节,因为在32位的操作系统下指针都是4个字节,哪有人会问了,我申请语句是怎样写的呀,(*info)->name = (char *)malloc(16);为什么不是name是有16个字节空间,首先你要知道malloc是什么意思,它是指给你分配了16个地址空间,只是用name来表示,你如果用(*info)->&name = (char *)malloc(16);这样来申请,就错了,&name前面有解析到,它是存储在二级指针变量中。
回归到这个程序:为什么前面讲的是要想改变指针变量指向的内容,要么就返回指针变量返回来的值要么就传指针变量的地址去操作,那这个结构体变量为什么传的是指针变量就改变里面的内容了呢?
解析如下:

在主函数里可以看到我传的的确是指针变量并非是指针变量的地址,InitStudent(info);然后到了初始化函数,结构体指针变量指向内容被改变了。这个我仔细研究了很久,最后发现是这样的:
我申请好了内存传入结构体指针变量,此时我在初始化函数里面,并没有对这个结构体指针变量进行操作,我初始化的是结构体成员,那么又有人会问了,结构体成员中含有指针,这还是你间接传了指针变量过去,没错,是传了指针变量过去,但是,在传这个name指针变量之前,name并没有初始化!name == NULL,malloc等这都没有这些操作,因此我可以把它传到别的函数进行第一次应用的时候,其里面的值就发生了变化!在对name进行赋值的时候info->name = “zhangsan”;意思是,在name的地址空间(&name)写入了“zhangsan”,这个时候&name才开始有数据,数据为“zhangsan”,而name本身的地址发生了变化和“zhangsan”地址一样了,因为name是指针变量,当然可以改变,但是&name是改变不了的!你分配在堆区了。

2.2 结构体变量指针的地址传参

直接看代码:

#include<stdio.h>
#include<string.h>
#include<malloc.h>
#include<assert.h>

typedef struct student{
	int age;
	char *name;
}STUDENT;

void GetMallocMen(STUDENT **info);
void InitStudent(STUDENT **info);
void PrintfStudent(STUDENT *info);

int main()
{
	STUDENT *info = NULL;
	GetMallocMen(&info);
	printf("主函数前:P(info) = %p\n",info);
	printf("主函数前:&P(info) = %p\n",&info);
	printf("主函数前:P(info->age) = %p\n",&info->age);
	printf("主函数前:P(info->name) = %p\n",info->name);
	printf("主函数前:P&(info->name) = %p\n",&info->name);
	InitStudent(&info);
	printf("主函数后:P(info) = %p\n",info);
	printf("主函数后:&P(info) = %p\n",&info);
	printf("主函数后:P(info->age) = %p\n",&info->age);
	printf("主函数后:P(info->name) = %p\n",info->name);
	printf("主函数后:P&(info->name) = %p\n",&info->name);
	PrintfStudent(info);
	return 0;
}

void GetMallocMen(STUDENT **info)
{
	if(*info)
	{
		free(*info);
	}
	*info = (STUDENT *)malloc(sizeof(STUDENT));
	(*info)->name = (char *)malloc(16);

	if(*info == NULL || (*info)->name == NULL)
	{
		assert(0);
	}
	else
	{
		printf("申请成功!\n");
	}
}

void InitStudent(STUDENT **info)
{
	if(*info == NULL)
	{
		printf("没有空间1!\n");
		assert(0);
	}
	
	printf("初始化之前:P(info->name) = %p\n",(*info)->name);
	printf("初始化之前:P(info->age) = %p\n",&(*info)->age);
	printf("主函数前:P&(info->name) = %p\n",&(*info)->name);
	(*info)->age = 10;
	(*info)->name = "zhangsan";
	printf("P(zhangsan) = %p\n","zhangsan");
}


void PrintfStudent(STUDENT *info)
{
		if(info == NULL)
	{
		printf("没有空间2!\n");
		assert(0);
	}
	printf("info->age = %d\n",info->age);
	printf("info->name = %s\n",info->name);
}

结果如下:

gec@ubuntu:826$ gcc text3.c  -o text3
gec@ubuntu:826$ ./text3
申请成功!
主函数前:P(info) = 0x850a008
主函数前:&P(info) = 0xbfa01bec
主函数前:P(info->age) = 0x850a008
主函数前:P(info->name) = 0x850a018
主函数前:P&(info->name) = 0x850a00c
初始化之前:P(info->name) = 0x850a018
初始化之前:P(info->age) = 0x850a008
主函数前:P&(info->name) = 0x850a00c
P(zhangsan) = 0x80489f9
主函数后:P(info) = 0x850a008
主函数后:&P(info) = 0xbfa01bec
主函数后:P(info->age) = 0x850a008
主函数后:P(info->name) = 0x80489f9
主函数后:P&(info->name) = 0x850a00c
info->age = 10
info->name = zhangsan


这个不做太多解析,1.2讲的很清楚了。
总结:关于这两种的灵活应用,要特别的注意,要不然程序产生了错误,你都找不到的,要对指针地址要有深入地了解,一定要注意指针的初始化,是否已经初始化了,是否一直没用这个指针,成为一个野指针,是否访问了其它的空间造成段错误等等。
假如2.1中name已经申请了空间再把name == NULL;置为空了再传入初始化函数,程序会怎样?
如果你看懂这篇文章了,这个问题你一定能知道。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值