5 ms·
> All variable declarations at top of block. And they say they want their C code to suck less? Why voluntarily open the door to read-before-write undefined-be
by MaxBarraclough 6y ago
> All variable declarations at top of block.
And they say they want their C code to suck less?
Why voluntarily open the door to read-before-write undefined-behaviour? The lifetime of a local should be as short as it reasonably can be.
- panpanna 6y agoDoesn't C99 warn you if you make such mistakes?
- flohofwoe 6y agoOn some compilers (gcc and clang at least) only when the warning is enabled (e.g. with -Wall). But I agree that it's a bit silly to on hand recommend C99, and on the other hand, not take advantage of C99 features. It seems like the rules have been written against C89, and the "use unextended C99" only slipped in later.
- yvdriess 6y agoMy take on this: pro: - Storage concerns are separated and grouped at the top. - Logic code is more compact, helping readability. - Stack use can be determined at a glance. - Looking up a variable's type is faster in cases where a variable is used throughout the code block. This is good to optimize, as you constantly need to look up variables when reading C code. - Reflects that object lifetime of any stack storage object is the same regardless of where it is declared in the code block. cons: - For long functions, the distance between use an declaration lines can impair looking up a var's type. (having to scroll to get to the top of your function is a sign you need to break it up though) - In many cases, a variable is only used in one location in the block and has a meaningful static initialization. Local declarations in this case readability, as it does not have to be looked up. - "-Wuninitialized" exists, but passing pointer and defining 'default' value style can still cause read-before-write. I tend to an in-between style, where code is grouped where it makes sense with group-local variables declared in front. Variables that are used throughout the entire code block body are declared at the very top.
- MaxBarraclough 6y ago> Storage concerns are separated and grouped at the top. That's rather circular. That's what defines the style, it's not in itself an advantage or a disadvantage. > Logic code is more compact, helping readability It's slightly more compact, but it harms readability, as I describe at [0]. Using the new style, especially with the const keyword, can make code much more readable. > Stack use can be determined at a glance. It can't, that's up to the compiler. Perhaps all locals will be enregistered. Even if it were true, it's extremely rare to need to do this. > Looking up a variable's type is faster in cases where a variable is used throughout the code block. In certain circumstances this may be true, but there may still be multiple levels you may have to check. For short-lived locals, it will be quicker to look up the type using the new style, as that keeps the declaration close to where it is used. Using a modern IDE, it's a draw - just hover over a local and it will tell you its type. > Reflects that object lifetime of any stack storage object is the same regardless of where it is declared in the code block. That doesn't sound right. That will be platform-dependent. I see no reason a compiler couldn't generate incremental pushes, rather than an upfront block allocation. Also, depending on what the compiler generates, different locals may end up occupying the same place in the stack (at different times), and a given local may be held in different places in the stack at different times. And that's if we're ignoring registers. No matter what we do, we can't rely on the generated assembly looking anything like the source code. Beyond that, it's 'dishonest' about locals' real lifetimes, in a way that can be harmful. Compare: { // Old style int i; int j; i = j; // Undefined behaviour j = 7; } { // New style int i = j; // Compile-time error. 'j' is not in scope. int j = 7; } > "-Wuninitialized" exists, but passing pointer and defining 'default' value style can still cause read-before-write. I'm afraid I don't get what you're saying here. Are you referring to the danger of passing a 'default' value to a function, say, in a way that might be dangerous?
- jagged-chisel 6y agoBut can't the compiler work that bit out? Source code is for humans. Put all the vars at the top to tell the humans "here's all the scratch space I'll be needing in this block" and then let the compiler observe "var x is only used twice, so let's optimize that lifetime..."
- atilaneves 6y ago> Source code is for humans The reason why making the scope as local as possible is important is for humans, not compilers. > Put all the vars at the top to tell the humans "here's all the scratch space I'll be needing in this block" Why would humans care about how much scratch space is needed? That's for the compiler to know.
- cesarb 6y ago> Why would humans care about how much scratch space is needed? In some contexts, it's important. For instance, each thread within the Linux kernel has a very limited fixed-size stack space (used to be 4K bytes, IIRC it's been increased to 8K and then 16K), which resides in physical memory (cannot be swapped out or lazily allocated). Avoiding large stack frames is necessary.
- atilaneves 6y ago1. That's a rare use case 2. There are ways of measuring that without hampering readability. I mean, are programmers supposed to add all the variables' sizes up in their head??
- Someone 6y agoAlso, unless your thread has only a single function that has local variables or arguments and that function doesn’t contain sub blocks that declare variables (say inside a while block), All variable declarations at top of block doesn’t help much in gauging stack space usage of a thread. Actually, not even that helps, you would also have to know how much stack a function call takes (might be non-trivial in the presence of stack alignment rules), and which functions get inlined. Edit: if you declare all your locals at the start of a function, chances are the compiler will check whether it can make some of them share memory, so you’d have to take that into account, too. If you’re concerned about stack overflows in your threading code, it is tooling is what you need, not manually counting stack usage.