【问题标题】:passing struct pointer to functions not working将结构指针传递给不起作用的函数
【发布时间】:2011-10-14 16:13:53
【问题描述】:

Motive:将结构传递给函数,例如我们可以在任何函数中更改它并在任何函数中检索任何值。

我从以下位置获取此代码:http://cboard.cprogramming.com/c-programming/126381-passing-structure-reference-through-4-functions.html#post942397

我稍微调整了一下,结果如下:

struct_passing.h

typedef struct thing {
    char *x;
}thing_t;

struct_passing.c


#include <stdio.h>
#include "struct_passing.h"

void f4(thing_t *bob) {
    bob->x = "changed";
}

void f3(thing_t *bob) {
    f4(bob);
        printf("inside f3 x: %s\n", bob->x);
}

void f2(thing_t *bob) {
    f3(bob);
}

void f1(thing_t *bob) {
    f2(bob);
}

int main(void) {
    thing_t foo;
    foo.x = "same";
    printf("Before: %s\n", foo.x);
    f1(&foo);
    printf("After: %s\n", foo.x);
    return 0;
}

它在 ubuntu 上的 **gcc 版本 4.4.3 上按预期工作

$ gcc -o struct struct_passing.c 
$ ./struct
Before: same
inside f3 x: changed
After: changed

但在 freebsd 上的 gcc 版本 4.2.1 上,我无法检索“bob->x”的更改值。我没有得到inside f3 x: changed。相反,我得到了垃圾。

为什么?

【问题讨论】:

  • 你得到了什么“垃圾”?代码看起来不错。
  • 提高编译器的警告级别:也许有帮助 (gcc -std=c89 -pedantic -Wall -Wextra ...)
  • 也许将x 声明为该特定代码的const char *?我不知道为什么它不起作用,可能值得一试
  • “任意随机字符串”。不过,它看起来像某种字符串吗?我的意思是,这段代码非常无害,任何版本的 gcc 都不应该有问题。它是最基本的。即使没有const,也应该没问题。
  • 也在这里工作:ideone.com/9eQXp

标签: c pointers gcc struct freebsd


【解决方案1】:

在我看来一切都很好。在调试器中运行一个像这样的小示例程序,看看到底发生了什么,总是很有启发性的。如果您不了解 gdb,那么现在是开始的好时机。我已经从你的代码中创建了 struct_passing.[ch],让我们看看:

[wes@eeegor ~]$ gcc -v
Using built-in specs.
Target: i386-undermydesk-freebsd
Configured with: FreeBSD/i386 system compiler
Thread model: posix
gcc version 4.2.1 20070719  [FreeBSD]

是的,相同的编译器。编译调试:

[wes@eeegor ~/src]$ cc -g -o struct struct_passing.c

然后运行它:

[wes@eeegor ~/src]$ gdb struct
GNU gdb 6.1.1 [FreeBSD]
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-marcel-freebsd"...

好的,我们可能想知道从 main() 开始发生了什么,所以...

(gdb) b main
Breakpoint 1 at 0x80484c0: file struct_passing.c, line 21.
(gdb) run
Starting program: /usr/home/wes/src/struct

