函数是语言编程的基本单位,每个有意义的函数都必须产生输入和输出行为。 本文的主题不是讨论函数,而是详细剖析我们忽略的函数返回值的特征是什么。
之所以写这个话题,是因为这两天我和公司里几位新入职的程序员聊了聊,他们都是今年刚毕业的大学生,都说基本功扎实,基本功扎实,至少专业课程还是很不错的。
当然,我们必须谈谈语言。 我不知道我是否不说话,但当我谈论它时,我很震惊。 几个人,甚至他们每个人都能分辨出函数的返回值。 在他们的理解中,函数通过返回值返回数据,另一个是通过指针将数据带出来。 让他们再详细说明一下,他们会不知所措。
我问他们输入参数是什么,输出参数是什么,他们都不知道。 原因是没有语法... 这意味着他们肯定不知道什么是语义,什么是语法。
因此,我决定今天就和大家一起分析一下“函数的返回值”,本文应该以本文为起点。
每个人都知道函数的组成:函数名称、返回值类型、参数列表和函数体。 函数通过参数列表输入参数,用 return 语句返回数据,或者从参数列表中的输出参数返回数据。
传入函数体数据的参数称为输入参数,传出函数数据的参数称为输出参数。 让我们从返回值开始。
如果返回值是数据本身,或者是某个特定的值,即值类型,那么我们都知道这个数据实际上是函数主体中数据的副本。
当函数被执行时,函数对象因为被分配在堆栈上而被自动销毁,所以任务完成,函数中数据使用的内存空间会被释放并重新分配。 因此,在销毁之前,会制作一个副本并返回给调用方使用。 (我将在下一篇文章中详细剖析内存布局,堆栈和堆会很清楚)。
返回值类型可以是单值数据,例如基于字符的变量、整数变量或实数变量。
char foo()
char c='a';
return c;
int bar()
int x = 3;
return x;
这两个函数分别返回两个单值类型:字符和整数。 它也可以是多值数据,例如结构。 一个结构可以包含多个成员变量。 例如:
返回多个值。
typedef struct ;
return many;
请注意,字符串不是值类型,而是下面描述的“指针类型”。 数组也不是值类型,数组本质上是一个“常量指针”(不是常量指针),例如整数数组
int array =;
它实际上是这样的:
int * const array = (int)
这里的数组不能再由其他数组分配,即它不能如下所示:
int other_array = ;
array = other_array;//error
想要了解详情,在“段玉河语”账号里有关于常量指针和指针常量深度分析的文章。 正如你所看到的,数组也是一种“指针类型”。
如果返回的数据是指针类型数据,则在函数执行结束时销毁函数之前,指针类型数据的副本仍将传递给调用方。
请注意,这是大多数初学者反复掉入坑的地方。
再次:在函数执行结束时,像值类型一样,会生成一个要返回的数据副本并传递给调用者,但这个数据(或副本)是指针类型,它的值存储在实值类型数据所在的内存地址中(假设指针是一级指针), 注意,指针指向的值类型变量没有“copy”,只有返回数据会生成副本,返回数据是指针。
例如:char* foo();
return array;
int* pointer_multiple_value_2()
return ptr;
当函数执行时,数据也会被销毁,当调用方通过获取的地址访问地址所在的内存数据时,会造成异常。 结果如下(环境与 c11):
我们再来看一个看似正常的情况:
typedef struct ;
return &many;
many* pointer_multiple_value_4()
return many;
这两个函数的返回值都是结构指针,一个指向单个结构变量,另一个指向一个结构数组,函数执行后会像函数一样被销毁,导致返回的结构指针指向的内存块行为未定义,程序有崩溃的风险。
这个**看似运行正常,但实际上很容易让他崩溃,所以暂时不扩大。
所以,我们只需要做这个值类型,当函数被销毁时,它仍然活着,而不是一起销毁。
如果是字符串常量,则存储在只读区域,当函数被销毁时,它仍然存在,可以继续访问。
char* foo();
return many;
此函数分配结构变量的空间,并使用复合文本分配值。 下面是一个截图:
请注意,在这个**截图中,在 main 函数中,在程序退出之前,我使用了 free 函数释放了函数 foo 中的许多指针。
这一步非常重要,在程序结束之前,一定要通过free功能释放堆上的空间,否则会出现“内存泄漏”或“内存碎片”、“百搭指针”等可怕的问题。
到目前为止,我们只对“返回值”的数据类型进行了分析和剖析,并没有将其与参数列表的特性相结合。
在下一篇文章中,我们将对函数的参数列表进行深入分析,例如“输入参数”的特征、“输出参数”的特征,以及如何为传入和传出数据定义一些参数。
研究参数列表将使我们编写的函数更加安全可靠,并且 5 分钟的编码天数和 5 小时的 bug 将显着减少。
段誉,2024年2月1日,合肥写。 优质作者名单