5

有史以来的第一个问题:)

我正在“自己”学习编程,正在阅读KNKing的“C Programming: A modern Approach”。在第 18 章 - 声明中的问答部分,有一个问题是关于为什么选择语句和迭代语句(以及它们的“内部”语句)在 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一旦“控制流”退出块,文字似乎不会被释放。

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

谢谢。

4

2 回答 2

5

出于性能原因,当程序到达变量范围的末尾(或此变量为freed)时,其内容不会被删除。但是它的内存地址可能会在以后被重用于存储另一个导致未定义行为的变量。

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

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

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

于 2020-07-07T13:36:08.300 回答
3

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

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

于 2020-07-08T22:26:34.050 回答