I know that unused variables in C typically just lead to compiler warnings, and const
variables are meant to be read-only.
However, I’m curious: according to the C standard, is there any scenario where simply declaring a const
variable without ever using it can result in undefined behavior?
I’m not asking about compiler warnings or optimizations — only whether the standard allows this to become undefined behavior in any edge case.
I know that unused variables in C typically just lead to compiler warnings, and const
variables are meant to be read-only.
However, I’m curious: according to the C standard, is there any scenario where simply declaring a const
variable without ever using it can result in undefined behavior?
I’m not asking about compiler warnings or optimizations — only whether the standard allows this to become undefined behavior in any edge case.
Share Improve this question edited Mar 20 at 10:21 Toby Speight 31.3k52 gold badges76 silver badges113 bronze badges asked Mar 20 at 9:07 Alphin ThomasAlphin Thomas 1 4- 1 Do you mean declare or define the variable? Declaring a variable only tells the compiler that it exists somewhere, possible in a different translation unit. While defining a variable is the actual "implementation" of the variable. – Some programmer dude Commented Mar 20 at 9:21
- This question reminds me of a bug I have witnessed with my own two eyes back in ~1987/1988: I was debugging a small program in Borland Turbo Pascal 3.2 and I was not able to find the issue, so I asked for help to a friend. He suggested me to add a variable in the involved procedure and recompile. Even if that was unused, the error disappeared. – sigmud Commented Mar 20 at 9:56
- 5 @sigmud What you describe sounds like the situation where there is an undefined behavior bug somewhere in the program. Then when you do something like adding a completely unrelated variable, you change the build and memory layout. If you are unlucky, this memory layout change turns the undefined behavior bug dormant for now - so it doesn't mean that you have fixed the problem, it means that you've unfortunately hidden the problem, until the next time it resurfaces. This scenario is very common even in modern compilers. – Lundin Commented Mar 20 at 10:05
- 1 @Lundin In a particularly annoying variant of that, adding code to help diagnose the buggy behavior causes it to cease happening until you remove the diagnostic code (typically called a Heisenbug, because the behavior changes when you observe it). – Ray Commented Mar 20 at 14:26
3 Answers
Reset to default 6Defining objects (such as variables) that are unused in a program does not constitute undefined behavior (UB). This is because all forms of undefined behavior are explicitly enumerated by the C standard, and unused variables are not among them. Additionally, applying qualifiers like const
or volatile
to these unused objects does not alter this fact.
Furthermore, the C standard does not mandate compilers to issue diagnostics for unused variables, although compilers commonly provide warnings as a convenience to developers.
There is indeed at least one case where declaring an (unused) variable leads to undefined behavior. C23 (draft N3220) clause 6.4.2.1-14 says:
Any identifiers that differ in a significant character are different identifiers. If two identifiers differ only in nonsignificant characters, the behavior is undefined.
This refers to the number of significant characters in identifier names (minimal limits for internal and external identifiers respectively are defined in clause 5.2.5.2).
Thus, if the variable is declared with a name that is identical to an existing identifier in the significant characters, but differ in the nonsignificant characters, then the behavior is undefined.
In general, it is impossible to give a complete language-lawyer answer showing that a behavior is not undefined without citing the entire C standard, as it is necessary to assert that no part of the standard makes the behavior undefined.
A reasonable assurance the behavior is not undefined may be obtained by considering parts of the standard that speak to the subject, while relying on general familiarity with the rest of the standard to provide assurance it does not speak to this particular matter.
C 2024 6.9.1 speaks to identifiers declare with internal linkage:
There shall be no more than one external definition for each identifier declared with internal linkage in a translation unit.
This indicates the C committee explicitly considered that an identifier with internal linkage may be declared but not defined. (Note that an external definition is one outside of [external to] any function, not necessarily one with external linkage.) And, of course, if it is not defined, it is not used in an expression. (The next sentence says that if it is used in an expression, there shall be a definition for it.) Therefore, the committee allowed for the possibility that an identifier with internal linkage was declared but never used.
Further down, 6.9.1 also speaks to identifiers declared with external linkage:
If an identifier declared with external linkage is used in an expression (other than [various operations that do not evaluate the object]), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one.
Again we see the committee has provided for there to be no uses of a declared identifier, this time with external linkage.
That leaves the case of identifiers with no linkage. In this case, the standard contains no language discussing declarations of identifiers that are not used. However, reviewing the clauses discussing linkage (6.2.2) and declarations (6.7) reveals no statements that would require declared objects be used.
The presence of const
does not affect any of the above.
… whether the standard allows this to become undefined behavior in any edge case.
Consider this program fragment:
#if MySwitch
const int a = 1, c = 3;
#else
const int a = 1, b = 2, c = 3;
#endif
int main(void)
{
if (&c == &a+1)
printf("Hello, world.\n");
else
printf("%d\n", INT_MAX + c);
}
It is possible that the program compiled with MySwitch
defined to be 1 observes that &c == &a+1
is true and prints “Hello, world.”, while the program compiled with MySwitch
defined to be 0 observes that &c == &a+1
is false (neither of these comparison results is guaranteed, but both may occur) evaluates an integer overflow and has undefined behavior. However, we would describe this program as flawed and the undefined behavior as due to relying on the memory locations of a
and c
, which are not guaranteed by the C standard. We would not attribute the failure to the fact that b
was defined.