16

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

#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

这里发生了什么?

4

3 回答 3

18

C中有一条很少生效的规则,它指出:

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

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

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

于 2011-11-01T08:19:59.867 回答
13

您已经设法遇到了该语言的一个相当晦涩的角落案例。

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

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

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

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

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

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

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

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

于 2011-11-01T08:07:31.187 回答
-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 */
}

我有美好的一天吗

于 2011-11-01T10:37:17.517 回答