【问题标题】:Compund literals storage duration in CC中的复合文字存储持续时间
【发布时间】:2020-10-27 18:24:59
【问题描述】:

有史以来第一个问题:)

我正在“自学”学习编程,正在阅读 K.N.King 的“C Programming: A modern Approach”。在Chapter18 - Declarations 中的 Q&A 部分,有一个问题是关于为什么选择语句和迭代语句(以及它们的“内部”语句)在 C99 中被视为块。在简单介绍了答案后,他说:

[...]C99 标准规定,如果复合文字出现在函数体之外,则由复合文字表示的对象具有静态持续时间。否则有自动保存期限;结果,对象占用的内存在复合文字出现的块的末尾被释放[...]

我想我明白了。我试图从函数返回一个指向复合文字的指针,实际上输出是错误的(或者我猜是未定义的)。我的问题如下;他举了这个例子:

/* Example 2 - if statements with braces */

double *coefficients, value;

if(x){
    coefficients = (double[3]) {1.5, -3.0, 6.0};
}else{
    coefficients = (double[3]) {4.5, 1.0, -3.5};
}
value = evaluate_polynomial(coefficients);

接下来是这样的解释:

每个复合文字都会导致创建一个对象,但该对象仅存在于由包含该文字出现的语句的大括号形成的块中。在调用evaluate_polynomial 时,coefficients 指向一个不再存在的对象。结果:未定义的行为。

当我在我的计算机上尝试完全相同的代码时(我在 Linux VM 中使用 GCC),我总是得到正确的输出。一旦“控制流”退出 if 块,文字似乎不会被释放。

有人可以详细说明一下吗?

谢谢。

【问题讨论】:

  • 嗯,这就是“未定义行为”的意思。它可能看起来有效,但并没有真正定义为这样做。您正在访问一些不再分配给对象的内存,但可能会意外包含一些产生“预期”结果的数据。
  • 字面量为{1.5, -3.0, 6.0} 字面量必须存在于程序某处,否则无法赋值。所以在退出块时,coefficients 指向文字。
  • 编译器当然可以创建一个新对象,将文字复制到该对象,在赋值中使用其地址,然后在退出块时释放内存,这将导致未定义的行为。
  • 您的编译器似乎使用了我的第一个解释。另一个编译器可以使用第二种解释。
  • 真的只是Can a local variable's memory be accessed outside its scope?的另一个骗子;标记为c++,但与 C 没有任何相关变化

标签: c scope storage c99 compound-literals


【解决方案1】:

尽管 C99 标准将复合文字视为可以获取地址的左值,但标准对它们的生命周期规则意味着代码应避免获取复合文字的地址,除非地址将立即传递给赢得'不要保留它的副本,或者获取内容永远不会改变的大型自动持续时间复合文字的地址。代码应该声明一个具有适当类型和值(静态常量,如果值永远不会改变)的常规命名对象,而不是做这些事情,并传递它的地址。

将复合文字的生命周期限制为块范围而不是函数范围的既定理由是,如果重新执行定义它的代码,则避免必须为复合文字的新实例分配空间。鉴于允许复合文字之前的代码转到与复合文字在同一块中但在其前面的标签,但是,有必要考虑在由相同文字的早期执行可能不会创建单独分配的对象;一旦处理了“goto”场景,这也将避免如果复合文字的生命周期受封闭函数限制,则会出现任何类似的问题。

【讨论】:

    【解决方案2】:

    出于性能原因,当程序到达一个变量作用域的末尾时(或者这个变量是freed),它的内容不会被删除。但它的内存地址可能稍后会被重用于存储另一个导致Undefined Behavior 的变量。

    因此,如果您稍后在程序中打印系数,它们可能或可能不符合您的预期值。

    如果您想说明这一点,您可以在变量的退出块和读取变量的指令之间逐步添加越来越多的代码。在某些时候,您的变量可能不会达到预期值。

    请小心避免未定义的行为,因为它们会导致难以重现的错误(例如,程序在 99% 的时间都可以运行,但在 1% 的时间里会严重中断)。

    【讨论】:

    • This is called an Undefined Behavior.“这个”是指以后重用一个区域?
    • 感谢您的评论。我澄清了我的答案。
    • 谢谢马克西姆。我想我明白了,这实际上是我想象的;文字所在的内存现在可以重用,但由于目前不需要它,它的内容没有改变,这就是我得到正确输出的原因。你已经确认这本书是正确的。再次感谢。
    • 明确地说,它始终是未定义的行为,结果可能会或可能不会发生各种影响。 (第一段似乎读到它可能是 UB)
    • 我想知道那些决定复合文字的生命周期不应该通过封闭函数扩展的人是否认识到将复合文字限制在块范围内并不能阻止它们在生命周期内被重新评估由此产生的对象(这是首先限制其寿命的既定原因)。
    猜你喜欢
    • 2015-04-23
    • 2018-04-15
    • 1970-01-01
    • 2021-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-27
    • 1970-01-01
    相关资源
    最近更新 更多