【问题标题】:Why can't a struct's member variables be modified in a function?为什么不能在函数中修改结构的成员变量?
【发布时间】:2014-09-19 17:36:12
【问题描述】:

我很好奇为什么当作为参数传递给函数时不能修改结构的变量。我知道参数是按值传递的,但是在传递结构变量时,您将其引用作为值传递。

C 肯定不会在堆栈上创建结构的副本,因为传入的结构引用的值与将结构的指针传递给函数的值相同。

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


typedef struct String {
    char* name;
    int x;
} String;

/* Func Protos */
void foo(String str);
void bar(String * str);
void banjo(String str);

int main() {
    String str = *(String *)malloc(sizeof(String));
    str.x = 5; /* x == 5 */
    foo(str); /* x == 5 still */
    printf("%d\n", str.x);
    banjo(str); /* x == 5 still */
    printf("%d\n", str.x);
    bar(&str); /* and of course, x = 0 now */
    printf("%d\n", str.x);
}

void foo(String str) {
    printf("%d\n", str); /* Same value as if we passed String * str */
    str.x = 0; 
}

void bar(String * str) {
    printf("%d\n", *str);
    str->x = 0;
}

void banjo(String str) {
    printf("%d\n", str);
    String * strptr = &str; /* This should be identical to passing String * str */
    strptr->x = 0;
}

产生这个输出:

3415000
5
3415000
5
3415000
0

任何帮助将不胜感激!

【问题讨论】:

  • 是的,那么您期望会发生什么,为什么?问题是什么?无论如何,C 确实会在堆栈上创建一个副本。
  • 你应该传递指针,因为 c 只传递值。
  • "str" 是 struct String 类型的实例,您不能使用 printf 语句一次打印整个结构。因此 "printf("%d\n", str)" 是错误的。
  • 那是什么? String str = *(String *)malloc(sizeof(String));
  • @Ejohnson:在进入讲座模式之前,请考虑问题的前提是错误的可能性:您可以修改作为参数传递给函数的结构,但更改将不可见,因为您正在修改副本。代码包含未定义行为 (IIRC) 的事实无济于事。

标签: c function struct parameters reference


【解决方案1】:
void banjo(String str) {
    printf("%d\n", str);
    String * strptr = &str; /* This should be identical to passing String * str */
    strptr->x = 0;
}

C 是按值传递。你在带有str 参数的banjo 函数中得到的是main str 对象的副本。

所以你的banjo 函数相当于:

void banjo(String str) {
    printf("%d\n", str);
    strptr.x = 0;  // useless, the object is discarded after function returns
}

顺便说一句,printf("%d\n", str); 无效。 d 转换说明符需要 int 但您传递的是结构值。该调用调用未定义的行为。如果要打印str 对象的地址,请使用:

printf("%p\n", (void *p) &str);

【讨论】:

  • 这就是我的想法,我想这里更有趣的问题是为什么副本与原始对象的地址相同?
  • @Ejohnson 只是因为他的代码有问题并且无法编译,所以输出不反映粘贴的程序。
  • @Ejohnson 我的意思是代码无效;它编译时带有警告。
  • 您使用的是标准的 GCC 编译器吗?
  • @Ejohnson 顺便说一句,抱歉我用了 he 因为我没有注意到你是真正的海报。使用 d 参数将结构值传递给 printf 会调用未定义的行为并按照您的想法进行操作。请在编译器中启用警告,-Wall for gcc
【解决方案2】:

这一行:

String str = *(String *)malloc(sizeof(String));

这就是您出现这种行为的原因。将其更改为:

String *str = (String *)malloc(sizeof(String));

这样就可以解决问题了。

【讨论】:

  • 那为什么要像这样在 main 中使用 malloc 呢?只需将其声明为普通变量,然后传入该变量的地址即可。
  • 这纯粹是假设,当我可以声明一个像 String str 这样的变量时,我永远不会在 main 中创建随机动态内存;
【解决方案3】:

声明

printf("%d\n", str);

不打印结构的地址,而是打印第一个元素的值
结构内部。 而且由于结构中的第一个元素是指向 char 的指针,因此 printf
在控制台上给出垃圾值。

试试这个例子:

#include<stdio.h>
struct a
{
   int b;
};

int main()
{
     struct a a1;
     a1.b = 10;  // try giving different value to a1.b
     printf("b = %u\n", a1.b);
     printf("a1 = %u\n", a1);
    return 0;
}

你认为你正在打印结构的基地址,你实际上是
打印结构内第一个元素的值。

【讨论】:

  • 这是正确的,接受了类似的答案,因为这在 cmets 中有说明。
  • 哎呀!!对于那个很抱歉。刚刚在讨论中走神了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-12
  • 2018-09-06
  • 2011-11-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多