Chinaunix首页 | 论坛 | 博客
  • 博客访问: 20537
  • 博文数量: 13
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 159
  • 用 户 组: 普通用户
  • 注册时间: 2013-11-15 08:25
文章分类

全部博文(13)

文章存档

2015年(3)

2014年(2)

2013年(8)

我的朋友

分类: LINUX

2014-02-11 09:39:31

函数返回指针

2011-10-09 13:26 | 分类: | 浏览5695次
我有一个程序是这样的:
int main()
{
	int *p;
	int i;
	int*fun(void);
	p=fun();
	for(i=0;i<3;i++)
	{
		printf("%d\n",*p);
		p++;
	    
	}
	
	return 0;
};
int* fun(void)
{
	static int str[]={1,2,3,4,5};
	 int*q=str;
	return q;
}
我想问一下,除了将str定义为静态区以及用malloc这样的方法外,还有什么好的方法,同时也想问一下如果我改成
int main()
{
	char *p;

	char*fun(void);
	p=fun();
	
	printf("%s\n",p);

	return 0;
};
char * fun(void)
{
	char *str="hello";

	return str;
}
为什么能输出准确值,函数返回指针有哪些方法。
使用字符数组时,就能返回正确值
int main()
{
	char *p;
	char *fun(void);
	p=fun();
	printf("%c",p[2]);
	return 0;
};

char* fun(void)
{
	char str[]={'a','b','c'};
	return str;
}
但若将str[]改成str[]="hello"却不行。
2011-10-09 14:00提问者采纳
您的后一个例子中,虽然str是在区,而该是局部的,函数结束时不保留的。
但是,字符串"hello"不是变量,而是一个,在处理这种时,通常把它放在了区中。而常量区则是始终存在的。
后一个例子中函数fun的返回值就是一个指向这种常量区的指针。

函数返回指针,要使主程序可以使用这个指针来访问有意义的数据,关键就是要保证在使用这个指针值的时候,该指针所指向的地方的数据仍然有意义。
除了您所说的:用静态区以及用这样的方法外,还有就是第二个例子里的:用常量区。再另外,还可以用,也就是说返回的指针是指向的。

还有,如果指针是指向函数的指针,那么这个指针就是指向程序代码区的。这也是一种应用的情况。

另外,如果明白了它的原理,还可以发明出一些其他灵活的使用方法,当然,那都属于“怪”方法,一般不提倡的。 
【总结】
常规程序中,函数返回的指针通常应该是:
(1)指向静态(static)变量;
(2)指向专门申请分配的(如用)空间;
(3)指向常量区(如指向字符串"hello");
(4)指向;
(5)指向程序代码区(如指向函数的指针)。 
除这5项以外,其它怪技巧不提倡。
追问
这里我还是没太明白,比如:
int main()
{
	int a,b;
	int max;
	int fun (int a,int b);	
	scanf("%d%d",&a,&b);
	max=fun (a,b);
	printf("\n%d\n",max);
	return 0;
};
int fun(int a,int b)
{
	int max;
	if(a>b)
		max=a;
	else 
		max=b;
	return max;
}
在这个程序,为什么在fun()函数中就能返回max,按照动态存储原理,在fun结束时,就max就被释放了,不能再返回值了。但事实却不是这样。
回答
这个例子中,返回的不是变量max的地址,返回的是它的值。
return后面的东西,看做一个表达式,返回的是这个表达式的值。
例如,入口如果a是3,b是5,则此时(执行return语句时)max里面存的是5。而return语句的功能就是把max里面的5取出来,放到“返回值寄存器”中。
主程序是从“返回值寄存器”得到这个5的(此时max变量已经不存在了)。

你前面的第二个例子中,同样,指针变量str在函数结束后已经不存在了。但是在return语句中,把指针变量str里面的值(等于字符串"hello"存放处的地址)送到“返回值寄存器”中了。
动态变量str不存在了,但常量区中的字符串"hello"还存在。主程序根据返回的地址就可以找到该字符串。
追问
返回的是值,这个我明白了,但还是有点不明白的是:
int main()
{
	char *p;
	char *fun(void);
	p=fun();
	printf("%x\n",p);
	printf("%s\n",p);
	return 0;
}

char* fun(void)
{
//	char str[]={'a','b','c','d','e','f','\0'};
	char str[]="hello";
	printf("%x\n",str);
	return str;
}
返回的也是str的地址,从结果来看p和str的值相等,但为什么准确,我的qq是121814322,谢谢加我,希望继续请教。
回答
char str[]="hello"; 和 char *str="hello"; 不一样。

char str[]="hello"; 是在动态变量区中开辟了可以容纳6个字符的数组,数组名叫str。同时将字符串"hello"(原存放于常数空间)拷贝到这个数组空间中去作为数组的初始化值。
此时若执行return str; 其中的str是数组名。C语言规定,表达式中如果是数组名,则该表达式的值就等于这个数组的地址。所以返回的是这个数组的地址,请注意:并不是字符串常量"hello"的地址!而函数结束时,虽然常数空间并不破坏,但这个数组空间是破坏了的,而你返回的却不是常数空间里的地址而正是已经破坏了的数组的地址。

而char *str="hello"; 是在动态变量区中开辟了一个可以存放一个指针值的变量,名叫str。同时将原存放于常数空间的字符串"hello"的地址赋给这个指针变量作为初始值。
此时若执行return str; 其中的str是指针变量名。C语言规定,表达式中如果是变量名,则该表达式的值就等于这个变量的值。所以返回的是变量str的值,而变量str的值就等于字符串常量"hello"的地址。而函数结束时,变量str破坏了的,但常数空间中的字符串并不破坏。主程序根据返回的地址就可以找到该字符串。
【再补充】
您最后的【问题补充】中的新例子,用了char str[]={'a','b','c'};据我所知,应该是不行的。

您之所以说试验了能行,有两种可能的原因:
(1)有可能函数返回后虽然数组str的空间已经被释放了,但暂时该空间还没有被新的内容完全覆盖冲掉,所以您用p[2]访问的时候那个位置里原来的字符还在。
(2)有可能具体某个编译程序对这类数组做了特殊处理(例如强行把它做成了静态的)。按标准,具体的编译程序应该保证符合标准的程序能正确运行,但“不符合标准”的程序并不一定要“不能运行”。如果某个编译程序放宽标准使得某些不合标准的程序也出了正确的结果,这个编译程序不算不标准。

如果是第一种原因,那么程序上下文改一改,就可能不成立了;如果是第二个原因,那么换一个C版本,就可能不成立了。
提问者评价
非常感谢,现在终于明白
阅读(317) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~