C语⾔⼩结-输⼊型参数和输出型参数
1、函数为什么需要传参和返回值
(1) 函数的作⽤就是处理数据,传⼊的参数就是被加⼯的数据,返回值就是输出的结果。
(2)函数名就是这个函数的⼊⼝,在内存中表⽰就是⼀个函数代码段的⾸地址,实质是⼀个指针常量,所以在程序中使⽤函数名都是当地址来⽤的,⽤来调⽤这个函数。再理解指针函数的实质就⽐较简单了。
(3) 如果⼀个函数没有传参和返回值,也是可以的,也可以正常运⾏,那么没有返回值和形参的函数怎么运⾏呢?为什么会有这种函数呢?其实这种类型的函数在C语⾔中有很多。主要有两个作⽤:1) 在系统初始化的时候,我们做⼀些系统的配置⼯作,这种类型的函数都是⼀些配置函数,不需要形参。2)我们处理⼀些全局变量的数据时,会⽤到这种类型的函数。
2、输⼊型参数和输出型参数
我们举个例⼦来说明⼀下什么是输⼊型参数,什么是输出型参数。
int func(int a , int *p)
{
*p = a +10;
return0;
}
int main()
{
int x , y ;
x = 10;
func(x,&y);
printf("x = %d \n", x);
printf("y = %d \n", y);
return0;
指针函数的作用}
运⾏结果:
root@ubuntu:/mnt/hgfs/share/code/c_advance/pointer# ./a.out
x = 10
y = 20
结论:
(1) func函数中a是输⼊型参数,p是输出型参数。
(2) 形参的传值调⽤是输⼊型参数。传址调⽤即有可能是输出型参数,也有可能是输⼊型参数。作为输⼊型参数使⽤时,⼀般会加⼊const关键字,表明是只读,不能修改。
2018.3.7更新
今天⼜发现了⼀个有趣的现象。事情是这样的:我需要申请⼀块内存空间,然后使⽤strcpy来拷贝⼀个字符串,最后printf打印,直接上代码:
{
printf("GetMemory\n");
p = (char *)malloc(100);
printf("GetMemory: %p\n", p);
}
int main(void)
{
char *str = NULL;
printf("&str = %p\n",&str); //0xbfc6f498
printf("str = %p\n\n",str); //(nil)
//GetMemory(str);
GetMemory2(&str);
strcpy(str, "hello world \n");
printf("str = %p\n",str);
printf("&str = %p\n",&str);
printf("%s",str);
//free(str);
return0;
}
运⾏结果如下:
void@ubuntu:/mnt/hgfs/VMshare/code/c$ ./a.out
&str = 0xbfce7718
str = (nil)
GetMemory
GetMemory:0x81ab410
Segmentation fault (core dumped)
结果分析:报了段错误,为什么呢? char *str;是⼀个局部变量,我们传⼊局部变量然后还想要修改该局部变量的值,这是不科学的,也是不合理的。这种虽然也是传址调⽤,但是本质上还是传值调⽤。说的有点绕,需要仔细体会下。 这种传递和上边的传⼊变量x的效果是⼀样的。
那么怎么改呢?
两个思路:
1、将char *str = NULL;定义为全局变量,这样我们是把地址空间传递给了⼀个全局变量,这样函数返回的时候地址空间还在。已验证,就不贴代码和运⾏结果了。
2、我们使⽤⼆维指针来试⼀下,直接上代码:
{
*p = (char *)malloc(100);
printf("p = %p \n", p);
printf("*p = %p \n", *p);
}
int main(void)
{
char *str = NULL;
printf("main:");
printf("&str = %p\n",&str); //0xbfc6f498
printf("str = %p\n\n",str); //(nil)
printf("\n");
//GetMemory(str);
GetMemory2(&str);
strcpy(str, "hello world \n");
printf("str = %p\n",str);
printf("&str = %p\n",&str);
printf("%s",str);
//free(str);
return0;
}
运⾏结果如下:
void@ubuntu:/mnt/hgfs/VMshare/code/c$ ./a.out
main:
&str = 0xbff03f88
str = (nil)
GetMemory2
&p = 0xbff03f70
p = 0xbff03f88
*p = 0x8c6d410
str = 0x8c6d410
&str = 0xbff03f88
hello world
满⾜了我们的要求。
3、总结
函数在传参的时候,其实在栈空间⼜复制了⼀份,⽐如我们定义函数:int fun(int x),当我们在调⽤fun(10);的时候,其实编译器内部操作可以看为两步:int x ; x = 10;,且在调⽤完成后释放该函数申请的变量。我们再看⼀下这个函数:void GetMemory(char *p),在被调⽤的时候,函数内部申请了⼀个char *p的变量,所以我们把这个变量的值是传递不到调⽤的函数内部的。
我们定义:void GetMemory2(char **p),虽然它内部也定义了⼀个变量char **p = &str,我们看到被分配了地址空间:0xbff03f70。但是*p指向了malloc申请出来的内存空间。所以我们的str也指向了malloc申请的内存空间。当函数调⽤结束,**p被释放,但是str指向的内存空间的地址确没有改变,所以就可以正常调⽤了。换句话说,我申请了⼀个⼆重指针指向你,然后修改了你的指向(本来指向null,后来指向malloc申请出来的空间),然后我申请的⼆重指针被释放掉,但是你的指向还在。
引申:其实传址调⽤和C++语⾔的引⽤&很相似,但⼜有⼀点不同(传参调⽤本⾝也申请了变量),但是本质都是⼀样的,我⽤多个指针变量指向同⼀块内存,这个指针修改完被释放,但是还有其他指针
指向该内存,以此来达到间接修改的⽬的。在Python中,我们也可以使⽤多个变量来指向同⼀个对象,当没有变量指向这个对象的时候,对象空间就会被⾃动回收。
最后,C语⾔是⼀门博⼤精深的语⾔,需要我们认真体会。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。