【问题标题】:Passing a struct by reference instead of value within void functions在 void 函数中通过引用而不是值传递结构
【发布时间】:2018-07-03 21:40:35
【问题描述】:

我正在尝试使用结构和函数创建联系人列表。目前我的代码可以编译,但结构的成员没有像我试图做的那样在函数之外被修改。这是我的代码(删除了一些长度的行)

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

struct ContactInfo
{
    char fname[50];
    char lname[50];
};

struct ContactInfo gc;

void getContactInfo(struct ContactInfo gc)
{
    printf("First Name: ");
    scanf("%s", gc.fname);

    printf("\nLast Name: ");
    scanf("%s", gc.lname);
}

void showContactInfo(struct ContactInfo gc)
{
    printf("* First Name: %s \n", gc.fname);

    printf("* Last Name: %s \n", gc.lname);
}

int main()
{
    getContactInfo(gc);

    showContactInfo(gc);

    return 0;
}

【问题讨论】:

  • 你在传递价值。通过 ref 是声明中的 foo&(注意“&”)。
  • @DaveS C 没有引用。
  • melpomene 是正确的。如果你想使用“引用”,你必须使函数 void func(struct type * s),然后使用 & 传递你的结构,传递指针
  • scanf %s 从根本上讲是不安全的(您正在将无限输入读取到固定大小的缓冲区中)。此外,使用scanf 进行用户输入通常是个坏主意。考虑改用fgets
  • 函数不返回任何内容的事实与问题没有直接关系。

标签: c pointers data-structures


【解决方案1】:

对于getContactInfo,你需要传递一个指向结构体的指针:

void getContactInfo( struct ContactInfo *gcptr )
{
  printf("First Name: ");
  scanf("%s", gcptr->fname);

  printf("\nLast Name: ");
  scanf("%s", gcptr->lname);
}

由于您尝试修改内容gc,因此您需要将指向它的指针传递给函数。请记住,C 通过值传递所有参数,因此被调用的函数会创建一个单独的、重复的对象来接收参数的值。您的代码正在修改该重复对象,这对实际参数没有影响。

-&gt; 运算符在操作数是指向 structunion 类型的指针时使用 - 它在访问特定成员之前隐式取消引用指针。相当于写了(*gcptr).fname(*gcptr).lname,同时更容易让人眼前一亮。

你可以把这个函数称为

getContactInfo( &gc );

对于showContactInfo,您可以保持原样,因为您不尝试修改参数。但是,很多人喜欢将指针传递给structs 以节省内存(您不会在被调用函数中构建struct 的副本)。如果您想使用指针,我建议您使用 const 关键字,如下所示:

void showContactInfo( const struct ContactInfo *gcptr )
{
  printf("* First Name: %s \n", gcptr->fname);
  printf("* Last Name: %s \n", gcptr->lname);
}

const 关键字告诉编译器,如果我试图修改 gcptr 指向的对象的内容 showContactInfo 函数中的内容。就像上面的getContactInfo,你可以称之为

showContactInfo( &gc );

请注意,我将参数名称更改为gcptr 只是为了帮助区分函数定义中的形式参数和函数调用中的实际参数。我通常不喜欢将任何类型的信息放在变量或参数名称中,但您可以使用任何您喜欢的命名约定。

【讨论】:

    【解决方案2】:

    执行此操作的 C 方法只是普通的旧指针:

    void showContactInfo(struct ContactInfo* gc)
    {
        printf("* First Name: %s \n", gc->fname);
    
        printf("* Last Name: %s \n", gc->lname);
    }
    

    除了必须使用箭头运算符-&gt; 来访问属性之外,它基本上是相同的。

    C 本身没有像 C++ 那样的引用。

    【讨论】:

    • 更重要的是,需要在 getContactInfo() 中进行类似的更改。
    • 确实,对 showContactInfo() 进行更改的主要原因是为了保持一致性。它已经按原样工作了,因为它没有尝试修改结构。
    • 谢谢你们。我做了改变。我需要理解指针。这听起来像是一个愚蠢的问题。我现在如何调用 main 中的函数?
    【解决方案3】:

    getContactInfo 函数应该返回值,但您将值传入。

    从函数中获取数据最自然的方法是使用返回值。此外,您不应该使用全局变量来传递数据。代码可能如下所示:

    struct ContactInfo getContactInfo(void)
    {
        struct ContactInfo g = { 0 };  // ensure no garbage in case input fails
    
        printf("First Name: ");
        scanf("%49s", g.fname);
    
        printf("Last Name: ");
        scanf("%49s", g.lname);
    
        return g;
    }
    

    main 中的代码为:

    struct ContactInfo gc = getContactInfo();
    showContactInfo(gc);
    

    【讨论】:

    • 这种方法需要分配 2 个结构并在它们之间进行复制,如果只执行一次,这很好,但它会更快并且只使用一次分配(并且不复制)来传递指针,或者使用函数 malloc 结构并返回一个指针。
    • @DaveS 请发布一个基准,显示所谓的速度差异。堆栈分配是一条指令,所以如果你能测量它会很有趣。我也怀疑是否存在与返回值关联的副本(例如 x64 ABI 实际上要求没有副本)。我的猜测是 malloc 比这慢几个数量级。
    • showContactInfo 或许可以手动优化以通过指针传递
    • 你在这个例子中是正确的。对不起,如果我的评论太简短以至于无法清楚。我的意思是一般来说,而不是针对这个特定的简单案例。一般来说,alloc-create-alloc-copy 会比 alloc-create 更糟糕,特别是对于更大更复杂的结构,这样的结构甚至可能不适合堆栈。
    猜你喜欢
    • 1970-01-01
    • 2021-09-16
    • 1970-01-01
    • 1970-01-01
    • 2018-08-17
    • 1970-01-01
    • 1970-01-01
    • 2011-10-29
    • 2011-06-28
    相关资源
    最近更新 更多