我在打印从函数返回的结构的成员时遇到问题:
#include <stdio.h>
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 */
}
printf
内的to_hex_string_
调用会打印出正确的结果,但printf
内的test_hex
调用会导致我的程序崩溃。究竟是为什么呢?这是一生的问题,还是其他问题?
当我用printf
替换puts(to_hex_string_(12345).a)
调用时,出现编译错误:
invalid use of non-lvalue array
这里发生了什么?
答案 0 :(得分:18)
C中有一条很少生效的规则,其中规定:
如果尝试修改函数调用的结果或者 在下一个序列点之后访问它,行为是未定义的。 (C99§6.5.2.2)
在这种情况下,在printf()
的参数被评估之后,printf()
函数本身执行之前,有一个序列点。传递给printf()
的指针是指向返回值本身的元素的指针 - 当printf()
尝试通过该指针访问字符串时,您会崩溃。
这个问题很难遇到,因为函数值不是左值,所以你不能用&
直接指向它。
答案 1 :(得分:13)
你已经设法遇到了一个相当模糊的语言角落。
在大多数情况下,数组类型的表达式被隐式转换为指向数组第一个元素的指针;例外情况是表达式是一元&
运算符的操作数,当它是一元sizeof
运算符的操作数时,以及它是用于初始化数组对象的初始值设定项中的字符串文字。这些例外都不适用于此。
但是在转换中有一个隐含的假设:指针指向数组的第一个元素 object 。
大多数数组表达式 - 实际上几乎所有数组 - 都引用了一些数组对象,例如声明的数组变量,多维数组的元素等等。函数不能返回数组,因此无法以这种方式获得非左值数组表达式。
但正如您所见,函数可以返回包含数组的结构 - 并且没有与数组表达式to_hex_string_(12345).a
关联的对象。
新的ISO C11标准通过在描述存储持续时间的部分中添加新措辞来解决此问题。 The N1570 draft,第6.2.4p8节,说:
具有结构或联合类型的非左值表达式,其中 结构或联合包含一个数组类型的成员(包括, 递归地,所有包含的结构和联合的成员)指的是 具有自动存储持续时间和临时生存期的对象。 它的生命周期从评估表达式及其初始值开始 value是表达式的值。它的生命终结了 对包含完整表达式或完整声明符的评估结束。 任何使用临时生命周期修改对象的尝试都会导致 未定义的行为。
实际上,这表示函数的返回值(与大多数函数结果不同)是临时对象的值,允许其数组成员的衰减为您提供(暂时)有效指针。
但是直到编译器完全支持新的C标准(这将不会持续几年),你只需要避免引用返回结构的数组成员。
答案 2 :(得分:-1)
您遇到的问题是:返回的变量result
是函数 _to_hex_string 的局部变量,这意味着它在函数调用结束时被删除。因此,当您尝试在函数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 */
}
我度过了愉快的一天