Thread: Reversing a string declared as char[] works, but as char* it segfaults. Why?

  1. #16
    Banned
    Join Date
    Jul 2022
    Posts
    112
    The snippet you have provided above invokes undefined behavior. According to C standard

    C11: 6.5 Expressions:
    If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings.84).

    In the expression,
    Code:
    *p0++ = mult(*p0, psign[i1])
    the modification to p0 on left side of the assignment operator is not sequenced before or after the use of p0 on right hand side of the expression. Therefore, the snippet

    Code:
    *p0++ = mult(*p0, psign[i1]);
    is not equivalent to

    Code:
    *p0 = mult(*p0, psign[i1]);
    p0++;                       // Side effect to p0 is guaranteed after the use  
                                // of p0 in mult function
    answered Aug 14, 2015 at 19:48
    haccks's
    c - unsequenced modification and access to pointer - Stack Overflow
    Last edited by kodax; 10-11-2022 at 09:05 PM.

  2. #17
    Banned
    Join Date
    Jul 2022
    Posts
    112
    @flp
    It never occurred to me that you were sensitive and frail to this point, to resort to ad hominem.

    An ad hominem argument is one that relies on personal attacks rather than reason or substance. This occurs when, instead of addressing someone's argument or position, you irrelevantly attack the person or some aspect of the person who is making the argument.
    Last edited by kodax; 10-11-2022 at 09:53 PM.

  3. #18
    Registered User
    Join Date
    Dec 2017
    Posts
    1,466
    You're being an idiot.
    Nothing could be more C than: *a++ = *b++
    There is no sequence point problem since different objects are being read (what is pointed to) and written (the pointers themselves).
    The whole problem with the world is that fools and fanatics are always so certain of themselves, but wiser people so full of doubts. - Bertrand Russell

  4. #19
    Banned
    Join Date
    Jul 2022
    Posts
    112
    Code:
    // return ++a;
    // b = --x == --y;
    // while (*s++);
    This is indeed ugly, but these practices will result in serious bugs..

  5. #20
    Registered User
    Join Date
    Sep 2020
    Posts
    395
    Quote Originally Posted by kodax View Post
    Code:
    // return ++a;
    // b = --x == --y;
    // while (*s++);
    This is indeed ugly, but these practices will result in serious bugs..
    Well lets' put your unconditional assertion that "these practices will result in serious bugs" to the test.

    What is the "serious bug" in this skip_str() function?

    Code:
    #include <stdio.h>
    
    
    const char data[] = "First string\0Second string\0Third and last string\0\0";
    
    
    const char *skip_str(const char *str) {
       while(*str)
         str++;
       return ++str;
    }
    
    
    int main(void) {
       const char *str = data;
       while(*str) {
          printf("String is '%s'\n",str);
          str = skip_str(str);
       }
       return 0;
    }
    The result of this is a binary outcome - this code either has a serious bug, or it doesn't. No room for "well, actually..." or "But if look at this use-case...".

    Does skip_str() have a serious bug or not?

    Here is a second example. What is the serious bug here, using another of the practices that you assert will cause bugs:

    Code:
    const char *skip_str(const char *str) {
       while(*str++) ;
       return str;
    }
    I'm no fan of that empty while() loop, but is properly defined by the C standards, and generates valid (bug free?) code.

    You might as well be saying that using the "+", "-", "*" and "/" operators will cause bugs.

    PS. Forgot to say that I am in the K.i.S.S camp - success is when my code has the complexity that makes it look like it is written by a 4 year old, but works and works well.
    Last edited by hamster_nz; 10-12-2022 at 12:02 AM. Reason: Added the PS

  6. #21
    Banned
    Join Date
    Jul 2022
    Posts
    112
    Using explicit pointer arithmetic in critical software is generally frowned upon. MISRA 2004 rules 17.1 to 17.3 prohibit some particular cases of explicit pointer arithmetic that do not give rise to well-defined results. Rule 17.4 then states that “Array indexing shall be the only allowed form of pointer arithmetic”. All 4 rules are “required” rather than “advisory”, so 17.4 appears to make the preceding 3 rules redundant. The implication seems to be that developers who break 17.4 should at least honour 17.1 to 17.3.
    How (un)safe is pointer arithmetic? | David Crocker's Verification Blog

  7. #22
    Registered User
    Join Date
    Sep 2020
    Posts
    395
    I could just as easy say that your code examples are bad because your commenting style does not comply with MIRSA 2004 Rule 2.2 that requires you to use the older "/* */" style comments. Or do only you get to pick and chose the rules?

    That aside, I'm pretty sure that most will agree C usage in critical systems is significantly different from common and accepted C usage. It is definitely different from what you would expect an inexperienced novice to use to reverse characters in a string.

    But back to being off topic - where are these serious bugs that you asserted will be in those bits of code?

  8. #23
    Banned
    Join Date
    Jul 2022
    Posts
    112
    You miss the point, even if you don't make errors, there's the likelihood that someone else will.

  9. #24
    Banned
    Join Date
    Jul 2022
    Posts
    112
    Potential pointer corruption.

    If code assumes that pointers and integers are the same size (in an arithmetic context), there will be problems.
    Pointer arithmetic is often a source of problems when migrating code. The ISO C standard dictates that incrementing a pointer adds the size of the data type to which it points to the pointer value. For example, if the variable p is a pointer to long, the operation (p+1) increments the value of p by 4 bytes (in 32-bit mode) or by 8 bytes (in 64-bit mode). Therefore, casts between long* and int* are problematic because of the size differences between pointer objects (32 bits versus 64 bits).
    IBM Documentation

  10. #25
    Registered User
    Join Date
    Feb 2019
    Posts
    1,047
    --->

  11. #26
    Registered User
    Join Date
    Feb 2019
    Posts
    1,047
    What Mr. "I am always right" hasn't realized yet is that his code is buggy, despite all the "pointer leads to bugs" rhetoric. His code:
    Code:
    void a_reverse(char *buffer, const char *array, int length) { 
        int a = length;
        int b = 0;
    
        while (0 != a) {
            a = a - 1;
            buffer[b] = array[a];
            b = 1 + b;
         }
     
        buffer[b] = '\0';
    
        // WHY?!
        return;
    }
    Of course the buffer and array pointers (oh, the horror!) must point to valid memory, but what will happen if length is negative? What will happen if both pointers points to the same memory region? All his effort to "be right and everybody knows it" (obviously a fallacy) vanished right there.

    Just because he doesn't feel confortable using pointers and tries to signal virtue... Comparing to his, this is way less buggy AND commented, in case anyone has doubts on what the routine is doing:
    Code:
    // Using restrict to tell the compiler the pointers cannot be the same.
    // Yep... assumes both pointers are valid (notice: no 'length'!).
    char *strrev( char * restrict out, const char * restrict in )
    {
      // points out to the end of the buffer and puts a '\0' there.
      out += strlen( in );
      *out-- = '\0';
     
      //  while '\0' isn't found yet, copy chars in reverse order,
      // incrementing the input pointer, and decrementing the output pointer.
      while ( *in ) *out-- = *in++;
     
      // puts out back at the beginning and return it.
      return ++out;
    }
    The same assumptions apply: Both arguments must be valid pointers AND not pointing to overlapped regions (as his routine also implies -- but worse!) AND the output buffer must be the same size as the input buffer.

    So... yep... keep trying. One day you'll enjoy using pointers...

  12. #27
    Registered User
    Join Date
    Feb 2019
    Posts
    1,047
    Quote Originally Posted by kodax View Post
    You miss the point, even if you don't make errors, there's the likelihood that someone else will.
    You did, in your code, using 'arrays' instead of pointer arithmetic.

  13. #28
    Registered User
    Join Date
    Feb 2019
    Posts
    1,047
    About #24, the same can be said if you use long instead of int (not pointers). On MS-ABI for x86-64 long and int have the same size, but on SysV ABI x86-64 long is the same as long long. This is problematic on Windows:
    Code:
    long x = -1L;
    Since is 0xFFFFFFFF, not 0xFFFFFFFFFFFFFFFFL.

    In the old MS-DOS int is 16 bits long and long is 32 (try it with TurboC, Borland C++ 3.1 and Microsoft C 6, as examples of good compilers for DOS).

    ISO 9899 is clear on this: There's no garantees about primitive types sizes (only minimum recommendations).

    To confuse pointer types is something a beginner suffers from. Of course, for example, char * and double * points to different objects and their addresses will be dealt differently. This isn't "insecure". This is a feature of the language.

    Another thing to notice: [] operator is a shortcut to pointer arithmetic:
    Code:
    // this:
    a[i] = 0;
    
    // is exactly the same as this:
    *(a + i) = 0;

  14. #29
    Banned
    Join Date
    Jul 2022
    Posts
    112
    What is going on with the name-calling, are you on a power trip ?

    Most of my projects use pointer-arithmetic.
    It is wrong to judge people without knowing them..
    Last edited by kodax; 10-12-2022 at 09:28 AM.

  15. #30
    Banned
    Join Date
    Jul 2022
    Posts
    112
    Use return to exit early of a void function.

    Code:
    void abcd() {
        if (done) {
            return; 
        }
        // do something
    }
    Return statement is optional in that context, but it is necessary in x86-64 Assembly.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 11
    Last Post: 06-16-2011, 11:59 AM
  2. Replies: 2
    Last Post: 09-12-2010, 09:15 AM
  3. char causes segfaults more than strings?
    By TheBigOnion in forum C++ Programming
    Replies: 3
    Last Post: 03-30-2010, 10:07 AM
  4. Reversing (char)...
    By yaya in forum C++ Programming
    Replies: 7
    Last Post: 05-27-2007, 04:15 AM
  5. I declared it as int but it shows up as an char...
    By XR3D403 in forum C Programming
    Replies: 5
    Last Post: 02-10-2003, 09:12 AM

Tags for this Thread