打印返回结构的成员

我在打印从函数返回的结构的成员时遇到问题:

#include  struct hex_string { char a[9]; }; struct hex_string to_hex_string_(unsigned x) { static const char hex_digits[] = "0123456789ABCDEF"; struct hex_string result; char * p = result.a; int i; for (i = 28; i >= 0; i -= 4) { *p++ = hex_digits[(x >> i) & 15]; } *p = 0; printf("%s\n", result.a); /* works */ return result; } void test_hex(void) { printf("%s\n", to_hex_string_(12345).a); /* crashes */ } 

to_hex_string_内的printf调用打印出正确的结果,但test_hexprintf调用test_hex我的程序崩溃。 究竟是为什么呢? 这是一生的问题,还是别的什么?

当我用puts(to_hex_string_(12345).a)替换printf调用时,我得到一个编译器错误:

 invalid use of non-lvalue array 

这里发生了什么?

C中有一条规则很少生效,其中规定:

如果尝试修改函数调用的结果或在下一个序列点之后访问它,则行为未定义。 (C99§6.5.2.2)

在这种情况下,在计算printf()的参数之后和printf()函数本身执行之前有一个序列点。 传递给printf()的指针是一个指向返回值本身元素的指针 – 当printf()尝试通过该指针访问该字符串时,就会崩溃。

这个问题很难遇到,因为函数值不是左值,所以你不能用&直接指向它。

你已经设法遇到一个相当模糊的语言角落案例。

在大多数情况下,数组类型的表达式被隐式转换为指向数组第一个元素的指针; 例外情况是表达式是一元&运算符的操作数,当它是一元sizeof运算符的操作数时,以及它是用于初始化数组对象的初始值设定项中的字符串文字。 这些例外都不适用于此。

但是在转换中有一个隐含的假设:指针指向数组对象的第一个元素。

大多数数组表达式 – 实际上几乎所有数组 – 都引用了一些数组对象,例如声明的数组变量,多维数组的元素等等。 函数不能返回数组,因此您不能以这种方式获得非左值数组表达式。

但正如您所见,函数可以返回包含数组的结构 – 并且没有与数组表达式to_hex_string_(12345).a相关联的对象to_hex_string_(12345).a

新的ISO C11标准通过在描述存储持续时间的部分中添加新的措辞来解决这个问题。 N1570草案第6.2.4p8节说:

具有结构或联合类型的非左值表达式,其中结构或联合包含具有数组类型的成员(包括,递归地,所有包含的结构和联合的成员)是指具有自动存储持续时间和临时生存期的对象。 它的生命周期在评估表达式时开始,其初始值是表达式的值。 当包含完整表达式或完整声明符的评估结束时,它的生命周期结束。 任何使用临时生命周期修改对象的尝试都会导致未定义的行为。

实际上,这表示函数返回的值(与大多数函数结果不同)是临时对象的值,允许其数组成员的衰减为您提供(临时)有效指针。

但是在编译器完全支持新的C标准(将不会存在多年)之前,您只需要避免引用返回结构的数组成员。

您遇到的问题是:返回的变量result是函数_to_hex_string的局部变量,这意味着它在fonction调用结束时被删除。 因此,当您尝试在fonction test_hex检查它时,它将不再可用。

要解决您的问题,您可以处理指针。

这是你的代码修改

 struct hex_string { char a[9]; }; struct hex_string * to_hex_string_(unsigned x) // here you return a pointer { static const char hex_digits[] = "0123456789ABCDEF"; struct hex_string result; result = (struct hex_string *) malloc(sizeof(struct hex_string)); char * p = result->a; int i; for (i = 28; i >= 0; i -= 4) { *p++ = hex_digits[(x >> i) & 15]; } *p = 0; printf("%s\n", result->a); /* works */ return result; } void test_hex(void) { printf("%s\n", to_hex_string_(12345)->a); /* works */ } 

我有美好的一天吗?