Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/64.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么许多C函数使用指针来传递数据,而不是使用;return";?_C_Function - Fatal编程技术网

为什么许多C函数使用指针来传递数据,而不是使用;return";?

为什么许多C函数使用指针来传递数据,而不是使用;return";?,c,function,C,Function,这或多或少是一个关于方法论和基本原理的问题。在为Linux编写各种内核模块时,我被我认为是一种笨拙的设计函数的方式弄糊涂了。例如,要检索给定路径的文件的inode,我必须使用以下命令: struct inode *inode; struct path path; kern_path(path_name, LOOKUP_FOLLOW, &path); inode = path.dentry->d_inode; 为什么不仅仅是一个像这样工作的函数: struct inode inod

这或多或少是一个关于方法论和基本原理的问题。在为Linux编写各种内核模块时,我被我认为是一种笨拙的设计函数的方式弄糊涂了。例如,要检索给定路径的文件的inode,我必须使用以下命令:

struct inode *inode;
struct path path;
kern_path(path_name, LOOKUP_FOLLOW, &path);
inode = path.dentry->d_inode;
为什么不仅仅是一个像这样工作的函数:

struct inode inode;
struct path path = kern_path(path_name, LOOKUP_FOLLOW);
inode = path.dentry->d_inode;

看起来更直观。

这有利于减少来回复制内存块


使用此方法允许调用的函数仅修改传入结构的某些部分,而不复制任何内存。

传入指向用于返回数据的结构的指针有助于传回大量数据,而无需实际传递。你只是来回传递一个指针,而不是一个巨大的结构

您的示例只显示了一个填充的元素。有些驱动程序会传回包含几十个(如果不是超过一百个)字段的结构。这将获得非常昂贵的内存和cpu。您希望从内核中挤出尽可能多的内容

此方法允许您做的另一件事是让内存管理发生在函数之外,使函数更通用和可重用

更不用说,如果你想返回不止一件东西,这几乎是唯一的方法


另一个好处是,它允许您非常清楚地定义与驱动程序的接口。声明一个结构,这就是你的API。没有其他东西可以进出。

你会用它做什么

函数必须能够返回某种错误代码,这样用户才能确保函数成功。有两个明显的选择:

  • 返回错误代码

    这意味着您必须将要返回的另一个值作为参数

  • 返回值

    这意味着您必须将错误代码作为参数


  • 由于在C中只能返回1个值,因此必须将某个值作为参数,因为最终需要向用户返回两个值(错误代码和值)。

    内核是一个具有特殊约束的C程序。特别是,不允许调用堆栈很深(IIRC,限制为4KB)

    当您返回一个
    struct
    时,ABI(请参阅…)命令(除了一些简短的
    struct
    用两个词进行拟合)返回的
    struct
    通过堆栈。因此,这种样式将有利于相当大的堆栈帧,因此更容易满足堆栈限制

    顺便说一句,通常的风格是返回一些整数错误代码,并修改参数指针指向的数据


    在x86-64/Linux上,返回两个标量值(整数、指针、枚举、双精度等)的结构绝对值得,请参阅。

    实际原因比其他答案简单得多:这只是习惯,来自一个不再存在的约束。所有其他的理由都是事后捏造出来的;对于相反的事物,通常有一个相等和相反的理由


    在旧的C编译器中,所有参数和返回值必须能够装入寄存器。这意味着,如果它们较大,则必须通过地址传递。

    好处是,当传递地址时,函数可以对该地址的数据进行操作,并且调用方可以使用更改。当传递不可能的副本时。返回的结构通常通过指针传递,除非它们足够小,可以装入寄存器。除非您的编译器真的很笨,否则不会涉及大内存拷贝。@o11c这完全不相关。即使“智能”编译器可以优化返回值,调用代码也可能需要将结果存储在局部变量中,然后进行复制。而且,很多相关代码都是在编译器明显不够先进的时候编写的,并没有人会因为编译器的改进而改变整个编码模式。最后但并非最不重要的一点是,您的整个假设依赖于完整的源代码,但这不适用于库,这里的情况正是如此(至少对于某些函数)(也请参见Basile的答案-相同的理由系列)…因为许多C函数都是由优秀的C程序员编写的…因为只有很少的函数返回简单数据,异常信息几乎是普遍首选的返回数据。然而,共享内存中的引用信息可能非常庞大,如果引用是样板文件,则通常会被卷积到一个定义的宏中,如
    somestatus=kern\u path(kern\u path\u PARMS)
    。至少在x86-64上,一段时间内限制为16KB。过去几年它一直是8KB,但这还不够(文件系统代码,主要是xfs,很容易溢出,特别是在i/o堆栈深处触发内存回收时)。结构通常在堆栈上。@o11c:但返回结构就是生成它的静默副本,因此您可以(暂时)得到它堆栈上有它的两个副本。@BasileStarynkevitch在任何合适的编译器上都没有。您可能需要担心的唯一一件事是它是否无法解决别名问题。有些人认为,返回值并在参数中传递错误可以更容易地确保您不会忽略错误。当然,有
    \uuuu属性((warn\u unused\u result))
    ,但人们通常太懒了,不想始终如一地使用它。@o11c:Wow,不知道warn\u unused\u result。我已经习惯了围棋中类似的功能。当然,在go中,这是一个错误而不是警告,并且您不能关闭它(这不是可选的)。