关于指针作为函数参数问题的拓展讨论


常见问题

指针作为函数参数时,参数是如何传递的
指针作为函数参数时,malloc函数返回动态内存问题

这一类问题我们经常会遇见,不注意的话很容易出错。并且对函数传参不是特别熟悉的话,理解起来很费劲。下面仔细分析一下这一类问题。


一、函数参数传递

在c语言中实参和形参之间的数据传输是单向的“值传递”方式,也就是实参可以影响形参,而形参不能影响实参。指针变量作为参数也不例外,但是可以改变实参指针变量所指向的变量的值。
这一段文字解释对于很多初学者(包括博主自己)来说,是很难理解的,因此,我们只有多分析代码,从具体的代码中去掌握。

1.1普通值作为函数参数

// 普通值传递
#include <stdio.h>

void test(int num)
{
    
    
    num = 5;
    printf("in function test, num = %d\n",num);
}

int main(int argc, char* argv[])
{
    
    
    int num = 9;
    test(num);
    printf("in function main, num = %d\n",num);                                                                                                    
    return 0;
}

在这里插入图片描述
在test函数内部,修改num的值,对实参num并无影响,即test函数接收到的实际上是外层num的一个拷贝。普通值作为函数参数时很容易理解,这里就不废话了。

1.2指针作为函数参数(一级指针)

示例1

//一级指针作为函数参数                                                                                                                             
#include <stdio.h>

void test(char *p) 
{
    
    
    p = "hello world";
    printf("str = %s\n",p);
}

int main()
{
    
    
    char *str = NULL;
    test(str);
    printf("str = %s\n",str);
    return 0;
}

在这里插入图片描述
我们可能会认为主函数输出结果是hello world,但实际上调用函数test并没有改变指针str的值,我们通过框图来分析一下整个代码的运行过程
在这里插入图片描述
主函数中指针str指向null,调用函数test,指针str的值(也就是null的地址)传递给指针string,指针string也指向了null,接着字符串“hello world”的地址传给string,指针string指向了“hello world”的地址,函数调用结束后,“hello world”内存释放。我们发现主函数中指针str始终指向null这个地址,而null这个地址中的内容自始至终没有被修改过。

为了更好的理解,我们打印一些信息:

#include <stdio.h>

void test(char *string)
{
    
    
    printf("string操作之前的指针:%p\n",string);
    string = "hello world";
    printf("string操作之后的指针:%p\n",string);
    printf("string = %s\n",string);
}

int main()
{
    
                                                                                                                                                      
    char *str = NULL;
    printf("传入test函数之前str的指针:%p\n",str);
    test(str);
    printf("传入test函数之后str的指针:%p\n",str);
    printf("str = %s\n",str);
    return 0;
}

在这里插入图片描述
示例2

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

void test(char *string)
{
    
    
    char *testString = "world";

    printf("222 string addr:%p,   testString addr:%p\n",string,testString);

    string = testString;
    printf("333 string addr:%p,   testString addr:%p\n",string,testString);
}

int main(void)
{
    
    
    char *str ="china";
    printf("111 str addr:   %p,   str is:%s\n",str,str);

    test(str);
    printf("444 str addr:   %p,   str is:%s\n",str,str);
    
    return 0;
}  

在这里插入图片描述
框图表示代码运行过程
在这里插入图片描述
我们将上面代码修改一下:

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

void test(char *string)
{
    
    
    printf("222 addr:%p, content:%s\n",string,string);
    strcpy(string,"world");
    printf("333 addr:%p, content:%s\n",string,string);  
}

int main(void)
{
    
    
    char *str ="china";
    printf("111 addr:%p, content:%s\n",str,str);

    test(str);
    printf("444 addr:%p, content:%s\n",str,str);
        
    return 0;
} 

在这里插入图片描述
分析不难发现是strcpy函数那里发生了非法内存访问,造成了段错误,原因是因为主函数中指针str指向了一个字符串常量“China”,它位于代码段(text段),因此,调用函数时,尝试去修改它是不允许的,会产生段错误。

那么,我们再次进行修改:

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

void test(char *string)
{
    
    
    printf("222 addr:%p, content:%s\n",string,string);
    strcpy(string,"world");
    printf("333 addr:%p, content:%s\n",string,string);  
}

