Compund literals storage duration in C
Asked Answered
E

2

5

First question ever :)

I'm studying programming "by myself", reading "C Programming: A modern Approach" by K.N.King. In Chapter18 - Declarations, in the Q&A section, there is a question about why selection statements and iteration statements (and their "inner" statements) are considered to be blocks in C99. After a small introduction to the answer, he says:

[...]The C99 standard states that the object represented by a compund literal has static duration if the compound literal occurs outside the body of a function. Otherwise, it has automatic storage duration; as a result, the memory occupied by the object is deallocated at the end of the block in which the compound literal appears[...]

Which I think I understand. I've tried to return a pointer to a compound literal from a function, and indeed the output is wrong (or undefined I guess). My problem is the following; He gives this example:

/* 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);

Followed by this explanation:

Each compound literal causes an object to be created, but that object exists only within the block formed by the braces that enclose the statement in which the literal appears. By the time evaluate_polynomial is called, coefficients points to an object that no longer exist. The result: undefined behavior.

And when I try exactly that same code in my computer (I'm using GCC in a Linux VM), I always get the right output. The literal does not seem to be deallocated once the "control flow" exit the if block.

Can someone elaborate a little more about this?

Thanks.

Edict answered 7/7, 2020 at 13:18 Comment(10)
Well, that's the meaning of "undefined behavior". It might seem to work, but not really defined to do so. You are accessing some memory that is no longer allocated to the object, but might accidentally contain some data which is yielding the "expected" result.Striker
The literal is {1.5, -3.0, 6.0} The literal must exist somewhere in the program, otherwise it cannot be assigned. So upon exiting the block, coefficients points to the literal.Whiten
A compiler could of course create a new object, copy the literal to that object, use its address in the assignment and then deallocate the memory upon exiting the block, which would cause undefined behavior.Whiten
Seems your compiler uses my first explanation. Another compiler could use the second explanation.Whiten
@EugeneSh. Ok thanks sir. I know (or rather I think I know) that "undefined behavior" means "not guaranteed to work". It's just that, since I'm constantly getting the right output, I was wondering if the information in the book was not up to date. But it seems that the book is right and that code would indeed cause undefined behavior.Edict
@PaulOgilvie Thanks for your time. You have also confirmed that the code in the book would cause undefined behavior, so the explanation that Mr.King gives is correct.Edict
really just another dupe of Can a local variable's memory be accessed outside its scope?; that is tagged c++ but nothing relevant changed from CDopp
@Dopp Thanks for the link. Before I posted my question I explored the "similar questions" offered by the site, but I couldn't really find what I was looking for. In the specific case of the link you posted I know that if int a was declared as static int a, it wouldn't be a problem to return the address of a from the function. I was just afraid that the book is outdated and the storage duration of a literal created inside an if block is static and not automatic, because I was not able to make the program fail.Edict
So, it turns out that this thread is similar to mine: Compound literal lifetime and if blocks.Edict
@Dopp I don't see how this is a duplicate. c++ doesn't support compound literals. It is not at all obvious why (double[3]){1.5, -3.0, 6.0} should behave like a local variable and not like a string literal.Pettigrew
P
5

For performance reasons, when the program reaches the end of a variable's scope (or this variable is freed), its content is not deleted. But its memory address might be reused later for storing another variable leading to an Undefined Behavior.

Hence, if you print your coefficients later in the program, they might or might not correspond to your expected value.

If you want to illustrate this you can progressively add more and more code beetween your variable's exit block and the instruction where your variable is read. At some point, your variable will likely not have the expected value.

Please carefully avoid Undefined Behaviors as they can lead to hard to reproduce bugs (e.g. program that works 99% of the time but breaks horribly 1% of the time).

Philibeg answered 7/7, 2020 at 13:36 Comment(5)
This is called an Undefined Behavior. "This" refers to reusing an area later?Orangery
Thanks for your comment. I clarified my answer.Philibeg
Thank you Maxime. I think I understand, and that is actually what I imagined; that the memory in which the literal resides is now available to reuse, but since It is not needed at the moment, the contents of it are unchanged, and that's why I'm getting the right output. You have confirmed that the book is correct. Thanks again.Edict
To be clear, it's always undefined behaviour, and various effects might or might not happen as a result. (First para appears to read that it might be UB)Watthour
I wonder if the people who decided that compound literals' lifetime shouldn't extend through the enclosing function recognized that limiting compound literals to block scope doesn't prevent them from being re-evaluated within the lifetimes of the objects thus produced (which was the stated reason for limiting their lifetime in the first place).Filippo
F
3

Although the C99 Standard regards compound literals as lvalues whose address may be taken, the Standard's lifetime rules for them mean that code should avoid taking the address of compound literals except in cases where the address will be immediately passed to a function that won't persist a copy of it, or taking the address of large automatic-duration compound literals whose contents will never change. Instead of doing those things, code should declare a conventional named object of appropriate type and value (static const, if the value will never change), and pass the address of that.

The stated rationale for limiting the lifetime of compound literals to block scope rather than function scope is to avoid having to allocate space for a new instance of a compound literal if the code defining it is re-executed. Given that code before a compound literal is allowed to goto a label which is in the same block as the compound literal but precedes it, however, it's necessary to allow for the possibility that executing a compound literal during the lifetime of the object created by an earlier execution of the same literal may not create a separately-allocated object; once one handles that for the "goto" scenario, that would also avoid any similar problems that would arise if compound literals' lifetime were bounded by the enclosing function.

Filippo answered 8/7, 2020 at 22:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.