C 语言解剖课 你真的完全理解函数返回值吗?

小夏 科技 更新 2024-02-05

函数是语言编程的基本单位,每个有意义的函数都必须产生输入和输出行为。 本文的主题不是讨论函数,而是详细剖析我们忽略的函数返回值的特征是什么。

之所以写这个话题,是因为这两天我和公司里几位新入职的程序员聊了聊,他们都是今年刚毕业的大学生,都说基本功扎实,基本功扎实,至少专业课程还是很不错的。

当然,我们必须谈谈语言。 我不知道我是否不说话,但当我谈论它时,我很震惊。 几个人,甚至他们每个人都能分辨出函数的返回值。 在他们的理解中,函数通过返回值返回数据,另一个是通过指针将数据带出来。 让他们再详细说明一下,他们会不知所措。

我问他们输入参数是什么,输出参数是什么,他们都不知道。 原因是没有语法... 这意味着他们肯定不知道什么是语义,什么是语法。

因此,我决定今天就和大家一起分析一下“函数的返回值”,本文应该以本文为起点。

每个人都知道函数的组成:函数名称、返回值类型、参数列表和函数体。 函数通过参数列表输入参数,用 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日,合肥写。 优质作者名单

相似文章

    C 语言解剖课 指针数组和数组指针很愚蠢,分不清?

    本来,在我看来,指针数组 和 数组指针 是两个彼此不相近的知识点。因为前者的 主语 是数组,而后者的 主语 是指针,所以主体是不同的。虽然数组可以看作是被 const 修改的指针 常量型指针 但这只是为了理解,数组是在堆栈上分配的,指针指向的内存一般是动态分配的,更多的是在堆上,使用起来比较仔细和复...

    C和C语言,你知道多少?

    C 和C是软件开发领域广泛使用的两种编程语言。对于那些对计算机编程感兴趣的人来说,理解和掌握这两种语言非常重要,无论你是刚刚开始学习习编程,还是已经是一个有经验的开发人员,了解两者之间的关系对你未来的职业生涯都会有很大的帮助。.C 和C的区别 面向对象与面向过程。C 是一种面向对象的编程语言,支持面...

    元旦假期,你的工作电话真的可以完全静音吗?

    马上就要过元旦了,相信很多朋友都已经放假了。元旦放假三天,忙碌了一年后,可以稍作休息,与亲朋好友共度新年。假期总是一个好时机,因为它们是你自己的休息时间。让我们忙碌一下,停下来享受一下,做一些我们想做的事情。但这个时候,作为工人的你,真的能彻底脱掉工作吗?更具体地说,您的工作电话真的可以完全静音吗?...

    你真的了解维生素C吗

    可以说我们对维生素C很感兴趣 这已经足够熟悉了。但你真的明白吗?维生素也被称为 抗坏血酸是一种水溶性维生素。由营养学会推荐。不同人群的每日维生素C摄入量。 毫克,持续 至 年, 毫克,持续 至 年 岁 毫克, 岁 毫克 怀孕和哺乳期间 毫克 维生素C有什么作用?提高身体的免疫力。维生素C能增强中性粒...

    本文将教你如何在 C 语言中使用浮点数

    在 C 中,float 是一种用于存储浮点数的数据类型,即带有小数部分的数字。以下是使用 float 类型的基本方法 声明浮点变量 float myfloat 声明一个名为 myfloat 的浮点变量。分配 myfloat . 遗嘱 分配给 myfloat 或者与声明同时初始化 float myfl...