Thread: Quick check for potential problems

  1. #16
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    38,172
    > # elif ('\0'|(1 << (CHAR_BIT-1))) < 0
    Quote Originally Posted by c99
    preprocessor arithmetic done in intmax_t/uintmax_t
    The whole thing is just going to get promoted all the way up some large type.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  2. #17
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,048
    Quote Originally Posted by laserlight View Post
    Would you happen to have an example of such code and the corresponding error?
    Well gave that (char)-1 a try to prove a point but that turned out to be my 1st time not getting a compiler error with that sort of thing, on the other hand it gave erronious results.
    Code:
    #ifdef CHAR_SIGNED
    	alu_puts("CHAR_SIGNED");
    #endif
    #ifdef CHAR_UNSIGNED
    	alu_puts("CHAR_UNSIGNED");
    #endif
    #if (char)-1 < 0
    	alu_printf("(char)-1 < 0 = %i", 1);
    #else
    	alu_printf("(char)-1 < 0 = %i", 0);
    #endif
    Gave this result:
    Code:
    test.c:264: main() 'CHAR_UNSIGNED'
    test.c:267: main() (char)-1 < 0 = 1
    Although I will note that CHAR_MAX & CHAR_MIN appear to be lining up with the second result which is confusing

  3. #18
    misoturbutc Hodor's Avatar
    Join Date
    Nov 2013
    Posts
    1,784
    Quote Originally Posted by awsdert View Post
    Although I will note that CHAR_MAX & CHAR_MIN appear to be lining up with the second result which is confusing
    I see nothing unexpected...
    Code:
    #include <stdio.h>
    
    #define CHAR_SIGNED ((char)-1 < 0)
    #define CHAR_UNSIGNED (!CHAR_SIGNED)
    
    int main(void)
    {
    #if defined CHAR_SIGNED && defined CHAR_UNSIGNED
        puts("Everything is ok so far");
        printf("CHAR_SIGNED == %d, CHAR_UNSIGNED == %d\n", CHAR_SIGNED, CHAR_UNSIGNED);
    #endif
    
    #if CHAR_SIGNED
        puts("char is signed");
    #endif
    
    #if CHAR_UNSIGNED
        puts("char is unsigned");
    #endif
    
    #ifdef CHAR_SIGNED
        puts("CHAR_SIGNED");
    #endif
    #ifdef CHAR_UNSIGNED
        puts("CHAR_UNSIGNED");
    #endif
    
    #if (char)-1 < 0
        printf("(char)-1 < 0 = %i", 1);
    #else
        printf("(char)-1 < 0 = %i", 0);
    #endif
    }
    Code:
    Everything is ok so far
    CHAR_SIGNED == 1, CHAR_UNSIGNED == 0
    char is signed
    CHAR_SIGNED
    CHAR_UNSIGNED
    (char)-1 < 0 = 1

  4. #19
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    738
    Quote Originally Posted by awsdert View Post
    Well gave that (char)-1 a try to prove a point but that turned out to be my 1st time not getting a compiler error with that sort of thing, on the other hand it gave erronious results.
    Code:
    #ifdef CHAR_SIGNED
        alu_puts("CHAR_SIGNED");
    #endif
    #ifdef CHAR_UNSIGNED
        alu_puts("CHAR_UNSIGNED");
    #endif
    #if (char)-1 < 0
        alu_printf("(char)-1 < 0 = %i", 1);
    #else
        alu_printf("(char)-1 < 0 = %i", 0);
    #endif
    Gave this result:
    Code:
    test.c:264: main() 'CHAR_UNSIGNED'
    test.c:267: main() (char)-1 < 0 = 1
    Although I will note that CHAR_MAX & CHAR_MIN appear to be lining up with the second result which is confusing
    You can do the same thing in the compiler, which is (IMO) cleaner than using the preprocessor:

    Code:
    if (CHAR_SIGNED) {
        alu_puts("CHAR_SIGNED");
    } else if (CHAR_UNSIGNED) {
        alu_puts("CHAR_UNSIGNED");
    } else {
        alu_puts("not CHAR_SIGNED and not CHAR_UNSIGNED??");
    }
    if ((char)-1 < 0) {
        alu_printf("(char)-1 < 0 = %i", 1);
    } else {
        alu_printf("(char)-1 < 0 = %i", 0);
    }
    // or: alu_printf("(char)-1 < 0 = %i", (char)-1 < 0);
    But I'll ask my question again: when would you ever care whether char is signed or unsigned in an actual program, and not just in an academic exercise in writing macros?

  5. #20
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,048
    Quote Originally Posted by christop View Post
    But I'll ask my question again: when would you ever care whether char is signed or unsigned in an actual program, and not just in an academic exercise in writing macros?
    I needed it for the fallbacks of CHAR_MAX & CHAR_MIN, also I expect the main place to care would be functions like iconv() (if I remembered that rightly that is) where a programmed check should be a last resort since that check can be optimised out by using such macros, it needs to do it that way because it is a heavy use function
    which behaves differently under the hood depending on whether char is signed or unsigned and how many bits it has access to.

  6. #21
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,048
    Quote Originally Posted by Hodor View Post
    I see nothing unexpected...
    Code:
    #include <stdio.h>
    
    #define CHAR_SIGNED ((char)-1 < 0)
    #define CHAR_UNSIGNED (!CHAR_SIGNED)
    
    int main(void)
    {
    #if defined CHAR_SIGNED && defined CHAR_UNSIGNED
        puts("Everything is ok so far");
        printf("CHAR_SIGNED == %d, CHAR_UNSIGNED == %d\n", CHAR_SIGNED, CHAR_UNSIGNED);
    #endif
    
    #if CHAR_SIGNED
        puts("char is signed");
    #endif
    
    #if CHAR_UNSIGNED
        puts("char is unsigned");
    #endif
    
    #ifdef CHAR_SIGNED
        puts("CHAR_SIGNED");
    #endif
    #ifdef CHAR_UNSIGNED
        puts("CHAR_UNSIGNED");
    #endif
    
    #if (char)-1 < 0
        printf("(char)-1 < 0 = %i", 1);
    #else
        printf("(char)-1 < 0 = %i", 0);
    #endif
    }
    Code:
    Everything is ok so far
    CHAR_SIGNED == 1, CHAR_UNSIGNED == 0
    char is signed
    CHAR_SIGNED
    CHAR_UNSIGNED
    (char)-1 < 0 = 1
    You should note that based on the code I gave my output should not have reported only CHAR_UNSIGNED (or CHAR_UNSIGNED at all) before the (char)-1 < 0 = # part but should have reported CHAR_SIGNED before both, I afterwords changed how I defined CHAR_SIGNED & CHAR_UNSIGNED & how I checked them when deciding what to report and the conflicting results disapeared afer the change

  7. #22
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    27,929
    Quote Originally Posted by awsdert
    I needed it for the fallbacks of CHAR_MAX & CHAR_MIN
    Eh, you expect that SCHAR_MAX, UCHAR_MAX, and SCHAR_MIN to be available in <limits.h>, but not necessarily CHAR_MIN and CHAR_MAX?! On which implementation have you observed this to be the case? I would expect all of them to be available, or if any of them are not available, it's a non-standard conforming implementation such that it is reasonable to suspect that none of them will be available, and perhaps <limits.h> might not even exist. If you read the C standard, it provides a note saying:
    CHAR_MIN, defined in <limits.h>, will have one of the values 0 or SCHAR_MIN, and this can be used to distinguish the two options.
    That is, it is expected that you'll determine whether char is signed or unsigned by checking if CHAR_MIN is 0 or SCHAR_MIN, not the other way round. Such a check can of course be done at compile-time, so there is no runtime penalty. You should be aware that christop's code in post #19, although it does not use the preprocessor is equally efficient since the values are constants known at compile-time, and hence will be optimised away (likely even at low optimisation levels). It is like writing:
    Code:
    if (1 == 1)
    {
        puts("Hello world!");
    }
    else
    {
        puts("Goodbye world!");
    }
    The if statement will be optimised away such that the result will be as if one wrote:
    Code:
    puts("Hello world!");
    Last edited by laserlight; 08-12-2020 at 04:53 PM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  8. #23
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    738
    Quote Originally Posted by awsdert View Post
    I needed it for the fallbacks of CHAR_MAX & CHAR_MIN, also I expect the main place to care would be functions like iconv() (if I remembered that rightly that is) where a programmed check should be a last resort since that check can be optimised out by using such macros, it needs to do it that way because it is a heavy use function
    which behaves differently under the hood depending on whether char is signed or unsigned and how many bits it has access to.
    Try compiling this code:

    Code:
    if ((char)-1 < 0) {
        puts("char is signed");
    } else {
        puts("char is unsigned");
    }
    and I can almost guarantee that you will see only one of the printed strings in the executable. Like I said awhile back, any halfway-decent compiler will optimize that if statement and completely remove the non-taken branch. It'll be equivalent to this code on an implementation where char is signed:

    Code:
    puts("char is signed");
    and to this code on an implementation where char is unsigned:

    Code:
    puts("char is unsigned");
    So the code will be just as fast whether you check for signed/unsigned char in the preprocessor or in the compiler proper.

    Same goes for checking the bit size of char. You could have a series of "if ((unsigned char)(1 << 16)) .. if ((unsigned char)(1 << 8)) ... else" statements in your code and it'll be optimized. Here's an example:

    Code:
    #include <stdio.h>
    
    int main()
    {
            if ((unsigned char)(1 << 16)) {
                    puts(">16");
            } else if ((unsigned char)(1 << 15)) {
                    puts("16");
            } else if ((unsigned char)(1 << 14)) {
                    puts("15");
            } else if ((unsigned char)(1 << 13)) {
                    puts("14");
            } else if ((unsigned char)(1 << 12)) {
                    puts("13");
            } else if ((unsigned char)(1 << 11)) {
                    puts("12");
            } else if ((unsigned char)(1 << 10)) {
                    puts("11");
            } else if ((unsigned char)(1 << 9)) {
                    puts("10");
            } else if ((unsigned char)(1 << 8)) {
                    puts("9");
            } else if ((unsigned char)(1 << 7)) {
                    puts("8");
            } else {
                    puts("<8");
            }
            return 0;
    }
    Compiler output (gcc -S test-char.c):

    Code:
            .file   "test-char.c"
            .text
            .section        .rodata
    .LC0:
            .string "8"
            .text
            .globl  main
            .type   main, @function
    main:
    .LFB0:
            .cfi_startproc
            pushq   %rbp
            .cfi_def_cfa_offset 16
            .cfi_offset 6, -16
            movq    %rsp, %rbp
            .cfi_def_cfa_register 6
            leaq    .LC0(%rip), %rdi
            call    puts@PLT
            movl    $0, %eax
            popq    %rbp
            .cfi_def_cfa 7, 8
            ret
            .cfi_endproc
    .LFE0:
            .size   main, .-main
            .ident  "GCC: (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0"
            .section        .note.GNU-stack,"",@progbits
    Only one string, "8", made it to the executable. And that's with GCC's default optimization level (same results as -O0).

    Besides that, I think it's a fool's errand to try to rewrite limits.h since any compiler that wants to call itself "C" must provide it (and I don't see any portable way to write macros contained in limits.h, which is why we are given limits.h in the first place).

  9. #24
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    27,929
    Quote Originally Posted by christop
    Besides that, I think it's a fool's errand to try to rewrite limits.h since any compiler that wants to call itself "C" must provide it
    That's a very good point. Here's what C11 has to say:
    A conforming freestanding implementation shall accept any strictly conforming program that does not use complex types and in which the use of the features specified in the library clause (clause 7) is confined to the contents of the standard headers <float.h>, <iso646.h>, <limits.h>, <stdarg.h>, <stdbool.h>, <stddef.h>, and <stdint.h>.
    Notice that both <limits.h> and <stdint.h> are in the list, i.e., even if you are programming for embedded systems etc such that you worry that not all of the standard library might be available, there's absolutely no worry about <limits.h> being unavailable. You might be wary about <stdint.h> since it was introduced in C99 (but that was two decades ago!), and likewise for the constants for long long and unsigned long long, but <limits.h> and the constants for char and the like have been standard since C has been standardised in the late 1980s/1990. If you cannot depend on this, then you can only depend on the documentation for these particular standard non-conforming compilers, so you should be making reference to these compilers otherwise you have nothing to stand on. After all, how do you know that the char type even exists? In a deliberately standard non-conforming implementation, it doesn't have to exist.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  10. #25
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,048
    Quote Originally Posted by laserlight View Post
    Eh, you expect that SCHAR_MAX, UCHAR_MAX, and SCHAR_MIN to be available in <limits.h>, but not necessarily CHAR_MIN and CHAR_MAX?! On which implementation have you observed this to be the case? I would expect all of them to be available, or if any of them are not available, it's a non-standard conforming implementation
    Yeah that's the worst case scenario, I prefer to programme around such scenarios (especially in the preprocessor) to reduce number of errors that occur, it's why I tend to throw the error found back 1st then design catches for deal-able ones after seeing them pop up in the final catch before the program aborts.
    Quote Originally Posted by laserlight View Post
    Such a check can of course be done at compile-time, so there is no runtime penalty.
    Which is precisely why I'm doing as much as I can in the preprocessor to create fallbacks for macros, things that can be optimised out, better to assume a scenario exists where they don't exist then to assume not so.

  11. #26
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,048
    Quote Originally Posted by christop View Post
    Besides that, I think it's a fool's errand to try to rewrite limits.h since any compiler that wants to call itself "C" must provide it (and I don't see any portable way to write macros contained in limits.h, which is why we are given limits.h in the first place).
    I'm not denying the abilities of modern compilers however I do like to assume the worst case scenario I can think of and tend to programme around that, preprocessor is the best place to guarantee something will be compiled out so wherever possible I do precisely that

  12. #27
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,048
    Quote Originally Posted by laserlight View Post
    If you cannot depend on this, then you can only depend on the documentation for these particular standard non-conforming compilers, so you should be making reference to these compilers otherwise you have nothing to stand on. After all, how do you know that the char type even exists? In a deliberately standard non-conforming implementation, it doesn't have to exist.
    Yeah I remember reading somewhere there was an implementation that only had int, I will be defining char as int in that scenario once I found out how to catch it but for now I'm going with any portable catch I can find, they'll work as expected once I do catch that scenario for which I only need to define char and CHAR_BIT

  13. #28
    misoturbutc Hodor's Avatar
    Join Date
    Nov 2013
    Posts
    1,784
    Quote Originally Posted by awsdert View Post
    Yeah I remember reading somewhere there was an implementation that only had int, I will be defining char as int in that scenario once I found out how to catch it but for now I'm going with any portable catch I can find, they'll work as expected once I do catch that scenario for which I only need to define char and CHAR_BIT
    If there is/was a C compiler that didn't support char (i.e. "that only had int") truly exists I wouldn't bother supporting it. Even Small-C (the original 1980 version) supported char.

    GitHub - trcwm/smallc_v1: Source code for the original Small C compiler published in Dr. Dobbs journal.

  14. #29
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    27,929
    Quote Originally Posted by awsdert
    Which is precisely why I'm doing as much as I can in the preprocessor to create fallbacks for macros, things that can be optimised out, better to assume a scenario exists where they don't exist then to assume not so.
    As christop pointed out, the code tends to be cleaner when using ordinary C syntax than when using the preprocessor syntax. You're doing premature optimisation such that it is almost certainly not optimisation at all. Instead of assuming, follow good practices and test for when they don't apply.

    Quote Originally Posted by awsdert
    I'm not denying the abilities of modern compilers
    "Modern" does depend on context (my undergrad class on "modern Japanese history" started with the mid-1500s), and in this case we're looking at a particular kind of dead code elimination that likely has been common for a quarter of a century or more. (I cannot cite a source, but it's an educated guess from a book written circa 1995 that explained why for ( ;; ) was considered canonical instead of while (1), but which also explained that the underlying issue, i.e., compilers generating code that evaluated the constant rather than generating the deliberate infinite loop, were old and no longer in use... in 1995.)
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Quick program check
    By Nik635 in forum C Programming
    Replies: 1
    Last Post: 09-17-2015, 08:20 AM
  2. Quick check
    By SofaWolf in forum C Programming
    Replies: 6
    Last Post: 06-26-2012, 12:53 PM
  3. potential problems?
    By deepcode in forum C Programming
    Replies: 4
    Last Post: 08-11-2010, 02:04 PM
  4. Need a quick check
    By Aliaks in forum C++ Programming
    Replies: 7
    Last Post: 06-05-2009, 04:57 AM
  5. Quick check on reading in + using strings
    By advancedk in forum C Programming
    Replies: 2
    Last Post: 12-08-2008, 10:12 PM

Tags for this Thread