int main(void)
{
    
    
    char str[] ="china";  //定义为字符数组
    printf("111 addr:%p, content:%s\n",str,str);

    test(str);
    printf("444 addr:%p, content:%s\n",str,str);
        
    return 0;
} 

在这里插入图片描述
我们发现主函数中指针str的内容被修改了,这是怎么回事呢?结合前面的分析框图,这里应该自己可以分析理解了。

总结:
函数的参数是指针时,在被调用的函数中
1.改变指针的值,不会改变实参的值。
2.改变指针指向的内容时,会改变实参的值。
注意:在传递的过程中如果是const类型的指针是不允许修改的。

1.3返回指针和二级指针

那么我们要改变主函数中str指针指向的地址应该怎么办呢?

两种办法
1.在函数内部对指针进行操作后 ,返回该指针
2.二级指针或指针的引用传递(c++)

返回指针的方法:

#include <stdio.h>

char *test(char *string)
{
    
    
    string = "hello world";
    printf("string addr:%p,      string = %s\n",string,string);
    return string;
}

int main()
{
    
    
    char *str = NULL;
    printf("str    addr:%p,      str    = %s\n",str,str);                                                                                          

    str = test(str);
    printf("str    addr:%p,      str    = %s\n",str,str);
    return 0;
}

在这里插入图片描述

二级指针:

#include <stdio.h>

void test(char **string)
{
    
    
    *string = "hello world";
}

int main()
{
    
    
    char *str = NULL;
    test(&str);
    printf("str=%s\n",str);
    return 0;
} 

在这里插入图片描述
具体过程见:C语言指针作为函数参数传递学习(一)

二、malloc返回动态内存问题

我们先来看两段代码(实际上是相同的代码)

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

void getmemory(char *p) 
{
    
    
	p = (char *) malloc(100);
	strcpy(p,"hello world");
}

int main( )
{
    
    
	char *str = NULL;
	getmemory(str);
	printf("str = %s\n",str);
  
  	free(str);
  	return 0;
} 

在这里插入图片描述

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

void getMemory(char *p,int num)
{
    
    
    p = (char*)malloc(num);
}

int main()
{
    
    
    char *str = NULL;
    getMemory(str,100);
    strcpy(str,"hello");

    printf("str = %s",str);
    return 0;
} 

在这里插入图片描述
我们发现上面两段代码执行时都会发生段错误。上面一段代码是因为getmemory函数中申请的动态内存没有返回给str,free函数对str操作出现了段错误;下面一段代码也是因为getmemory函数中申请的动态内存没有返回给str,strcpy函数操作出现了非法内存访问,造成段错误。两段代码段错误的本质是一样的,分析过前面的几段代码之后,我们就很容易理解问题所在了。

void getMemory(char * p, int num)中的指针p实际上是主函数中str的一个副本,编译器总是要为函数的每个参数制作临时副本。在本例中,p申请了新的内存,只是把p所指向的内存地址改变了,但是str丝毫未变。因为函数getMemory没有返回值,因此str并不指向p所申请的那段内存,所以函数getMemory并不能输出任何东西,如下框图所示:
在这里插入图片描述

如果一定要用指针参数去申请内存,那么应该采用指向指针的指针,传str的地址给函数getMemory。代码如下:

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

void getMemory(char **p,int num)
{
    
    
	*p = (char*)malloc(num);
}

int main()
{
    
    
	char *str = NULL;
	getMemory(&str,100);
	strcpy(str,"hello");
	printf("str = %s\n",str);
	return 0;
}

在这里插入图片描述
由于“指向指针的指针”这个概念不容易理解,我们可以用函数返回值来传递动态内存。这种方法更简单,代码如下:

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

char* getMemory(char *p, int num)
{
    
    
      p = (char*)malloc(sizeof(char) * num);
      return p;
}

int main()
{
    
    
      char *str = NULL;
      str = getMemory(str, 100);
      strcpy(str, "hello");

      printf("str = %s\n",str);                                                                                                                    
      return 0;
}

在这里插入图片描述

三、致谢

参考博文:
C语言指针作为函数参数传递学习(一)
传递动态内存时的若干问题
感谢以上两位博主提供的帮助,读者如有疑问,可参考以上两篇博客。

おすすめ

転載: blog.csdn.net/little_rookie__/article/details/110312398