在C语言中如何通过引用传递结构体

41

这段代码是否正确?它可以按预期运行,但是这段代码是否正确地使用了指针和结构体的点符号表示法?

struct someStruct {
 unsigned int total;
};

int test(struct someStruct* state) {
 state->total = 4;
}

int main () {
 struct someStruct s;
 s.total = 5;
 test(&s);
 printf("\ns.total = %d\n", s.total);
}

3
为什么测试函数应该返回一个整数?实际上,这个函数并没有返回任何值。 - user418748
C 有引用吗?惊讶... - Benjamin Bannier
5
在C++出现之前,指针是一种“引用”(因为它们具有“所引用的对象”)。现在它们有一个“指向”或类似的无意义术语。由于someStruct具有值语义,在C++术语中,“按引用传递”(在C中不这样做)与“通过值传递引用”完全相同(它确实这样做,所谓的“引用”是指针)。 - Steve Jessop
C++中使用引用(用&符号声明)的优点在于:1)使用C++引用更不容易出现错误,2)使用C++引用编写的代码更有可能启用编译器进行别名优化。由于Fortran中不存在别名问题,未经优化的指针例程在C中可能会导致尴尬的情况。 - mda
5个回答

67

你使用指针和点符号的方法很好。如果有问题,编译器应该会给出错误和/或警告。

这里是你的代码副本,其中包含一些额外的注释和需要考虑的内容,如结构体、指针、函数和变量作用域。

注意:下面原始示例中代码编写的一个不同之处是,在函数定义/声明中在结构体名称后和星号之前放置了一个空格,如struct someStruct *p1;,而 OP 在星号后放置了一个空格,如struct someStruct* p1;。编译器没有区别,只是对程序员来说一种阅读和习惯上的区别。我喜欢把星号放在变量名旁边,以明确星号改变其旁边的变量名。如果我在声明或定义中有多个变量,这尤其重要。编写struct someStruct *p1,*p2,var1;将创建两个指针p1p2,以及一个变量var1。编写struct someStruct* p1,p2,var1;将创建单个指针p1和两个变量p2var1

// Define the new variable type which is a struct.
// This definition must be visible to any function that is accessing the
// members of a variable of this type.
struct someStruct {
    unsigned int total;
};

/*
 * Modifies the struct that exists in the calling function.
 *   Function test() takes a pointer to a struct someStruct variable
 *   so that any modifications to the variable made in the function test()
 *   will be to the variable pointed to.
 *   A pointer contains the address of a variable and is not the variable iteself.
 *   This allows the function test() to modify the variable provided by the
 *   caller of test() rather than a local copy.
 */
int test(struct someStruct *state) {
    state->total = 4;
    return 0;
}

/* 
 * Modifies the local copy of the struct, the original
 * in the calling function is not modified.
 * The C compiler will make a copy of the variable provided by the
 * caller of function test2() and so any changes that test2() makes
 * to the argument will be discarded since test2() is working with a
 * copy of the caller's variable and not the actual variable.
 */
int test2(struct someStruct state) {
    state.total = 8;
    return 0;
}

/*
 * Make a local copy of the argument then modify the local copy.
 * Until the assignment of the local copy to the argument is made,
 * the changes to the local copy are not made to the argument.
 * To make any changes made to the local copy in the argument,
 * you need to assign the local copy to the argument.
 */
int test3(struct someStruct *state) {
    struct someStruct  stateCopy;
    stateCopy = *state;    // make a local copy of the struct
    stateCopy.total = 12;  // modify the local copy of the struct
    *state = stateCopy;    /* assign the local copy back to the original in the
                              calling function. Assigning by dereferencing pointer. */
    return 0;
}

int main () {
    struct someStruct s;

    /* Set the value then call a function that will change the value. */
    s.total = 5;
    test(&s);
    printf("after test(): s.total = %d\n", s.total);

    /*
     * Set the value then call a function that will change its local copy 
     * but not this one.
     */
    s.total = 5;
    test2(s);
    printf("after test2(): s.total = %d\n", s.total);

    /* 
     * Call a function that will make a copy, change the copy,
       then put the copy into this one.
     */
    test3(&s);
    printf("after test3(): s.total = %d\n", s.total);

    return 0;
}

把“*”放在变量名前面(就像这篇文章中所做的那样)或者放在类型名后面(就像题主所做的那样)有什么意义?例子: int test(struct someStruct *state)题主: int test(struct someStruct* state) - ALM
3
抱歉,@ALM,您的澄清并未澄清问题。不确定您所指的“struct的分配名称”的意思是什么。也许您的意思是在声明中在结构体名称后加上一个空格,而OP在名称后加上了一个空格?对于编译器来说没有区别,只是可读性和习惯上的差异。我更喜欢将星号放在变量名旁边,因为如果在声明中有多个变量,“struct someStruct p1,p2,var1;”将创建两个指针和一个变量。“struct someStruct* p1,p2,var1;”将创建一个指针“p1”和两个变量“p2”和“var1”。 - Richard Chambers
何时应该使用 test 风格,何时应该使用 test3 风格? - Dave
1
@Dave 函数 test3() 只是为了展示修改本地 struct 副本与修改传递给函数的 struct 的区别。在函数 test3() 中重要的部分是修改本地副本不会修改参数。该函数还展示了如何修改本地副本,然后一旦所有更改都完成,将本地副本分配给参数,从而改变参数的值。在大多数情况下,如果要修改参数,则应像 test() 一样进行,而不是像 test3() - Richard Chambers
@RichardChambers 感谢您提供的额外解释!那么,除非有特定问题需要使用test3(),否则我将从现在开始采用test()的风格。 - Dave
显示剩余3条评论

15

这是 struct 的正确用法。你的返回值有一些问题。

另外,因为你要打印一个无符号整数,所以应该使用 %u 而不是 %d


3
是的,没错。它创建了一个结构体s,并将其总数设置为5,将指向该结构体的指针传递给函数,函数使用该指针将总数设置为4,然后打印输出。" -> "用于指向结构体的指针的成员,"." 用于结构体的成员。就像你之前使用的那样。
不过,返回值是不同的。 test 应该是void类型,而 main 需要在结尾处加上 return 0

1

没错。如果不正确(从 . / -> 的角度来看),你的编译器会报错。


0

是的,这是结构体的正确用法。你也可以使用

typedef struct someStruct {
 unsigned int total;
} someStruct;

那么你就不必一遍又一遍地写 struct someStruct s;,而是可以使用 someStruct s;


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接