From here[1]:
Switch statements without a default case can lead to unexpected
behavior and incomplete handling of all possible cases. When a switch
statement lacks a default case, if a value is encountered that does
not match any of the specified cases, the program will continue
execution without any defined behavior or handling.
Maybe I misunderstood that sentence caused by my bad English.
On 10/22/2025 8:44 AM, Richard Harnden wrote:
....
Your program fragment is well defined.Use an enum, and the compiler will warn you ...
What the poster certainly tried to express was that in case you
haven't implemented a complete list of all possible cases and
also not provided a 'default' to catch all non-specified cases,
then you might get in troubles with your program, probably by
possible oversights, future extensions, new data, and whatnot.
Personally I have the habit to always define a default branch,
and even if that default is impossible to reach you'll find an
error message (like "internal error with unexpected value...")
generated at that place.
$ cat x.c
#include <stdio.h>
enum x {A, B, C};
int main(void)
{
enum x x = C;
switch (x)
{
case A:
printf("A\n");
break;
case B:
printf("B\n");
break;
}
return 0;
}
$ gcc -Wall x.c
x.c: In function ‘main’:
x.c:9:9: warning: enumeration value ‘C’ not handled in switch [-Wswitch] >> 9 | switch (x)
| ^~~~~~
The problem with this GCC approach is when there are many enumerators
but only a few are used.
(case 3 (0 "zero") (1 "one") (2 "two"))NIL
(case 0 (0 "zero") (1 "one") (2 "two"))"zero"
(ecase 3 (0 "zero") (1 "one") (2 "two"))
On 22/10/2025 13:44, Richard Harnden wrote:
On 22/10/2025 10:32, Janis Papanagnou wrote:
On 22.10.2025 10:56, pozz wrote:
Switch statements without a default case can lead to unexpected
behavior and incomplete handling of all possible cases. When a switch >>>>> statement lacks a default case, if a value is encountered that does
not match any of the specified cases, the program will continue
execution without any defined behavior or handling.
Maybe I misunderstood that sentence caused by my bad English. I knew
that in case the switch value is not present in any case inside the
switch, the program continues without doing anything (in the switch) and >>>> without any problem.
int x = 3;
switch(x) {
case 1: printf("Hello");break;
case 2: printf("World");break;
}
Will the program execution continue without any defined behaviour?
Presumably you meant "without any undefined behaviour" ? The code is
fine - if no cases match and there is no default case, execution
continues from the end of the switch statement. Like most warnings,
this is about a possible bug in the code - not a definite one.
On 22/10/2025 15:56, Janis Papanagnou wrote:
On 22.10.2025 13:44, Richard Harnden wrote:
On 22/10/2025 10:32, Janis Papanagnou wrote:
On 22.10.2025 10:56, pozz wrote:Use an enum, and the compiler will warn you ...
Switch statements without a default case can lead to unexpected
behavior and incomplete handling of all possible cases. When a switch >>>>>> statement lacks a default case, if a value is encountered that does >>>>>> not match any of the specified cases, the program will continue
execution without any defined behavior or handling.
Maybe I misunderstood that sentence caused by my bad English. I knew >>>>> that in case the switch value is not present in any case inside the
switch, the program continues without doing anything (in the
switch) and
without any problem.
int x = 3;
switch(x) {
case 1: printf("Hello");break;
case 2: printf("World");break;
}
Will the program execution continue without any defined behaviour?
Your program fragment is well defined.
What the poster certainly tried to express was that in case you
haven't implemented a complete list of all possible cases and
also not provided a 'default' to catch all non-specified cases,
then you might get in troubles with your program, probably by
possible oversights, future extensions, new data, and whatnot.
Personally I have the habit to always define a default branch,
and even if that default is impossible to reach you'll find an
error message (like "internal error with unexpected value...")
generated at that place.
Maybe. Although I don't recall that the "C"-compilers I formerly
used checked enums as you've shown below. - But anyway...
Enums don't help if you use and compare against (for example)
characters that are (for example) got from an external source.
char * cmds = "CMDQ"; // 'D' maybe added later
char cmd;
...some n lines of code...
...get input cmd...
...optionally verify cmd with cmds...
...some more m lines of code...
switch (cmd) {
case 'C': ...;
case 'M': ...;
default: printf ("Error: uncaught cmd '%c'\n", cmd);
}
It's good to take precautions and define the 'default' case. YMMV.
That's not "taking precautions". If the "...optionally verify cmd" part
does a good job, then the default line is worse than useless because it
is code that never runs. [...]
On 22/10/2025 16:05, Janis Papanagnou wrote:
On 22.10.2025 15:41, David Brown wrote:
On 22/10/2025 13:44, Richard Harnden wrote:
On 22/10/2025 10:32, Janis Papanagnou wrote:
On 22.10.2025 10:56, pozz wrote:
Switch statements without a default case can lead to unexpected
behavior and incomplete handling of all possible cases. When a
switch
statement lacks a default case, if a value is encountered that does >>>>>>> not match any of the specified cases, the program will continue
execution without any defined behavior or handling.
Maybe I misunderstood that sentence caused by my bad English. I knew >>>>>> that in case the switch value is not present in any case inside the >>>>>> switch, the program continues without doing anything (in the switch) >>>>>> and
without any problem.
int x = 3;
switch(x) {
case 1: printf("Hello");break;
case 2: printf("World");break;
}
Will the program execution continue without any defined behaviour?
Presumably you meant "without any undefined behaviour" ? The code is
fine - if no cases match and there is no default case, execution
continues from the end of the switch statement. Like most warnings,
this is about a possible bug in the code - not a definite one.
Your program fragment is well defined.
What the poster certainly tried to express was that in case you
haven't implemented a complete list of all possible cases and
also not provided a 'default' to catch all non-specified cases,
then you might get in troubles with your program, probably by
possible oversights, future extensions, new data, and whatnot.
Personally I have the habit to always define a default branch,
and even if that default is impossible to reach you'll find an
error message (like "internal error with unexpected value...")
generated at that place.
I don't think it is normally appropriate to add a default case unless
you actually need it
Yes I was saying that I want it; I "need" it once errors slip in and
such a message or error log immediately clears the issue! (You might
be excluding some "needs" from your repertoire of necessities, okay.)
- code that exists but can never be reached is
untestable and can be confusing to people reading the code. But
sometimes it can be useful to add a "default : printf("Internal
error...");" for debugging, however.
This printf error message or log entry is what I suggested. It isn't
confusing because it even _documents_ what's the case here. Rather,
a missing default leaves the reader with an unnecessary uncertainty.
YMMV.
Indeed YMMV. But when I see a printf call that says something has gone horribly wrong,
I wonder /how/ that could happen.
I don't put such
statements into my code without it being a possible execution path.
I
agree, of course, that a good error message here makes the code somewhat self-documenting in terms of what it does.
But it does nothing to help say how it happened to run.
A printf call that never runs is not free -
it costs in many different ways, and should not be there unless it is
worth those costs.
David Brown <david.brown@hesbynett.no> writes:
On 22/10/2025 13:44, Richard Harnden wrote:
On 22/10/2025 10:32, Janis Papanagnou wrote:
On 22.10.2025 10:56, pozz wrote:
Switch statements without a default case can lead to unexpected
behavior and incomplete handling of all possible cases. When a switch >>>>>> statement lacks a default case, if a value is encountered that does >>>>>> not match any of the specified cases, the program will continue
execution without any defined behavior or handling.
Maybe I misunderstood that sentence caused by my bad English. I knew >>>>> that in case the switch value is not present in any case inside the
switch, the program continues without doing anything (in the switch) and >>>>> without any problem.
int x = 3;
switch(x) {
case 1: printf("Hello");break;
case 2: printf("World");break;
}
Will the program execution continue without any defined behaviour?
Presumably you meant "without any undefined behaviour" ? The code is
fine - if no cases match and there is no default case, execution
continues from the end of the switch statement. Like most warnings,
this is about a possible bug in the code - not a definite one.
No, pozz meant "without any defined behavior", which is a direct quote
from the cited document.
That document is poorly written. The phrase "without any defined
behavior" strongly implies that the behavior is undefined, which is
simply wrong.
It says:
When a switch statement lacks a default case, if a value is
encountered that does not match any of the specified cases, the
program will continue execution without any defined behavior or
handling.
It would be more accurate to say:
When a switch statement lacks a default case, if a value is
encountered that does not match any of the specified cases, the
switch statement will do nothing and the program will continue
execution without handling the value.
A warning might be warranted, but the behavior is well defined.
Note that the documentation is for the add-on tool clang-tidy, not for
the clang compiler.
I've submitted a bug report :
https://github.com/llvm/llvm-project/issues/164699
On 22.10.2025 17:25, David Brown wrote:
On 22/10/2025 15:56, Janis Papanagnou wrote:
On 22.10.2025 13:44, Richard Harnden wrote:
On 22/10/2025 10:32, Janis Papanagnou wrote:
On 22.10.2025 10:56, pozz wrote:Use an enum, and the compiler will warn you ...
Switch statements without a default case can lead to unexpected
behavior and incomplete handling of all possible cases. When a switch >>>>>>> statement lacks a default case, if a value is encountered that does >>>>>>> not match any of the specified cases, the program will continue
execution without any defined behavior or handling.
Maybe I misunderstood that sentence caused by my bad English. I knew >>>>>> that in case the switch value is not present in any case inside the >>>>>> switch, the program continues without doing anything (in the
switch) and
without any problem.
int x = 3;
switch(x) {
case 1: printf("Hello");break;
case 2: printf("World");break;
}
Will the program execution continue without any defined behaviour?
Your program fragment is well defined.
What the poster certainly tried to express was that in case you
haven't implemented a complete list of all possible cases and
also not provided a 'default' to catch all non-specified cases,
then you might get in troubles with your program, probably by
possible oversights, future extensions, new data, and whatnot.
Personally I have the habit to always define a default branch,
and even if that default is impossible to reach you'll find an
error message (like "internal error with unexpected value...")
generated at that place.
Maybe. Although I don't recall that the "C"-compilers I formerly
used checked enums as you've shown below. - But anyway...
Enums don't help if you use and compare against (for example)
characters that are (for example) got from an external source.
char * cmds = "CMDQ"; // 'D' maybe added later
char cmd;
...some n lines of code...
...get input cmd...
...optionally verify cmd with cmds...
...some more m lines of code...
switch (cmd) {
case 'C': ...;
case 'M': ...;
default: printf ("Error: uncaught cmd '%c'\n", cmd);
}
It's good to take precautions and define the 'default' case. YMMV.
That's not "taking precautions". If the "...optionally verify cmd" part
does a good job, then the default line is worse than useless because it
is code that never runs. [...]
Not sure what you expect in that line. I've had something like
strchr (cmds, cmd) in mind. And the 'switch' is independent
of that, so you could miss adding the switch branch of a later
added command character.
The point was that mistakes can be made, not only in the initial implementation but also if it gets extended later, and probably
by other folks than the original implementer. I also gave a hint
with "...some more m lines of code..." that such omissions may
also be hard to spot.
It is experience from professional real life projects that such
things happen. And blaming anyone that he's not done "a good job"
may be the appropriate diagnosis and important point if what you
want is primarily to blame someone, but if you want software to
get developed reliably, one element is to quickly spot the place
of such omissions.
On 2025-10-22, Thiago Adams <thiago.adams@gmail.com> wrote:
On 10/22/2025 8:44 AM, Richard Harnden wrote:
....
Your program fragment is well defined.Use an enum, and the compiler will warn you ...
What the poster certainly tried to express was that in case you
haven't implemented a complete list of all possible cases and
also not provided a 'default' to catch all non-specified cases,
then you might get in troubles with your program, probably by
possible oversights, future extensions, new data, and whatnot.
Personally I have the habit to always define a default branch,
and even if that default is impossible to reach you'll find an
error message (like "internal error with unexpected value...")
generated at that place.
$ cat x.c
#include <stdio.h>
enum x {A, B, C};
int main(void)
{
enum x x = C;
switch (x)
{
case A:
printf("A\n");
break;
case B:
printf("B\n");
break;
}
return 0;
}
$ gcc -Wall x.c
x.c: In function ‘main’:
x.c:9:9: warning: enumeration value ‘C’ not handled in switch [-Wswitch]
9 | switch (x)
| ^~~~~~
The problem with this GCC approach is when there are many enumerators
but only a few are used.
The problem with the C and GCC approach is that there is no
one-size-fits all solution.
Some switches are intended to be exhaustive, such that
missing a case is a bug.
Some are not.
You need an "eswitch" for the exhaustively handled enumerations, and
switch for the others.
GCC can turn on diagnostics over ranges of a file with pragma
and there is also _Pragram, but it's all too clumsy.
On 22.10.2025 17:23, David Brown wrote:
On 22/10/2025 16:05, Janis Papanagnou wrote:
But it does nothing to help say how it happened to run.
??? - The example scenario will run. Just may have erroneous results
when triggering the case that isn't handled. With diagnostic records
you can quickly identify and fix it (usually in one of the QA test
cycles before you deliver the software).
A printf call that never runs is not free -
it costs in many different ways, and should not be there unless it is
worth those costs.
(You're obviously a better discussion candidate for folks who count
bytes and microseconds, say, like bart. - The "costs" that matters
much more in professional software development are quality and time.)
David Brown <david.brown@hesbynett.no> writes:
On 22/10/2025 13:44, Richard Harnden wrote:
On 22/10/2025 10:32, Janis Papanagnou wrote:
On 22.10.2025 10:56, pozz wrote:
Switch statements without a default case can lead to unexpected
behavior and incomplete handling of all possible cases. When a switch >>>>>> statement lacks a default case, if a value is encountered that does >>>>>> not match any of the specified cases, the program will continue
execution without any defined behavior or handling.
Maybe I misunderstood that sentence caused by my bad English. I knew >>>>> that in case the switch value is not present in any case inside the
switch, the program continues without doing anything (in the switch) and >>>>> without any problem.
int x = 3;
switch(x) {
case 1: printf("Hello");break;
case 2: printf("World");break;
}
Will the program execution continue without any defined behaviour?
Presumably you meant "without any undefined behaviour" ? The code is
fine - if no cases match and there is no default case, execution
continues from the end of the switch statement. Like most warnings,
this is about a possible bug in the code - not a definite one.
No, pozz meant "without any defined behavior", which is a direct quote
from the cited document.
That document is poorly written. The phrase "without any defined
behavior" strongly implies that the behavior is undefined, which is
simply wrong.
It says:
When a switch statement lacks a default case, if a value is
encountered that does not match any of the specified cases, the
program will continue execution without any defined behavior or
handling.
It would be more accurate to say:
When a switch statement lacks a default case, if a value is
encountered that does not match any of the specified cases, the
switch statement will do nothing and the program will continue
execution without handling the value.
A warning might be warranted, but the behavior is well defined.
Note that the documentation is for the add-on tool clang-tidy, not for
the clang compiler.
I've submitted a bug report :
https://github.com/llvm/llvm-project/issues/164699
On 22/10/2025 20:22, Kaz Kylheku wrote:
On 2025-10-22, Thiago Adams <thiago.adams@gmail.com> wrote:
On 10/22/2025 8:44 AM, Richard Harnden wrote:
....
Your program fragment is well defined.Use an enum, and the compiler will warn you ...
What the poster certainly tried to express was that in case you
haven't implemented a complete list of all possible cases and
also not provided a 'default' to catch all non-specified cases,
then you might get in troubles with your program, probably by
possible oversights, future extensions, new data, and whatnot.
Personally I have the habit to always define a default branch,
and even if that default is impossible to reach you'll find an
error message (like "internal error with unexpected value...")
generated at that place.
$ cat x.c
#include <stdio.h>
enum x {A, B, C};
int main(void)
{
enum x x = C;
switch (x)
{
case A:
printf("A\n");
break;
case B:
printf("B\n");
break;
}
return 0;
}
$ gcc -Wall x.c
x.c: In function ‘main’:
x.c:9:9: warning: enumeration value ‘C’ not handled in switch [-
Wswitch]
9 | switch (x)
| ^~~~~~
The problem with this GCC approach is when there are many enumerators
but only a few are used.
The problem with the C and GCC approach is that there is no
one-size-fits all solution.
Some switches are intended to be exhaustive, such that
missing a case is a bug.
Some are not.
You need an "eswitch" for the exhaustively handled enumerations, and
switch for the others.
GCC can turn on diagnostics over ranges of a file with pragma
and there is also _Pragram, but it's all too clumsy.
The gcc approach works fine in almost all situations - use "- Wswitch=error", and add a default case if your switch is not meant to
handle all enumeration values. If the default should do nothing, it's
just "default: // Not all cases need handling". If the default should never happen, "default: __builtin_unreachable();" or "default: __builtin_trap();" might be appropriate.
On 10/23/2025 4:12 AM, David Brown wrote:
On 22/10/2025 20:22, Kaz Kylheku wrote:
On 2025-10-22, Thiago Adams <thiago.adams@gmail.com> wrote:
On 10/22/2025 8:44 AM, Richard Harnden wrote:
....
Your program fragment is well defined.Use an enum, and the compiler will warn you ...
What the poster certainly tried to express was that in case you
haven't implemented a complete list of all possible cases and
also not provided a 'default' to catch all non-specified cases,
then you might get in troubles with your program, probably by
possible oversights, future extensions, new data, and whatnot.
Personally I have the habit to always define a default branch,
and even if that default is impossible to reach you'll find an
error message (like "internal error with unexpected value...")
generated at that place.
$ cat x.c
#include <stdio.h>
enum x {A, B, C};
int main(void)
{
enum x x = C;
switch (x)
{
case A:
printf("A\n");
break;
case B:
printf("B\n");
break;
}
return 0;
}
$ gcc -Wall x.c
x.c: In function ‘main’:
x.c:9:9: warning: enumeration value ‘C’ not handled in switch [- >>>>> Wswitch]
9 | switch (x)
| ^~~~~~
The problem with this GCC approach is when there are many enumerators
but only a few are used.
The problem with the C and GCC approach is that there is no
one-size-fits all solution.
Some switches are intended to be exhaustive, such that
missing a case is a bug.
Some are not.
You need an "eswitch" for the exhaustively handled enumerations, and
switch for the others.
GCC can turn on diagnostics over ranges of a file with pragma
and there is also _Pragram, but it's all too clumsy.
The gcc approach works fine in almost all situations - use "-
Wswitch=error", and add a default case if your switch is not meant to
handle all enumeration values. If the default should do nothing, it's
just "default: // Not all cases need handling". If the default should
never happen, "default: __builtin_unreachable();" or "default:
__builtin_trap();" might be appropriate.
But then instead a compiler time error (like I suggest) you leave it for runtime.
On 23/10/2025 13:03, Thiago Adams wrote:
On 10/23/2025 4:12 AM, David Brown wrote:
On 22/10/2025 20:22, Kaz Kylheku wrote:
On 2025-10-22, Thiago Adams <thiago.adams@gmail.com> wrote:
On 10/22/2025 8:44 AM, Richard Harnden wrote:
....
Your program fragment is well defined.Use an enum, and the compiler will warn you ...
What the poster certainly tried to express was that in case you
haven't implemented a complete list of all possible cases and
also not provided a 'default' to catch all non-specified cases,
then you might get in troubles with your program, probably by
possible oversights, future extensions, new data, and whatnot.
Personally I have the habit to always define a default branch,
and even if that default is impossible to reach you'll find an
error message (like "internal error with unexpected value...")
generated at that place.
$ cat x.c
#include <stdio.h>
enum x {A, B, C};
int main(void)
{
enum x x = C;
switch (x)
{
case A:
printf("A\n");
break;
case B:
printf("B\n");
break;
}
return 0;
}
$ gcc -Wall x.c
x.c: In function ‘main’:
x.c:9:9: warning: enumeration value ‘C’ not handled in switch [- >>>>>> Wswitch]
9 | switch (x)
| ^~~~~~
The problem with this GCC approach is when there are many enumerators >>>>> but only a few are used.
The problem with the C and GCC approach is that there is no
one-size-fits all solution.
Some switches are intended to be exhaustive, such that
missing a case is a bug.
Some are not.
You need an "eswitch" for the exhaustively handled enumerations, and >>>> switch for the others.
GCC can turn on diagnostics over ranges of a file with pragma
and there is also _Pragram, but it's all too clumsy.
The gcc approach works fine in almost all situations - use "-
Wswitch=error", and add a default case if your switch is not meant to
handle all enumeration values. If the default should do nothing,
it's just "default: // Not all cases need handling". If the default
should never happen, "default: __builtin_unreachable();" or "default:
__builtin_trap();" might be appropriate.
But then instead a compiler time error (like I suggest) you leave it
for runtime.
As I said - use "-Wswitch=error". That gives you a compile-time error - not merely a warning, as you had suggested. But /if/ your switch is not meant to handle all cases, which was what Kaz was complaining about,
then you add a default case. I agree with you that a compile-time error
is best when possible, which is why that was my suggestion.
On 10/23/2025 12:06 PM, David Brown wrote:
One alternative is to use default for all the non used:
2)
void f(enum E e)
{
switch (e)
{
//used
case A:
case B:
break;
//NON USED (all others)
default:
break;
};
}
The problem with (2) is when we add a new enumerator and
this new enumerations should be used,
In C2Y the new keyword _Countof was introduced.
It works returns the number of elements of array. IT IS FOR ARRAY ONLY.
I did an EXTENSION in my compiler where _Countof(enum E) also returns
the number of enumerators.
enum E2 {A, B};
static_assert(_Countof(enum E2) == 2);
(It also could be a new keyword.
static_assert(_EnumCount(enum E2) == 2);)
Having this we can do:
3)
void f(enum E e)
{
switch (e)
{
//used
case A:
case B:
break;
default:
static_assert(_EnumCount(enum E2) == 20);
break;
};
}
Then when adding a new enumerator the programmer will have to review
this code and update to 21 if it is not used, or handle it in a new case.
This is also useful in other scenarios. For instance:
enum E parse_enum_e(const char* s)
{
if (strcmp(s, "A") == 0) return A;
if (strcmp(s, "B") == 0) return B;
if (strcmp(s, "C") == 0) return C;
if (strcmp(s, "D") == 0) return D;
if (strcmp(s, "E") == 0) return E;
if (strcmp(s, "F") == 0) return F;
static_assert(_Countof(enum E) == 6);
return A;
}
If a new enumerator is added we need to include it.
On 23/10/2025 17:15, Thiago Adams wrote:
On 10/23/2025 12:06 PM, David Brown wrote:
One alternative is to use default for all the non used:
2)
void f(enum E e)
{
switch (e)
{
//used
case A:
case B:
break;
//NON USED (all others)
default:
break;
};
}
The problem with (2) is when we add a new enumerator and
this new enumerations should be used,
How does the compiler know whether it should be used or not?
And if not, how do you stop the warning? (Of non-exhaustive checking, if there is one.)
In C2Y the new keyword _Countof was introduced.
It works returns the number of elements of array. IT IS FOR ARRAY ONLY.
I did an EXTENSION in my compiler where _Countof(enum E) also returns
the number of enumerators.
enum E2 {A, B};
static_assert(_Countof(enum E2) == 2);
(It also could be a new keyword.
static_assert(_EnumCount(enum E2) == 2);)
Having this we can do:
3)
void f(enum E e)
{
switch (e)
{
//used
case A:
case B:
break;
default:
static_assert(_EnumCount(enum E2) == 20);
break;
};
}
Then when adding a new enumerator the programmer will have to review
this code and update to 21 if it is not used, or handle it in a new case.
It sounds limited. What if part of the set of enums is conditional? Then that '20' can vary depending on some macro value.
But having a hard-coded 20 is also problematical; do you have to painstakingly count maybe 200 enumerations? And then keep it maintained? What if you put in the wrong number?
What if you want to temporarily comment out some of those cases, or some
of the enums?
There may also be muliple 'switch' statements working on the same set of enums, which may need to check a different subset.
Anyway, I don't see how this helps with reporting whether a enum that
should be in a 'case' label is missing, or vice versa, when it should be checked.
This is also useful in other scenarios. For instance:
enum E parse_enum_e(const char* s)
{
if (strcmp(s, "A") == 0) return A;
if (strcmp(s, "B") == 0) return B;
if (strcmp(s, "C") == 0) return C;
if (strcmp(s, "D") == 0) return D;
if (strcmp(s, "E") == 0) return E;
if (strcmp(s, "F") == 0) return F;
static_assert(_Countof(enum E) == 6);
return A;
}
If a new enumerator is added we need to include it.
Another hard-coded value! An anti-pattern I think. Most of what I said
above applies here. You have N enum values, you have N checking lines,
and you have that static assert on N. But what happens if you leave out
a checking line, or cidentally have the D line twice then check F?
What if you forget to update it to 7, or write it as 5 anyway?
It looks like a weak check that also adds more opportunities for error.
I think getting the number of values of an enum type has some uses: you
can use to iterate over the values, when they start from zero and are consecutive.
Or it can iterate over arrays indexed by the enum. And it can be used to
set the bounds of such arrays. These sound more useful and more reliable than your asserts!
On 10/23/2025 12:06 PM, David Brown wrote:
On 23/10/2025 13:03, Thiago Adams wrote:
On 10/23/2025 4:12 AM, David Brown wrote:
On 22/10/2025 20:22, Kaz Kylheku wrote:
On 2025-10-22, Thiago Adams <thiago.adams@gmail.com> wrote:
On 10/22/2025 8:44 AM, Richard Harnden wrote:
....
Your program fragment is well defined.Use an enum, and the compiler will warn you ...
What the poster certainly tried to express was that in case you >>>>>>>> haven't implemented a complete list of all possible cases and
also not provided a 'default' to catch all non-specified cases, >>>>>>>> then you might get in troubles with your program, probably by
possible oversights, future extensions, new data, and whatnot. >>>>>>>>
Personally I have the habit to always define a default branch, >>>>>>>> and even if that default is impossible to reach you'll find an >>>>>>>> error message (like "internal error with unexpected value...") >>>>>>>> generated at that place.
$ cat x.c
#include <stdio.h>
enum x {A, B, C};
int main(void)
{
enum x x = C;
switch (x)
{
case A:
printf("A\n");
break;
case B:
printf("B\n");
break;
}
return 0;
}
$ gcc -Wall x.c
x.c: In function ‘main’:
x.c:9:9: warning: enumeration value ‘C’ not handled in switch [- >>>>>>> Wswitch]
9 | switch (x)
| ^~~~~~
The problem with this GCC approach is when there are many enumerators >>>>>> but only a few are used.
The problem with the C and GCC approach is that there is no
one-size-fits all solution.
Some switches are intended to be exhaustive, such that
missing a case is a bug.
Some are not.
You need an "eswitch" for the exhaustively handled enumerations, and >>>>> switch for the others.
GCC can turn on diagnostics over ranges of a file with pragma
and there is also _Pragram, but it's all too clumsy.
The gcc approach works fine in almost all situations - use "-
Wswitch=error", and add a default case if your switch is not meant
to handle all enumeration values. If the default should do nothing, >>>> it's just "default: // Not all cases need handling". If the default >>>> should never happen, "default: __builtin_unreachable();" or
"default: __builtin_trap();" might be appropriate.
But then instead a compiler time error (like I suggest) you leave it
for runtime.
As I said - use "-Wswitch=error". That gives you a compile-time error
- not merely a warning, as you had suggested. But /if/ your switch is
not meant to handle all cases, which was what Kaz was complaining
about, then you add a default case. I agree with you that a compile-
time error is best when possible, which is why that was my suggestion.
I think my sample is not covered by any GCC flag
I will copy past it again:
--------
The problem with this GCC approach is when there are many enumerators
but only a few are used.
For instance :
enum E {A, B, C /*, ...*/, Z};
1)
void f(enum E e)
{
switch (e)
{
//used
case A:
case B:
break;
//NON USED
case C:
...
case Z:
break;
};
}
The problem with (1) is when we have too many
non used enumerators, it is impractical to have a lot of switch cases.
One alternative is to use default for all the non used:
2)
void f(enum E e)
{
switch (e)
{
//used
case A:
case B:
break;
//NON USED (all others)
default:
break;
};
}
The problem with (2) is when we add a new enumerator and
this new enumerations should be used, but there is no warning and it
goes accidentally for default.
Solution?
In C2Y the new keyword _Countof was introduced.
It works returns the number of elements of array. IT IS FOR ARRAY ONLY.
I did an EXTENSION in my compiler where _Countof(enum E) also returns
the number of enumerators.
enum E2 {A, B};
static_assert(_Countof(enum E2) == 2);
(It also could be a new keyword.
static_assert(_EnumCount(enum E2) == 2);)
Having this we can do:
3)
void f(enum E e)
{
switch (e)
{
//used
case A:
case B:
break;
default:
static_assert(_EnumCount(enum E2) == 20);
break;
};
}
On 23/10/2025 18:15, Thiago Adams wrote:
On 10/23/2025 12:06 PM, David Brown wrote:
People do this regularly. Personally, I usually prefer to have the
"last", "max", or "count" indicator outside the enumeration :
enum E1 { A, B, C, D };
static const int count_of_E1 = D;
Personally, I usually prefer to have the "last", "max", or "count"
indicator outside the enumeration :
enum E1 { A, B, C, D };
static const int count_of_E1 = D;
On 22/10/2025 16:05, Janis Papanagnou wrote:
On 22.10.2025 15:41, David Brown wrote:
On 22/10/2025 13:44, Richard Harnden wrote:
On 22/10/2025 10:32, Janis Papanagnou wrote:
On 22.10.2025 10:56, pozz wrote:
Switch statements without a default case can lead to unexpected
behavior and incomplete handling of all possible cases. When a switch >>>>>>> statement lacks a default case, if a value is encountered that does >>>>>>> not match any of the specified cases, the program will continue
execution without any defined behavior or handling.
Maybe I misunderstood that sentence caused by my bad English. I knew >>>>>> that in case the switch value is not present in any case inside the >>>>>> switch, the program continues without doing anything (in the switch) >>>>>> and
without any problem.
int x = 3;
switch(x) {
case 1: printf("Hello");break;
case 2: printf("World");break;
}
Will the program execution continue without any defined behaviour?
Presumably you meant "without any undefined behaviour" ? The code is
fine - if no cases match and there is no default case, execution
continues from the end of the switch statement. Like most warnings,
this is about a possible bug in the code - not a definite one.
Your program fragment is well defined.
What the poster certainly tried to express was that in case you
haven't implemented a complete list of all possible cases and
also not provided a 'default' to catch all non-specified cases,
then you might get in troubles with your program, probably by
possible oversights, future extensions, new data, and whatnot.
Personally I have the habit to always define a default branch,
and even if that default is impossible to reach you'll find an
error message (like "internal error with unexpected value...")
generated at that place.
I don't think it is normally appropriate to add a default case unless
you actually need it
Yes I was saying that I want it; I "need" it once errors slip in and
such a message or error log immediately clears the issue! (You might
be excluding some "needs" from your repertoire of necessities, okay.)
- code that exists but can never be reached is
untestable and can be confusing to people reading the code. But
sometimes it can be useful to add a "default : printf("Internal
error...");" for debugging, however.
This printf error message or log entry is what I suggested. It isn't
confusing because it even _documents_ what's the case here. Rather,
a missing default leaves the reader with an unnecessary uncertainty.
YMMV.
Indeed YMMV. But when I see a printf call that says something has gone horribly wrong, I wonder /how/ that could happen. I don't put such statements into my code without it being a possible execution path.
I
agree, of course, that a good error message here makes the code somewhat self-documenting in terms of what it does. But it does nothing to help
say how it happened to run. A printf call that never runs is not free -
it costs in many different ways, and should not be there unless it is
worth those costs.
Il 22/10/2025 21:41, Keith Thompson ha scritto:[...]
That document is poorly written. The phrase "without any defined
behavior" strongly implies that the behavior is undefined, which is
simply wrong.
It says:
When a switch statement lacks a default case, if a value is
encountered that does not match any of the specified cases, the
program will continue execution without any defined behavior or
handling.
It would be more accurate to say:
When a switch statement lacks a default case, if a value is
encountered that does not match any of the specified cases, the
switch statement will do nothing and the program will continue
execution without handling the value.
A warning might be warranted, but the behavior is well defined.
It is exactly what i wanted to read. Thanks for the explanation.
Note that the documentation is for the add-on tool clang-tidy, not for
the clang compiler.
Sure
I've submitted a bug report :
https://github.com/llvm/llvm-project/issues/164699
David Brown <david.brown@hesbynett.no> writes:
On 23/10/2025 18:15, Thiago Adams wrote:
On 10/23/2025 12:06 PM, David Brown wrote:
People do this regularly. Personally, I usually prefer to have the >>"last", "max", or "count" indicator outside the enumeration :
enum E1 { A, B, C, D };
static const int count_of_E1 = D;
D has the value '3', yet there are four elements in the
set. Perhaps the name should be count_of_E1_minus_1?
How do you use count_of_E1 in this case?
Too bad 'sizeof' doesn't work on typed enums.
On 10/23/2025 12:06 PM, David Brown wrote:
On 23/10/2025 13:03, Thiago Adams wrote:
On 10/23/2025 4:12 AM, David Brown wrote:
On 22/10/2025 20:22, Kaz Kylheku wrote:
On 2025-10-22, Thiago Adams <thiago.adams@gmail.com> wrote:
On 10/22/2025 8:44 AM, Richard Harnden wrote:
....
Your program fragment is well defined.Use an enum, and the compiler will warn you ...
What the poster certainly tried to express was that in case you >>>>>>>> haven't implemented a complete list of all possible cases and
also not provided a 'default' to catch all non-specified cases, >>>>>>>> then you might get in troubles with your program, probably by
possible oversights, future extensions, new data, and whatnot. >>>>>>>>
Personally I have the habit to always define a default branch, >>>>>>>> and even if that default is impossible to reach you'll find an >>>>>>>> error message (like "internal error with unexpected value...") >>>>>>>> generated at that place.
$ cat x.c
#include <stdio.h>
enum x {A, B, C};
int main(void)
{
enum x x = C;
switch (x)
{
case A:
printf("A\n");
break;
case B:
printf("B\n");
break;
}
return 0;
}
$ gcc -Wall x.c
x.c: In function ‘main’:
x.c:9:9: warning: enumeration value ‘C’ not handled in switch [- >>>>>>> Wswitch]
9 | switch (x)
| ^~~~~~
The problem with this GCC approach is when there are many enumerators >>>>>> but only a few are used.
The problem with the C and GCC approach is that there is no
one-size-fits all solution.
Some switches are intended to be exhaustive, such that
missing a case is a bug.
Some are not.
You need an "eswitch" for the exhaustively handled enumerations, and >>>>> switch for the others.
GCC can turn on diagnostics over ranges of a file with pragma
and there is also _Pragram, but it's all too clumsy.
The gcc approach works fine in almost all situations - use "-
Wswitch=error", and add a default case if your switch is not meant to >>>> handle all enumeration values. If the default should do nothing,
it's just "default: // Not all cases need handling". If the default >>>> should never happen, "default: __builtin_unreachable();" or "default: >>>> __builtin_trap();" might be appropriate.
But then instead a compiler time error (like I suggest) you leave it
for runtime.
As I said - use "-Wswitch=error". That gives you a compile-time error - >> not merely a warning, as you had suggested. But /if/ your switch is not >> meant to handle all cases, which was what Kaz was complaining about,
then you add a default case. I agree with you that a compile-time error >> is best when possible, which is why that was my suggestion.
I think my sample is not covered by any GCC flag
I will copy past it again:
--------
The problem with this GCC approach is when there are many enumerators
but only a few are used.
For instance :
enum E {A, B, C /*, ...*/, Z};
1)
void f(enum E e)
{
switch (e)
{
//used
case A:
case B:
break;
//NON USED
case C:
...
case Z:
break;
};
}
The problem with (1) is when we have too many
non used enumerators, it is impractical to have a lot of switch cases.
One alternative is to use default for all the non used:
2)
void f(enum E e)
{
switch (e)
{
//used
case A:
case B:
break;
//NON USED (all others)
default:
break;
};
}
The problem with (2) is when we add a new enumerator and
this new enumerations should be used, but there is no warning and it
goes accidentally for default.
Solution?
In C2Y the new keyword _Countof was introduced.
It works returns the number of elements of array. IT IS FOR ARRAY ONLY.
I did an EXTENSION in my compiler where _Countof(enum E) also returns
the number of enumerators.
enum E2 {A, B};
static_assert(_Countof(enum E2) == 2);
(It also could be a new keyword.
static_assert(_EnumCount(enum E2) == 2);)
Having this we can do:
3)
void f(enum E e)
{
switch (e)
{
//used
case A:
case B:
break;
default:
static_assert(_EnumCount(enum E2) == 20);
break;
};
}
Then when adding a new enumerator the programmer will have to review
this code and update to 21 if it is not used, or handle it in a new case.
This is also useful in other scenarios. For instance:
enum E parse_enum_e(const char* s)
{
if (strcmp(s, "A") == 0) return A;
if (strcmp(s, "B") == 0) return B;
if (strcmp(s, "C") == 0) return C;
if (strcmp(s, "D") == 0) return D;
if (strcmp(s, "E") == 0) return E;
if (strcmp(s, "F") == 0) return F;
static_assert(_Countof(enum E) == 6);
return A;
}
If a new enumerator is added we need to include it.
On 22/10/2025 15:56, Janis Papanagnou wrote:[...]
switch (cmd) {
case 'C': ...;
case 'M': ...;
default: printf ("Error: uncaught cmd '%c'\n", cmd);
}
It's good to take precautions and define the 'default' case. YMMV.
That's not "taking precautions". If the "...optionally verify cmd"
part does a good job, then the default line is worse than useless
because it is code that never runs. If that part doesn't exist (since
it is "optional"), then the default line is not "taking precautions",
it is normal handling of a realistic situation.
pozz <pozzugno@gmail.com> writes:
Il 22/10/2025 21:41, Keith Thompson ha scritto:[...]
That document is poorly written. The phrase "without any defined
behavior" strongly implies that the behavior is undefined, which is
simply wrong.
It says:
When a switch statement lacks a default case, if a value is
encountered that does not match any of the specified cases, the
program will continue execution without any defined behavior or
handling.
It would be more accurate to say:
When a switch statement lacks a default case, if a value is
encountered that does not match any of the specified cases, the
switch statement will do nothing and the program will continue
execution without handling the value.
A warning might be warranted, but the behavior is well defined.
It is exactly what i wanted to read. Thanks for the explanation.
Note that the documentation is for the add-on tool clang-tidy, not for
the clang compiler.
Sure
I've submitted a bug report :
https://github.com/llvm/llvm-project/issues/164699
My fix for this has been accepted into the llvm-project git repo.
The web page https://clang.llvm.org/extra/clang-tidy/checks/bugprone/switch-missing-default-case.html
has not yet been updated (not surprisingly).
David Brown <david.brown@hesbynett.no> writes:
On 23/10/2025 18:15, Thiago Adams wrote:
On 10/23/2025 12:06 PM, David Brown wrote:
People do this regularly. Personally, I usually prefer to have the
"last", "max", or "count" indicator outside the enumeration :
enum E1 { A, B, C, D };
static const int count_of_E1 = D;
D has the value '3', yet there are four elements in the
set. Perhaps the name should be count_of_E1_minus_1?
How do you use count_of_E1 in this case?
Too bad 'sizeof' doesn't work on typed enums.
On 10/23/25 22:40, David Brown wrote:
Personally, I usually prefer to have the "last", "max", or "count"
indicator outside the enumeration :
enum E1 { A, B, C, D };
static const int count_of_E1 = D;
Why did you use 'static' here ?
Thiago Adams <thiago.adams@gmail.com> wrote:
On 10/23/2025 12:06 PM, David Brown wrote:
On 23/10/2025 13:03, Thiago Adams wrote:
Then when adding a new enumerator the programmer will have to review
this code and update to 21 if it is not used, or handle it in a new case.
This helps catch some switches that need attention, but will miss
situations where you need to change handling of some existing code,
more precisely, when you need to remove case from some switches and
add it to different ones.
In similar cases I usually depend on a different approach: I am
writing my types in a way that can be found by textual searches.
If there is any change to usage pattern I search for all uses
of given type (I may also wrap enum inside otherwise useless
struct to make sure that all uses need declaration).
David Brown <david.brown@hesbynett.no> writes:
On 22/10/2025 15:56, Janis Papanagnou wrote:[...]
switch (cmd) {
case 'C': ...;
case 'M': ...;
default: printf ("Error: uncaught cmd '%c'\n", cmd);
}
It's good to take precautions and define the 'default' case. YMMV.
That's not "taking precautions". If the "...optionally verify cmd"
part does a good job, then the default line is worse than useless
because it is code that never runs. If that part doesn't exist (since
it is "optional"), then the default line is not "taking precautions",
it is normal handling of a realistic situation.
Error handling can be complicated.
You're right that code that prints a message for a condition that
should never happen (equivalently, that can only happen as a result
of a programming error) is difficult to test. (I suppose you could
tweak the code to introduce an error so the message is triggered,
but that's ugly and difficult to automate.)
For example, the failure of the first Ariane 5 launch involved
an unexpected error diagnostic message being interpreted as data.
If the error had been quietly ignored, the rocket might have survived.
If a condition is really expected never to happen, something
like gcc's _builtin_unreachable might be useful, or more portably
an assert.
If you want to test for a condition that should never happen, you
need to think about how you would want to handle it. If the best
way to handle it is to abort the application, that's easy enough.
If it's a safety critical system, though, it might be better to
attempt to log an error message (in a way that won't itself cause
a problem) and try to continue running.
And yes, handling a condition that can actually happen (say, in the
presence of bad input) is quite different.
On 23/10/2025 22:51, Scott Lurndal wrote:
David Brown <david.brown@hesbynett.no> writes:
On 23/10/2025 18:15, Thiago Adams wrote:
On 10/23/2025 12:06 PM, David Brown wrote:
People do this regularly. Personally, I usually prefer to have the
"last", "max", or "count" indicator outside the enumeration :
enum E1 { A, B, C, D };
static const int count_of_E1 = D;
D has the value '3', yet there are four elements in the
set. Perhaps the name should be count_of_E1_minus_1?
Argh! That's the danger of writing code directly in Usenet posts - it's >easy to make the silliest of mistakes. Clearly "D" is fine for "max" or >"last", but a "count" would want "D + 1".
How do you use count_of_E1 in this case?
Too bad 'sizeof' doesn't work on typed enums.
Sizeof does work on enumerated types :
| Sysop: | DaiTengu |
|---|---|
| Location: | Appleton, WI |
| Users: | 1,076 |
| Nodes: | 10 (1 / 9) |
| Uptime: | 81:28:01 |
| Calls: | 13,805 |
| Files: | 186,990 |
| D/L today: |
7,299 files (2,453M bytes) |
| Messages: | 2,443,305 |