未定义行为:当试图访问函数调用的结果 [英] Undefined behavior: when attempting to access the result of function call

查看:178
本文介绍了未定义行为:当试图访问函数调用的结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下编译并打印字符串作为输出。

The following compiles and prints "string" as an output.

#include <stdio.h>

struct S { int x; char c[7]; };

struct S bar() {
    struct S s = {42, "string"};
    return s;
}

int main()
{
    printf("%s", bar().c);
}

显然,这似乎是根据要调用一个未定义的行为

Apparently this seems to invokes an undefined behavior according to

C99 6.5.2.2/5如果试图修改函数的结果
  致电或下一个序列点之后访问它,行为
  不确定的。

C99 6.5.2.2/5 If an attempt is made to modify the result of a function call or to access it after the next sequence point, the behavior is undefined.

我不明白的地方它说关于下一个序列点。这是怎么回事呢?

I don't understand where it says about "next sequence point". What's going on here?

推荐答案

您已经运行到语言的细微角落。

You've run into a subtle corner of the language.

数组类型的前pression是,在大多数情况下,隐式转换为指向数组对象的第一个元素。例外情况,其中没有一个适用于此处,分别是:

An expression of array type is, in most contexts, implicitly converted to a pointer to the first element of the array object. The exceptions, none of which apply here, are:


  • 当数组前pression是一元&放大器的操作; 操作符(它产生整个阵列的地址);

  • 当它是一个一元的sizeof <击>或(如C11)的 _Alignof 操作符( sizeof的改编产生的阵列,而不是一个指针的大小的大小);和

  • 当它在用于初始化数组对象的初始化字符串文字(字符海峡[6] =你好; 不转换你好的char *

  • When the array expression is the operand of a unary & operator (which yields the address of the entire array);
  • When it's the operand of a unary sizeof or (as of C11) _Alignof operator (sizeof arr yields the size of the array, not the size of a pointer); and
  • When it's a string literal in an initializer used to initialize an array object (char str[6] = "hello"; doesn't convert "hello" to a char*.)

(参数 N1570 草案增加了不正确 _Alignof 来的例外列表中。事实上,对于那些没有明确的理由, _Alignof 只能应用于键入名称,而不是一个前pression。)

(The N1570 draft incorrectly adds _Alignof to the list of exceptions. In fact, for reasons that are not clear, _Alignof can only be applied to a type name, not to an expression.)

请注意,有一个隐含的假设:该数组前pression指的是摆在首位的数组对象。在大多数情况下,它(最简单的情况是,当阵列前pression是一个声明数组对象的名称) - 但在这一种情况下,的没有数组对象

Note that there's an implicit assumption: that the array expression refers to an array object in the first place. In most cases, it does (the simplest case is when the array expression is the name of a declared array object) -- but in this one case, there is no array object.

如果一个函数返回一个结构,则返回结构结果的通过值的。在这种情况下,该结构包含数组,给我们一个数组的的,没有相应的数组的对象的,至少在逻辑上。所以数组前pression 巴()。ç衰变到一个指向没有关系,...数组对象的......呃,呃第一要素T存在。

If a function returns a struct, the struct result is returned by value. In this case, the struct contains an array, giving us an array value with no corresponding array object, at least logically. So the array expression bar().c decays to a pointer to the first element of ... er, um, ... an array object that doesn't exist.

2011年的ISO C标准解决了这个通过引入的临时一辈子的,它仅适用于非左值前pression与结构或联合类型,其中结构或联合
包含数组类型的成员( N1570 6.2.4p8)。这样的对象不能被修改的,其寿命在包含完整的前pression或完整声明符的末尾。

The 2011 ISO C standard addresses this by introducing "temporary lifetime", which applies only to "A non-lvalue expression with structure or union type, where the structure or union contains a member with array type" (N1570 6.2.4p8). Such an object may not be modified, and its lifetime ends at the end of the containing full expression or full declarator.

因此​​,作为C2011的,你的程序的行为是明确界定。在的printf 调用得到一个指针数组与临时寿命的结构对象的一部分的第一要素;该对象将继续存在,直到的printf 通话结束。

So as of C2011, your program's behavior is well defined. The printf call gets a pointer to the first element of an array that's part of a struct object with temporary lifetime; that object continues to exist until the printf call finishes.

但作为C99的,行为是不确定的 - 不一定是因为你引用条款(据我所知,没有插入顺序点),但因为C99没有定义的数组对象会是必要的的printf 工作

But as of C99, the behavior is undefined -- not necessarily because of the clause you quote (as far as I can tell, there is no intervening sequence point), but because C99 doesn't define the array object that would be necessary for the printf to work.

如果你的目标是让这个程序工作,而不是理解为什么它可能会失败,你可以存储在一个明确的目标函数调用的结果是:

If your goal is to get this program to work, rather than to understand why it might fail, you can store the result of the function call in an explicit object:

const struct s result = bar();
printf("%s", result.c);

现在你有一个结构对象的自动的,而不是的临时的,存储时间,所以在和的printf执行后存在电话。

Now you have a struct object with automatic, rather than temporary, storage duration, so it exists during and after the execution of the printf call.

这篇关于未定义行为:当试图访问函数调用的结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