Breakpoint 1, main () at struct_passing.c:21
21      int main(void) {

我们将主要使用 (s)tep 单步执行函数,使用 (n)ext 单步执行我们不想看到其内部的函数,例如 printf()。我们也可能偶尔 (p) 打印一些东西。

(gdb) n
main () at struct_passing.c:23
23          foo.x = "same";

我们还没有执行这一行,所以如果我们看一下 foo,它可能会包含垃圾:

(gdb) p foo
$1 = {x = 0x80482c5 "\203Ä\fÃ"}

是的,正如预期的那样,x 指向一些垃圾字符串。这是因为 foo 以及扩展名 foo.x 是在 main() 函数中的堆栈上创建的,而堆栈只是之前留下的任何随机垃圾。

(gdb) n
24          printf("Before: %s\n", foo.x);
(gdb) n
Before: same
25          f1(&foo);
(gdb) s
f1 (bob=0xbfbfec40) at struct_passing.c:18
18          f2(bob);
(gdb) p bob
$2 = (thing_t *) 0xbfbfec40

所以我们进入 f1() 并看到我们已经正确地将 foo 的地址传递给了函数。请注意 gdb (p)rint 函数如何始终知道它正在打印的类型?在这种情况下,查看结构的地址并不是很有用,所以:

(gdb) p *bob
$3 = {x = 0x804857c "same"}

哦,很好,结构看起来还是应该的。让我们继续,直到我们改变它:

(gdb) s
f2 (bob=0xbfbfec40) at struct_passing.c:14
14          f3(bob);
(gdb) p *bob
$4 = {x = 0x804857c "same"}
(gdb) s
f3 (bob=0xbfbfec40) at struct_passing.c:9
9           f4(bob);
(gdb) p *bob
$5 = {x = 0x804857c "same"}
(gdb) s
f4 (bob=0xbfbfec40) at struct_passing.c:5
5           bob->x = "changed";

记住,我们还没有执行第 5 行,所以“bob”应该还是一样的:

(gdb) p *bob
$6 = {x = 0x804857c "same"}

是的,所以让我们执行第 5 行并再看一遍:

(gdb) n
6       }
(gdb) p *bob
$7 = {x = 0x8048563 "changed"}

所以“bob”确实变了。让我们继续看看 main() 中是否还有变化:

(gdb) n
f3 (bob=0xbfbfec40) at struct_passing.c:10
10              printf("inside f3 x: %s\n", bob->x);
(gdb) n
inside f3 x: changed
11      }
(gdb) n
f2 (bob=0xbfbfec40) at struct_passing.c:15
15      }
(gdb) n
f1 (bob=0xbfbfec40) at struct_passing.c:19
19      }
(gdb) n
main () at struct_passing.c:26
26          printf("After: %s\n", foo.x);
(gdb) n
After: changed
27          return 0;

所以我们得到了我们的预期。此时,我们即将从 main() 返回,最好让调试器继续运行,这样你就不会半途而废地走过 C 运行时启动代码的尾部:

(gdb) c
Continuing.

Program exited normally.
(gdb) Quit
(gdb)

让我们退出调试器并正常运行它以确保我们得到相同的输出。如果我们不这样做,那就太奇怪了……

(gdb) q
[wes@eeegor ~/src]$ ./struct
Before: same
inside f3 x: changed
After: changed

哦,很好,星星还在发光。我不确定你的情况发生了什么,让我们尝试像你一样编译并运行它:

[wes@eeegor ~/src]$ gcc -o struct struct_passing.c
[wes@eeegor ~/src]$ ./struct
Before: same
inside f3 x: changed
After: changed

现在,让我们改进您的错误报告。给我们'uname -a'、'gcc -v'和'ld -v'的输出,这样我们就可以准确地找出你正在使用的系统。另请阅读有关如何编写错误报告的“Joel on Software”文章。 :)

【讨论】:

    【解决方案2】:

    在我看来,代码是完全合法的,它应该可以工作...... 我怀疑这个问题与 gcc 4.2.1 的优化器如何处理字符串常量有关。 你可以试试下面的编译命令行,看看是不是真的:

    $ gcc -O0 -o struct struct_passing.c
    

    我也建议尝试以下代码变体,看看是否能得到更好的结果:

    struct_passing.c
    
    #include <stdio.h>
    #include "struct_passing.h"
    
    const char *same = "same";
    const char *changed = "changed";
    
    void f4(thing_t *bob) {
        bob->x = changed;
    }
    
    void f3(thing_t *bob) {
        f4(bob);
            printf("inside f3 x: %s\n", bob->x);
    }
    
    void f2(thing_t *bob) {
        f3(bob);
    }
    
    void f1(thing_t *bob) {
        f2(bob);
    }
    
    int main(void) {
        thing_t foo;
        foo.x = same;
        printf("Before: %s\n", foo.x);
        f1(&foo);
        printf("After: %s\n", foo.x);
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2020-05-15
      • 1970-01-01
      • 2012-11-08
      • 2021-07-17
      • 1970-01-01
      • 1970-01-01
      • 2010-09-20
      • 2021-11-02
      • 2019-05-03
      相关资源
      最近更新 更多