• Re: bugprone-switch-missing-default-case

    From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Wed Oct 22 18:13:41 2025
    From Newsgroup: comp.lang.c

    On 2025-10-22, pozz <pozzugno@gmail.com> wrote:

    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.

    It is just slop written by someone who is new to the subject matter.

    "defined behavior" is a radioactive term that you simply don't
    use so carelessly, when documenting a C implementation.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Wed Oct 22 18:22:50 2025
    From Newsgroup: comp.lang.c

    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.

    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.

    Use an enum, and the compiler will warn you ...

    $ 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.

    In Common Lisp we have case vs ecase:

    (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"))

    *** - The value of 3 must be one of 0, 1, 2
    The value is: 3

    If falling through without hitting a case would be wrong, you use ecase; problem solved.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Wed Oct 22 12:41:49 2025
    From Newsgroup: comp.lang.c

    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
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Thu Oct 23 04:39:17 2025
    From Newsgroup: comp.lang.c

    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:

    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.

    Use an enum, and the compiler will warn you ...

    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.

    Janis

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Thu Oct 23 04:54:18 2025
    From Newsgroup: comp.lang.c

    On 22.10.2025 17:23, David Brown wrote:
    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,

    "has gone horribly wrong"? - It says nothing like that. - As I already
    said it's an effective measure to be able to quickly spot errors that
    slipped in.

    I wonder /how/ that could happen.

    (In the world I live and worked in I saw mistakes and errors happen.)

    I don't put such
    statements into my code without it being a possible execution path.

    (Do what you prefer in your personal context. - I don't mind.)

    I
    agree, of course, that a good error message here makes the code somewhat self-documenting in terms of what it does.

    (Glad to hear you see and accept that.)

    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.)

    Janis

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From pozz@pozzugno@gmail.com to comp.lang.c on Thu Oct 23 08:47:16 2025
    From Newsgroup: comp.lang.c

    Il 22/10/2025 21:41, Keith Thompson ha scritto:
    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.

    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


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Thu Oct 23 09:08:19 2025
    From Newsgroup: comp.lang.c

    On 23/10/2025 04:39, Janis Papanagnou wrote:
    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:

    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.

    Use an enum, and the compiler will warn you ...

    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.


    It is not about placing blame - it is about being careful to avoid mistakes.

    As you said, and I agreed, YMMV, and the details will depend on the type
    of project and type of development environment you have. There is a difference if "... some more lines of code..." means three lines in the
    same function, or three thousand lines over the course of five years and
    ten different developers.

    I am not against putting in extra checks to catch mistakes that are
    realistic. I am against putting in extra checks by /habit/, which is
    what you said in your first post in this branch. Habit implies lack of thought and consideration for the context. Sometimes habits are good,
    and help you avoid forgetting important things, but they can also be bad because you avoid the thought and analysis that is appropriate, and you
    can easily end up with something that looks "better" or "safer", but is actually worse in many ways. And it can reduce the likelihood of using
    better solutions (such as enumerated types with -Wswitch=error checking
    to turn missing cases into hard compiler errors). Even just a couple of comments at each part of the code "if you change this code, remember to
    change that code to match" may be better than unnecessary default cases.

    And I am against any code that will never run. Such code is untestable,
    and can quickly become a maintenance pain - people who see it can be
    left wondering what it is expected to do, and why it is there, and how
    it should be changed when other code is changed. An unnecessary printf
    that is never called can turn a simple and efficient function into a
    bigger and slower one, and turn a "pure" function into one with
    side-effects which can spoil code analysis, thread safety, and
    optimisations.

    So by all means add a default clause with an error printout if it is
    realistic for someone to use the code incorrectly, or if you are
    debugging. And certainly check for bad data if the data is coming from outside (like user input). But don't do it as a "habit", don't do it if
    the default case can never be triggered, don't do it if it would be
    better to check the data in other ways, and don't do it if you can use
    other techniques that catch potential problems earlier (like using enumerations).






    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Thu Oct 23 09:12:51 2025
    From Newsgroup: comp.lang.c

    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.

    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.

    Use an enum, and the compiler will warn you ...

    $ 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.



    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Thu Oct 23 09:30:46 2025
    From Newsgroup: comp.lang.c

    On 23/10/2025 04:54, Janis Papanagnou wrote:
    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).


    There may have been a misunderstanding here about what your various
    "..." lines before the switch statement did. I understood them to be
    checking the input data for validity, in which case the default case is unnecessary and will not be run. But if the switch itself is checking
    the input data, then the default case is an integral part of that check.
    It's not an unnecessary or extra default added by habit, it's just
    normal usage.

    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.)


    As a professional software developer, costs matter - different kinds of
    costs, scaled differently in different situations. Yes, developer costs matter. So do run-time costs. If developer costs are the only thing
    that matters and run-time costs do not, then why are you programming in
    C? There can be a number of good reasons for choosing C over, say,
    Python, but run-time efficiency for at least part of the code is very
    often a major reason.

    I work mainly on small-system embedded devices. A printf is a very
    expensive operation in many ways. I'll use it if it is the right thing
    to use, and not if is inappropriate. For some of my systems, being
    slap-dash about printf's and the like means the code won't fit in the microcontroller's flash, and we need a new hardware design (at very significant developer cost). Not long ago I wrote some code that had to
    run in less than a tenth of a microsecond - adding an extra printf, even
    if it was never called, would have added overhead to the function which
    would have exceeded the timing requirements. Timing failures here would
    mean shoot-through of power transistors and blowing the boards.

    Premature optimisation is the root of all evil, but premature
    pessimisation is not good either. You have to know when code needs to
    be efficient, and pick different balances between developer costs and
    run-time costs according to the situation.



    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Thu Oct 23 10:44:46 2025
    From Newsgroup: comp.lang.c

    On 22/10/2025 21:41, Keith Thompson wrote:
    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.


    OK.

    That document is poorly written. The phrase "without any defined
    behavior" strongly implies that the behavior is undefined, which is
    simply wrong.

    I guess I owe Pozz an apology for thinking he had made a little typo,
    when in fact it was a clear error from the clang-tidy folk! Thanks for clearing that up.


    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


    It looks like your patch has been accepted, as far as I could tell.
    Very helpful!



    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Thiago Adams@thiago.adams@gmail.com to comp.lang.c on Thu Oct 23 08:03:00 2025
    From Newsgroup: comp.lang.c

    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.

    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.

    Use an enum, and the compiler will warn you ...

    $ 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.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Thu Oct 23 17:06:02 2025
    From Newsgroup: comp.lang.c

    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.

    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.

    Use an enum, and the compiler will warn you ...

    $ 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.



    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Thiago Adams@thiago.adams@gmail.com to comp.lang.c on Thu Oct 23 13:15:30 2025
    From Newsgroup: comp.lang.c

    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.

    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.

    Use an enum, and the compiler will warn you ...

    $ 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.

    --------
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From bart@bc@freeuk.com to comp.lang.c on Thu Oct 23 17:46:48 2025
    From Newsgroup: comp.lang.c

    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!
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Thiago Adams@thiago.adams@gmail.com to comp.lang.c on Thu Oct 23 14:12:31 2025
    From Newsgroup: comp.lang.c

    On 10/23/2025 1:46 PM, bart wrote:
    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!


    I am trying to avoid having to answer each question (but if necessary I
    can do that)

    Consider:

    bool is_free(enum product product)
    {
    switch (procuct)
    {
    case combo1:
    case combo2:
    return true;
    default:
    break;
    }
    static_assert(_countof(enum product) == 100);
    return false;
    }

    I have 100 products (or more), just few are free.
    I have this function that return true is the product is free.

    If I add a new product I need update this function. The new product can
    be free or not. default: assert(false) only catches the problem in runtime.
    How to count? You can print _countof(enum product)




    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Thu Oct 23 22:40:49 2025
    From Newsgroup: comp.lang.c

    On 23/10/2025 18:15, Thiago Adams wrote:
    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.

    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.

    Use an enum, and the compiler will warn you ...

    $ 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;
        };

    }

    I appreciate that enumerations in C are missing a lot of potential
    compared to enumerations in some other languages (they could be real new
    types that do not implicitly convert with "int", they could have a
    "count" feature, they could have a feature for having the identifier
    names in a const array of strings, etc.). But none of this is at all
    hard with today's enums :

    enum E1 { A, B, C, D, last_E1 = D };

    void f(enum E1 e)
    {
    switch (e)
    {
    //used
    case A:
    case B:
    break;

    default:
    static_assert(last_E1 == D);
    // or, according to preference
    static_assert(last_E1 == 4);
    break;
    };

    }

    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;

    It is not standardised, so different people use different names, but
    it's perfectly doable.

    And if you want to automate it a little more and negate the possibility
    of forgetting to change the "count_of_E1" line, because automation of
    these things is nice, then I think you could easily put together an
    XMacro style solution.

    In a new, different language, I'd want better enumerations with more
    features, and this kind of thing could be handled better in the
    language. But for C, I can't see this being worth the effort for an extension.



    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Thu Oct 23 20:51:13 2025
    From Newsgroup: comp.lang.c

    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.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From tTh@tth@none.invalid to comp.lang.c on Thu Oct 23 23:57:11 2025
    From Newsgroup: comp.lang.c

    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 ?
    --
    ** **
    * tTh des Bourtoulots *
    * http://maison.tth.netlib.re/ *
    ** **
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From antispam@antispam@fricas.org (Waldek Hebisch) to comp.lang.c on Thu Oct 23 22:40:44 2025
    From Newsgroup: comp.lang.c

    David Brown <david.brown@hesbynett.no> wrote:
    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.

    IME it is a possible execution path unless you have very simple
    and carefully checked proof that it is not. I mean, you may
    _think_ that you proved that given case can not happen, but
    once your argument gets complex there is realistic chance of
    error in your proof. That happened to myself, but I had code
    like:

    default:
    ERROR("Impossible");

    and well, "Impossible" happened.

    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.

    That depends. I one constext that means adding some tens of bytes
    to binary, which is negligible given size to the thing of modern
    PC-s. Execution time usualy is negligible too (I do not have
    switches in inner loops, so even if extra case leads to extra
    comparison it does not matter much). There is question of
    consequences. In my case unhandled case can easily lead to producing
    wrong value, which is worst kind of error for the application.
    Signaling error essentially means "can not do this" and is nothing
    unusual.

    In embedded context this is different, as you noted extra code
    can exceed time budget. Stopping device may not be an option
    and there could be no way to print error message. OTOH control
    and date flow in embedded applications tend to be much simpler
    than in PC applications, so chance of correct proof are better.
    --
    Waldek Hebisch
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Thu Oct 23 16:17:15 2025
    From Newsgroup: comp.lang.c

    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).

    commit b474be668091301d4a214da922f0cb98d416dc6b
    Author: Keith Thompson <Keith.S.Thompson@gmail.com>
    Date: 2025-10-23 02:41:12 -0700

    [clang-tidy][NFC] Clarify switch-missing-default-case doc (#164699) (#164709)

    Falling through a defaultless switch statement has well defined
    behavior. Fixes https://github.com/llvm/llvm-project/issues/164699.

    Credit for noticing this problem goes to user "pozz" on comp.lang.c,
    Message-ID: <10da67g$3q59f$1@dont-email.me>

    diff --git clang-tools-extra/docs/clang-tidy/checks/bugprone/switch-missing-default-case.rst clang-tools-extra/docs/clang-tidy/checks/bugprone/switch-missing-default-case.rst
    index 648c2c208a4e..3ce862ff8afc 100644
    --- clang-tools-extra/docs/clang-tidy/checks/bugprone/switch-missing-default-case.rst
    +++ clang-tools-extra/docs/clang-tidy/checks/bugprone/switch-missing-default-case.rst
    @@ -9,8 +9,8 @@ on covering cases with non-enums where the compiler may not issue warnings.
    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.
    +specified cases, the switch statement will do nothing and the program will +continue execution without handling the value.

    This check helps identify switch statements that are missing a default case,
    allowing developers to ensure that all possible cases are handled properly.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Thu Oct 23 16:21:47 2025
    From Newsgroup: comp.lang.c

    scott@slp53.sl.home (Scott Lurndal) writes:
    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.

    sizeof works just fine on typed enums. It just doesn't tell you how
    many members it has.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From antispam@antispam@fricas.org (Waldek Hebisch) to comp.lang.c on Thu Oct 23 23:23:43 2025
    From Newsgroup: comp.lang.c

    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:
    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.

    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.

    Use an enum, and the compiler will warn you ...

    $ 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 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).

    Let me mention variation of technique proposed by David Brown.
    Namely, for me reasonably typical usage pattern is when I
    have a few ranges of enum, each with somewhat different usage.
    For example, in a compiler I need codes for terminals and
    nonterminals. But there are places when I need to handle both,
    while in other places only terminals (or maybe nonterminals)
    make sense. In such case one can define things like
    'last_terminal' and 'last_sym_code' (and possibly 'first_nonterminal')
    and test in appropriate places that codes are in expected
    range. In Pascal one can define new types as subranges of
    bigger enum type, IIUC in C this is not possible.

    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.

    In similar context I would probably use table of pairs + GNU
    tyle macrology. That is have "codes.h" with content like

    M(A)
    M(B)
    ...
    M(F)

    and in source file have

    enum codes {
    #define M(x) x,
    #include "codes.h"
    #undef M
    };

    struct {char * name; enum codes code;} name_tab[] = {
    #define M(x) {#x, x},
    #include "codes.h"
    #undef M
    };

    Or possibly have an external program to generate needed C code.
    The point is that when usage is consistent with a simple pattern
    it is easier to make sure that code is correct "by construction"
    than to depend on language features to catch errors in manually
    maintained code.
    --
    Waldek Hebisch
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Thu Oct 23 16:31:58 2025
    From Newsgroup: comp.lang.c

    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.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Thu Oct 23 19:13:31 2025
    From Newsgroup: comp.lang.c

    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    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).

    And now it's been updated:

    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 switch
    statement will do nothing and the program will continue execution
    without handling the value.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Fri Oct 24 08:59:16 2025
    From Newsgroup: comp.lang.c

    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 :

    enum E : unsigned short { a, b, c, d, e, f };

    int x = sizeof(enum E);
    int y = sizeof(a);

    "x" and "y" are both 2 on x86. It would be a terrible mistake for it to
    be anything else here.

    A better possibility is the _Lengthof operator in the future C2y
    standard. It is for arrays, giving the number of elements (rather than
    the size). I believe it could be practical to have it apply to
    enumeration types and give the count of the elements, though it is not
    clear what it should do if the enumeration constants are explicitly initialised in different ways.

    (For those that don't obsessively read the latest standards, specifying
    the underlying type of an enumeration is a C23 addition. But sizeof
    works without that too.)


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Fri Oct 24 09:22:28 2025
    From Newsgroup: comp.lang.c

    On 23/10/2025 23:57, tTh wrote:
    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 ?


    The enumeration definition will either be in a C file, in which case the
    const should be "static" because it is local to the file, or it will be
    in a header, in which case making the small const "static" can be vastly
    more efficient in many ways. I have been assuming that this definition
    is at file scope, as most type declarations are, rather than local to a function. If it is local to a function, then decent compilers will give
    the same resulting code with or without the static.


    But I see I have made a mistake here - I have mixed up with C++. You
    can't use static_assert in C with a "const" like this for compile-time checking. So in C, prior to C23, you actually need to write (with the
    +1 correction to my original example) :

    enum E1 { A, B, C, D };
    enum { count_of_E1 = D + 1 };

    if you want to be able to write :

    _Static_assert(count_of_E1 == 4, "Checking enumeration");

    With C23, you could have "constexpr int count_of_E1 = D;" and use it in
    a static assertion. (constexpr declarations always have internal
    linkage, so no "static" is needed.)


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Fri Oct 24 09:53:26 2025
    From Newsgroup: comp.lang.c

    On 24/10/2025 01:23, Waldek Hebisch wrote:
    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).

    Since you mention "textual searches", one technique that can sometimes
    be helpful when you change a definition and want to be sure that you
    have changed or checked all uses is to change the names in the
    declaration. While we have been using "enum E { A, B, C };" in these
    posts, real code typically has longer names - perhaps "enum run_states",
    or whatever, with either a prefix or a postfix to the enumeration type
    and the enumerators. If you change that in your declaration, such as by adding an X somewhere, you are guaranteed compile-time errors until you
    have gone through all the uses in the code and added the X as you
    checked the use-cases and changed or added as appropriate. Once
    everything is complete, a big search-and-replace can remove the X's.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Fri Oct 24 10:09:38 2025
    From Newsgroup: comp.lang.c

    On 24/10/2025 01:31, Keith Thompson wrote:
    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.


    That's a good entry for the "understatement of the week" competition!

    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.


    I like a macro that can then expand to different things according to
    what I want to do such as "__builtin_unreachable()" for gcc,
    "unreachable()" for C23, blank for other tools, for use with well
    checked and well tested code. Alternatively it can expand to a check of
    some sort, a log, a debug message, an LED indicator, or whatever during testing, debugging, or fault-finding.

    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.


    Indeed. Very often you see error handling that is done without due
    thought about how the errors could occur, and what you should do about
    them. I see code that obsessively checks "malloc" for zero returns,
    because someone heard that you should always check the result of
    "malloc". That's fair enough if you are in a situation where malloc
    could fail, and when you can do something about it that is better than ignoring it - but usually malloc cannot fail in any remotely realistic use-case, and usually there's nothing you can do anyway that is better
    than continuing until dereferencing a null pointer crashes your code.

    Basically, there's no point in asking a question until you have a plan
    of what to do with the answer.

    And yes, handling a condition that can actually happen (say, in the
    presence of bad input) is quite different.


    Very much so, yes. "Edge" code - whether it is getting data from
    outside, or is an API called by external code - has to sanitize its
    inputs. Internal code should not be second-guessing other parts of the
    same code - if you can't trust the different bits of the same project to
    get things right, you have big problems that can't be solved by adding
    default cases. (Where the boundaries between "internal" and "edge" code
    go will obviously vary.)

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Fri Oct 24 15:51:30 2025
    From Newsgroup: comp.lang.c

    David Brown <david.brown@hesbynett.no> writes:
    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 :

    yes, I'm aware. I should have added 'to determine the number of enums'.

    --- Synchronet 3.21a-Linux NewsLink 1.2