原文

引入:

  • 自定义函数的使用,使整个程序模块化。
  • 每个函数都解决一个小问题,当我们编写好函数后,就把精力转移到,函数之间的逻辑关系上。不必再纠结每一条语句,这样大大提高了编程的效率。
  • 当需要实现相同功能时,可以将函数多次调用,提升代码的复用性。
  • 同时在程序出现问题时,可以快速定位,解决问题。

不过对于函数的参数调用时常会出现问题。


  • 1.使用中出现的两种情况

  • 主函数的值没有被修改(变量的传递)

#include < stdio.h >
int p(int a) {
a = 20;
return a;
}
int main(void) {
int a;
a = 10;
p(a);
printf("%d", a); //这里的a值没有被修改,依然是10
return 0;
}

这里的自定义函数p,接收一个int类型的参数,并定义为a。

虽然自定义函数和主函数的变量名,都为a,但是这里的a是两个变量。

虽然函数也将a返回了,但是主函数并没有接收,忽略了p的返回值。

所以主函数的a依然是10

  • 主函数的值被修改了(指针的传递)
#include < stdio.h >
void p(int * w) { * w = 20;
}
int main(void) {
int a;
int * s = & a;
p(s);
printf("%d", a);
return 0;
  }


这里的自定义函数p,接收一个int类型的指针,命名为w。

虽然这里的指针w,和主函数的指针s,名称不一样,但是他们两个都指向了内存中a的地址。

通过指针的解引用,在内存中修改相关数值。

主函数再次访问a时,就已经是修改过的值,即使自定义函数没有返回值,a的值已经改变。

所以主函数的打印的结果为20


  • 2.从内存的角度分析上面的两种情况

  • 程序运行过程当中,所有的变量都是在内存中存储的

  • 当自定义函数创建时,定义的形参,都会分配新的空间(即使参数名称相同)

  • 新空间存储的内容,为上一级函数传入的内容。

    • 传入变量值时,新空间记录传入值。

    主函数实参地址存:10 / 自定义形参地址存:10
    两个地址存的数值相同,但两者没有联系,修改形参的值,实参值并不会修改

    • 传入变量指针时,新空间记录指针地址

    主函数变量地址存:10
    主函数指针指向:变量地址 自定义指针指向:变量参地址
    主函数指针的地址,和自定义函数指针的地址虽然不同
    但两者都指向了需要修改的内容,通过指针修改内存的值。


  • 3.不同的应用场景

函数传递参数

  • 一般用于数字计算,不使用指针,防止无意间修改内存的值
  • 思路清晰,更好理解

函数传递指针

  • 需要修改多个上级函数的值
  • 需要跨函数修改内容(两个函数之间没有调用关系)
  • 结构体等内存较大的传递,使用指针缩短传递时间

  • 4.函数参数的几种错误

自定义函数创建的变量,返回指针参数

#include<stdio.h>
int *creat(void)
{
    int a;
    int *p = &a;//应该为:static int *p = &a;
    a=20;
    return p;
}

int main(void)
{
    int *place;
    place=creat();
    printf("%d",*place);

    return 0;
}

上面的程序运行时会出现错误,因为自定函数中创建的参数,存储期只有自定义函数运行的这段时间,当自定义函数结束后,自定义函数中声明的变量会被释放。自定义函数返回的指针所指向的地址也就没有意义了。这是用主函数解引用指针会出现错误。

  • 修改建议:
    • 返回变量值
    • 使用 static 将自定义函数存储在静态内存中,保证在程序退出前不被清除

字符串变量名作为参数,原字符串会被修改

#include<stdio.h>
void pass(char *pa)
{
    pa[1]='a';
    printf("%s",pa);
}

int main(void)
{
    char list[10]="Humphrey";
    pass(list);
    printf("%s",list);

    return 0;
}

上面程序,自定义函数虽然没有返回值,但主函数中list被无意间修改了。

这是由于字符串是以指针传入函数,自定义函数是在原始地址上进行修改。

主函数再次访问就会出现修改过的结果。

  • 修改建议:在传入函数前新建字符串副本,将副本传入函数,通过对副本的操作,实现功能。这样就不会出现原始字符串被修改的情况。

修改后的代码:

#include<stdio.h>
#include<string.h>
void pass(char *pa)
{
    pa[1]='a';
    printf("%s",pa);
}

int main(void)
{
    char list[10]="Humphrey";
    char mid[10];
    strcpy(mid,list);
    pass(mid);
    printf("%s",list);

    return 0;
}