• Yanroy (unregistered) in reply to Yanroy

    I forgot to add a couple important details: adding the CR/LF and removing it serves no discernable purpose and is indeed a WTF, and just because you won't get an exploit from a buffer overflow doesn't mean you shouldn't protect against it. Most embedded programs are contrived in such a way that they can avoid the potentially costly bounds checking by always knowing it won't happen.

  • (cs)

    Finally a WTF in a language I fully understand.

    1. If this is an embedded system, then most likely the lengths of header and value are known not to exceed a given length. If we obtain buffer contents from an internal trusted source, we just need to make sure our buffer size N is big enough to contain the longest string possible.

    2. Ignoring return value from sprintf is ignorant but common among C programmers. Usually you don't care about it too much.

    3. Dropping a NULL|0|'\0' into the middle of a string is a common way to truncate junk off the end of the string. We do it all the time. Just don't do it in anything security related like passwords, keys, etc....

    4. Putting stuff into your own string constant that you then truncate away is a WTF. The rest is just apathetic programming.

  • (cs) in reply to darin
    darin:
    Surely you know about memset(), the standardized function that the universe intended to be used here?

    In defense of using ZeroMemory if you don't have to worry about portability.

    (BTW, neither g++ -W -Wall or Visual C++ at level 4 warnings warn about this.)

  • Jeltz (unregistered) in reply to Lumpy
    Lumpy:
    I know! It's not i18n compliant; the code should be using wchar_t and wsprintf.

    Not if the string is in UTF-8 (like the guy writing this code would care).

  • Ishai Sagi (unregistered) in reply to Aaron
    Aaron:
    bonus points? that depends.... is it because we're assuming that "N" is indeed an integer value? :D

    (Given some of the horrid programming we see on this site, it wouldn't at all surprise me to see someone trying to use a char value as an array size.)

    FRIST!

    What? you mean I can't have a buffer of 3.5 characters? how awful!

  • JS (unregistered)
    #include <string.h>

    strcat( buffer, header ); strcat( buffer, ":" ); strcat( buffer, value );

    Even if you changed the first strcat to strcpy, you wouldn't do that, since it will have to do the equivalent of strlen for each of the 2 subsequent strcats. In this case sprintf is better.
  • (cs)

    Solution - implement own stpcpy :) stpcpy - copy a string returning a pointer to its end

    char *stpcpy(char *dest, const char *src);

    "CONFORMING TO: This function is not part of the C or POSIX.1 standards, and is not customary on Unix systems, but is not a GNU invention either. Perhaps it comes from MS-DOS."

    stpcpy(stpcpy(stpcpy(buffer, header), ":"), value);

    YAY!

  • (cs) in reply to viraptor
    viraptor:
    Solution - implement own stpcpy :) stpcpy - copy a string returning a pointer to its end

    char *stpcpy(char *dest, const char *src);

    "CONFORMING TO: This function is not part of the C or POSIX.1 standards, and is not customary on Unix systems, but is not a GNU invention either. Perhaps it comes from MS-DOS."

    stpcpy(stpcpy(stpcpy(buffer, header), ":"), value);

    YAY!

    You could also work with counted strings too. Not sure what the tradeoffs would be of the two approaches... would probably depend on how your strings were created and what modules they were passed to.

    (BTW, I like the MS-DOS comment.)

  • Steve Wahl (unregistered) in reply to Jesse
    Jesse:
    A Guy:
    Really this should be:
    snprintf(buffer, N-1, "%s: %s",header,value);

    bounds checking is good

    Don't forget to add this after that line:

    buffer[N-1] = '\0';

    snprintf won't null terminate a string if it runs out of buffer space.

    YES! Somebody else has noted the WTF about snprintf!!!

    Imagine... A function that was created to get rid of a long standing WTF (no bounds checking) introduces another WTF (not ensuring the string gets terminated).

  • Steve Wahl (unregistered) in reply to Anonymous
    Anonymous:
    Jesse:
    snprintf won't null terminate a string if it runs out of buffer space.
    Yes it will!

    From c99 7.19.6.5, section 2:

    Otherwise, output characters beyond the n-1st are discarded rather than being written to the array, and a null character is written at the end of the characters actually written into the array.

    I wish people here would learn WTF they're talking about before posting comments.

    Ahhh... so the WTF is the GNU documentation for snprintf! I won't quote it here, since I'd have to quote the whole thing to show it's not there, but the GNU/Linux man page for snprintf is quite ambiguous as to whether the string gets terminated or not.

    Sorry 'bout my confusion in earlier post.

  • RockinJack (unregistered) in reply to Will
    Will:
    snoofle:
    WTF would we do to handle a language that someone designed to print diagonally on the page?

    Bomb the country where the language originated into submission.

    Are you american?

  • Jon (unregistered)

    There is nothing wrong with this code, its fine. I'm presuming sprinf is a transcript typo, since the linker wouldn't have let it pass. Yes the call to strlen is redundant because of sprintf returning the length, but its really No Big Deal unless this code has to run at absolute lightning speed.

    Get over it.

    Jon

  • Bombadil (unregistered)

    Some of the comments here suggest that their submitters are good candidates to become the next CodeSOD makers.

    Watch this space...

    (Captcha - digdug, means "a tickle" in Hebrew)

  • Jonah (unregistered)

    Well, I've visited their website, where I learned that "Your Require. Our Passion.", but I'm still not really sure who or what 'sprinf' is.

  • Kuba (unregistered) in reply to itsjustme
    itsjustme:
    Kuba:
    void hb_strcat(char* dest, char *src1, char *src2)
    {
      register char* d = dest;
      register char* s = src1;
      while (*d++ = *s++);
      *d++ = ';';
      s = src2;
      while (*d++ = *s++);
    }
    

    Won't the first

    while (*d++ = *s++);
    also copy the '\0' terminator from src1, so that for all string manipulation purposes, dest will actually contain only src1?

    Yeah, my F indeed :).

    Here're two variations on the correct version:

    void hb_strcat(char* dest, char *src1, char *src2)
    {
      register char* d = dest;
      register char* s = src1;
      #if 1
      while (*d++ = *s++);
      --d;
      #else
      while (*s) *d++ = *s++;
      #endif
      *d++ = ';';
      s = src2;
      while (*d++ = *s++);
    }
    
  • (cs) in reply to Jon
    Jon:
    There is nothing wrong with this code, its fine. I'm presuming sprinf is a transcript typo, since the linker wouldn't have let it pass. Yes the call to strlen is redundant because of sprintf returning the length, but its really No Big Deal unless this code has to run at absolute lightning speed.

    Get over it.

    Jon

    Actually there is something wrong with this code. Or do you routinely add stuff, and the remove it just because you can?

    Just because code works, doesn't been there is nothing wrong with it. My predecessor would use the default MSVC templates to build a file, and then add his code to it. I kept asking him to clean the code up and remove extraneous calls (for example the call to ShowWindow(SW_HIDE); followed by an UpdateWindow(); for an app that uses a hidden window. I've been fighting the extraneous cruft he left in his code ever since.

    The WTF isn't that we ignored the return result to sprintf. Its that he is remove something that he purposefully added for no good reason the line before.

  • RRA (unregistered)

    Mwahahaha I love TDWTF ! Even the users comments are great WTFs :)

    Gabelstaplerfahrer:
    And for the bonus points: of course the length can be read from N.
    No, boy. N is the array length, not the string length.
    akatherder:
    What is that attempting to do??? Replace one element of the array with the number 0?
    0 == '\0' -> surprising isn't it ?
    Rowboat:
    The actual issue is that the \r returns to the beginning of the string before writing the final 0
    This one has obviously never heard about the standard end-of-line marker in network protocols.
    A Guy:
    snprintf(buffer, N-1, "%s: %s",header,value);
    Wrong ! man snprintf. This should be N, not N-1.
    Pitabred:
    I think the 0 was trying to move the null-termination forward. But it's still not null, it's just the integer value 0. Null should be \0.
    Another one ! Your friend akatherder made the same mistake.
    Frank Mitchell:
    Oh, and snprintf isn't part of the standard C library, as far as I can tell.
    Wrong ! snprintf is C99.
    Leo:
    He WRITES a CR, then removes it. If he didn't want the CR, the line should be
    Nope, he removes the LF (\n).

    Etc, etc. I could go on and on.

  • mark (unregistered) in reply to EvanED
    EvanED:
    darin:
    Surely you know about memset(), the standardized function that the universe intended to be used here?

    In defense of using ZeroMemory if you don't have to worry about portability.

    (BTW, neither g++ -W -Wall or Visual C++ at level 4 warnings warn about this.)

    Good one! reddited.

    Since it's zero length, it just compiles to a no-op. If memset() is defined as a macro, the savvy compiler might throw it out altogether.

  • Omry (unregistered)

    And two more bonus point to the one that explains why the coder first added \n\r int the sprintf and then removed it.

  • (cs) in reply to mark
    mark:
    EvanED:
    darin:
    Surely you know about memset(), the standardized function that the universe intended to be used here?

    In defense of using ZeroMemory if you don't have to worry about portability.

    (BTW, neither g++ -W -Wall or Visual C++ at level 4 warnings warn about this.)

    Good one! reddited.

    Since it's zero length, it just compiles to a no-op. If memset() is defined as a macro, the savvy compiler might throw it out altogether.

    I should probably fess up... that's not my idea to search for it, and in fact it's not even my regex. The search came from here which was linked to from a /. post. That link has a bunch of other Code Search links, mostly to amusing searches. For some reason most of the links are broken though... but the one to the memset one works.

    Actually the really amusing thing is that even if you get it right, compilers may still throw it away when you use it to clear a sensitive buffer before freeing. The optimizer says "hmm, you assign 0 to this region, then free it immediately. Since you don't look at it afterwords, it's a useless assignment. I'll just remove that for you." There were a couple real-world products that had this sort of vulnerability, including a crypto library and something by MS. (This example is part of my research group's talking points for why (some) code analysis should be done on compiled binaries instead of source ;-).)

  • StupidGuy (unregistered) in reply to andr01d

    Hmmm... Just to get shure...

    Assuming the following: 1.) this function doesn't need to be reentrant 2.) embedded system - so probably not multithreading 3.) multiple instances of buffer wouldn't be needed..

    ... so I'd say that the real WTF(TM) would be that buffer isn't static... Because: a.) static variables generate fewer Bytes in the Binary (no Stack magic - especially if the embedded CPU doesn't have ENTER/LEAVE-Opcodes) b.) faster access c.) some embedded CPU's don't have Stacks (or very small ones) - so the Stack is emulated in SW (Example: Compiler cc65) d.) Binaries using static variables are faster (see [a])

  • Bodo Eggert (unregistered) in reply to Pitabred

    C guarantees that (int) 0 or (foo*) NULL will be converted to (char) '\0' here. However, the compiler may warn about converting a pointer to a char.

  • Anonymousse (unregistered) in reply to viraptor
    viraptor:
    Ok - i'm scared... comments are "The Real WTF":

    [snip]

    I'm sorry - you all fail :)

    Sigh. Kids today don't even know how C-style zero-terminated strings work anymore. Reminds me of this blog entry someone posted a link to not too long ago on this very forum: http://www.joelonsoftware.com/articles/fog0000000319.html

  • (cs)

    the first half of the first page of comments for this article is so full of WTF's, that i simply cannot bear to read further.

  • Baggy McBagster (unregistered)

    For me, this is the first TDWTF in which the WTF-ness of the comments utterly eclipses the (already considerable) WTF in the article.

    Way back when, TDWTF used to have plenty of idiots (GoatCheez, anyone?) but at least they were idiots who had actually done a fair bit of work related to computer programming.

    The generation of idiots we have now seem not to be actual computer programmers, and as a result their comments are no longer grand follies that begin "Let me tell you, good sir, why it is that your solution can never work on AIX...". No, they are petty and silly follies that could be generated algorithmically without much trouble.

    Shape up, people. I'm talking to the idiots in particular here. It's ok to be wrong, but you have to be wrong in a grand, majestic way -- not just plain thick.

  • StupidGuy (unregistered)

    But I just wanted to bring my all-time C-coding favorite: http://www.plethora.net/~seebs/faqs/c-iaq.html

    Some of the statements here just reminded me :-)

  • (cs) in reply to StupidGuy
    StupidGuy:
    Hmmm... Just to get shure...

    Assuming the following: 1.) this function doesn't need to be reentrant 2.) embedded system - so probably not multithreading 3.) multiple instances of buffer wouldn't be needed..

    ... so I'd say that the real WTF(TM) would be that buffer isn't static... Because: a.) static variables generate fewer Bytes in the Binary (no Stack magic - especially if the embedded CPU doesn't have ENTER/LEAVE-Opcodes)

    But when you're running the thing once you call the function, that array would stick around if it's static. (In fact, it's probably taking up [virtual] address space even if you haven't yet called the function.) Is that worth maybe saving a couple bytes of code?

    (You'd only save on the stack magic at all if there aren't any other locals; it's as cheap to allocate one word as a hundred on the stack in terms of code size. The only place you're likely to save is if instructions the reference the buffer would be longer with [reg+offset] than [address]. I don't know enough about embedded programming to be sure to what degree this is true.)

    Also, why would static be faster, other than if making it static would let you drop the stack increment and decrement?

    (c) seems like your best argument.

  • (cs) in reply to StupidGuy
    StupidGuy:
    But I just wanted to bring my all-time C-coding favorite: http://www.plethora.net/~seebs/faqs/c-iaq.html

    Some of the statements here just reminded me :-)

    "Only sometimes. It's not portable, because in EBCDIC, i and j are not adjacent."

    Ha, best answer ever.

    My favorite bug I've seen was when I was working for IBM's z/VM group for some client software that would allow you to configure the servers. Our software connected to z/VM using sockets, and we'd send commands and receive the response over the sockets. Most of the talking was text, but in the responses z/VM prepended the length of strings before transmitting them, but the lengths were in binary rather than "123" strings. The whole load went through EBCDIC to ASCII translation before going out on the wire, so by the time we got them the lengths looked like garbage. (The sockets server was in development at the time, and this was a bug carried over from when it used RPCs.)

  • StupidGuy (unregistered) in reply to EvanED

    I've a different point of view :-)

    1.) Stack-operations: Well, on "bigger" CPUs, you are right - especially when you have both a Base and Stack-Pointer and operations like "movl 8(%ebp), %eax" are possible. There are tons of CPUs that only allow PUSH/POP and few registers - so Stack-working is expensive (you want the 10th element on the Stack ? ok: POP A, POPA, .. :-)

    2.) Some CPUs allow Stack-adressing with one "general-purpose"-Register - so you could write "LDX #8; LDA 100,X; " - again more work than "LDA VARIABLE".

    Therefore I meant that this problem exists specifically on CPUs without Enter/Leave (or direct Stack-Access via ESP, EBP, ... )

    And therefore static variables would be /in these cases/ faster :-)

  • Anonymous Coward (unregistered) in reply to Steve Wahl
    Steve Wahl:

    Ahhh... so the WTF is the GNU documentation for snprintf! I won't quote it here, since I'd have to quote the whole thing to show it's not there, but the GNU/Linux man page for snprintf is quite ambiguous as to whether the string gets terminated or not.

    Sorry 'bout my confusion in earlier post.

    On my Ubuntu 6.06 system, 'man snprintf' says (the manpage is dated 2000-10-16, btw):

    "The functions snprintf() and vsnprintf() do not write more than size bytes (including the trailing '\0')."

    That's clear enough for me.

  • ComputerForumUser (unregistered)

    Actually, the real wtf here is that they didn't just do:

    10 let char buffer[N] := String.ToCharArray($header + ":" . value$;
  • (cs)

    Looking for the most efficient solution, you might call strlen() on both the source strings and then use memcpy to build the target string using the now known positions. memcpy could well be compiled more efficiently than any handwritten loop. strlen is only scanning for a 0 character we have to look for anyway.

    size_t build_string( char * buf, size_t buflen, const char * str1, const char * str2, char delim )
    {
       size_t len1 = strlen( str1 );
       size_t len2 = strlen( str2 );
       size_t reqLen = len1 + len2 + 2;
       if ( buflen >= reqLen )
       {
          memcpy( buflen, str1, len1 );
          buflen[ len1 ] = delim;
          memcpy( buflen + len1 + 1, str2, len2 );
          buflen[ reqLen - 1 ] = '\0'
       }
       else // copy what we can
       {
          size_t copyLen = ( len1 < buflen ? len1 : buflen );
          memcpy( buflen, str1, copyLen );
          if ( buflen > len1 )
          {
             buflen[ len1 ] = delim;
             size_t remainLen = buflen - len1 - 1;
             memcpy( buflen + len1 + 1, str2, remainLen );
          }
       }
       return reqLen;
    }
    

    Note I have assumed one of the later standards of C that allows variables to be declared near first use. If that isn't allowed in your standard then declare them at the top.

    Note that if the text doesn't fit in the buffer I have written it to write what it can, with no 0 terminator.

    Either way it returns what should be the required length of the buffer, i.e. target string + 1.

    A revised strcat that returns the end of the string instead of the beginning, but uses strlen and memcpy might be preferred. In addition we may have an option of that that allows the length of the source string to be passed in (could be more efficient if we know it).

  • Tim Reynolds (unregistered) in reply to SeekerDarksteel

    Inconceivable!

  • (cs) in reply to StupidGuy
    StupidGuy:
    I've a different point of view :-)

    1.) Stack-operations: Well, on "bigger" CPUs, you are right - especially when you have both a Base and Stack-Pointer and operations like "movl 8(%ebp), %eax" are possible. There are tons of CPUs that only allow PUSH/POP and few registers - so Stack-working is expensive (you want the 10th element on the Stack ? ok: POP A, POPA, .. :-)

    2.) Some CPUs allow Stack-adressing with one "general-purpose"-Register - so you could write "LDX #8; LDA 100,X; " - again more work than "LDA VARIABLE".

    Therefore I meant that this problem exists specifically on CPUs without Enter/Leave (or direct Stack-Access via ESP, EBP, ... )

    And therefore static variables would be /in these cases/ faster :-)

    Ah, I see. I'm not apparently familiar enough with emebedded programming; I didn't realize that you wouldn't necessarily be able to do, say, esp-relative addressing.

    Earl Purple:
    Looking for the most efficient solution, you might call strlen() on both the source strings and then use memcpy to build the target string using the now known positions. memcpy could well be compiled more efficiently than any handwritten loop. strlen is only scanning for a 0 character we have to look for anyway.

    You're still doing excess work; you're traversing str1 and str2 twice, so you've got more loop counter tests/increments than you need.

    How much of a difference this makes, I don't know. But I'm pretty sure you could do measurably better than combinations of C library functions. I don't know if it'd be worth it though.

  • StupidGuy (unregistered) in reply to EvanED
    How much of a difference this makes, I don't know. But I'm pretty sure you could do measurably better than combinations of C library functions. I don't know if it'd be worth it though.

    I'm completely with you...

    We assume the following: 1.) You are using actual Compilers (therefore good optimization) 2.) the CPU is extremely limited (because the 2 bugs shown by the thread-opener are worth mentioning despite of [1]) 3.) Memory is /probably/ limited, too...

    This means: (a) Because auf (3): The worst thing you can do is building a secondary libc. Even worse would be linking this "mylibc" into /each/ binary you write :-) (b) If you really need a general function like "mystringcat" - the function should be highly optimized for the specific CPU... Using (at least inline-)Assembler and Blockmoves.. Even good old Z80, 8086 and others had some commands for this... (c) If your real Bottleneck is only CPU - why writing functions ? Why not Macros ? (d) I assume I miss the point... Using the "most efficient solution" from Earl Purple: He wants to

    • copy
    • at max n Bytes
    • from a String (so limited by '\0')
    • in a Buffer and this 2 times...

    So... Why wouldn't one use memccpy ?

    SYNOPSIS #include <string.h>

    void *memccpy(void *dest, const void *src, int c, size_t n);

       The  memccpy()  function  copies no more than n bytes from
       memory area src to memory area  dest,  stopping  when  the
       character c is found.
    

    RETURN VALUE The memccpy() function returns a pointer to the next char- acter in dest after c, or NULL if c was not found in the first n characters of src.

    Or do I miss something ??? I hope there are no assumptions that memmove, .. exist but this function doesn't :-)
  • Corey (unregistered) in reply to Anonymousse

    Seconded. C string handling generates a lot of WTFs anywhere.

    I work in a company doing networking hardware code, embedded in C (no, not 'cisco). UI is all done in India, where it's easy to get C n00bs (but, of course, any programmer can do UI, right?) and that's where all our string-handling is.

    I've had to clean up stuff like folks clearing out a buffer with:

    memcpy(buf, NULL, 128)

    As a bonus, lots of our targets are VxWorks where the above won't crash. Hit it on a Linux target, though... blam!

    captcha: pointer (appropriate)

  • (cs) in reply to StupidGuy
    StupidGuy:
    (d) I assume I miss the point... Using the "most efficient solution" from Earl Purple: He wants to - copy - at max n Bytes - from a String (so limited by '\0') - in a Buffer and this 2 times...

    So... Why wouldn't one use memccpy ?

    Because you have to know n before calling memcpy. Thus the dynamic execution looks like you have the following two loops (assuming a naive implementation of strlen and memcpy; in reality you can do rather better than this, but not in such a way that it affects my point; I also wrote these using array references while pointers would improve the code, but this is a good opportunity for the compiler to step up):

    len1 = len2 = 0;
    while(str1[len1]) len1++; // loop 1, in strlen(str1)
    while(str2[len2]) len2++; // loop 2, in strlen(str2)
    
    for(int i=0 ; i<len1 ; ++i) finalbuf[i] = str1[i]; // loop 3, in memcpy
    for(int i=0 ; i<len2 ; ++i) finalbuf[i+len1] = str2[i]; // loop 4, in memcpy</pre>
    

    But look, you're going over str1 twice, and str2 twice: you have two loads of str1[i], and two loads of str2[i]. This is silly.

    Better to transform these loops into something like the following:

    len1 = len2 = 0;
    while(str1[len1]) {
      finalbuf[len1] = str1[len1];
      len1++;
    }
    while(str2[len2]) {
      finalbuf[len1 + len2] = str1[len2];
      len2++;
    }
    

    Now you have half as many loop increments, half as many loop tests, and half as many memory loads (after the compiler does some available expression analysis) as you had before.

    I haven't checked, but I suspect the compiler is very unlikely to do this optimization. It would either have to do detailed enough interprocedural analysis to find it, or (much more likely but still hard to see the compiler doing; this optimization uses non-trivial information) inline both strlen and memcpy then fuse the loops.

  • StupidGuy (unregistered) in reply to EvanED
    EvanED:
    StupidGuy:
    So... Why wouldn't one use memccpy ?

    Because you have to know n before calling memcpy.

    Sorry, but i wrote of memccpy, NOT memcpy!

    And therefore I included the manpage... You needn't go over the string 2 times...

    Example - pseudo: startpoint_for_string2 = memccpy(buffer,string1,0,strlen(buffer)); if (startpoint_for_string2) { memccpy(startpoint_for_string2,string2,0,...); } buffer[sizeof(buffer)-1] = '\0';

    Mixing up memcpy and memccpy is a real WTF :-)

    Where is the problem ?

  • StupidGuy (unregistered) in reply to StupidGuy
    memccpy(startpoint_for_string2,string2,0,...)
    should assumingly be memccpy(startpoint_for_string2-1,string2,0,...)

    :-)

  • (cs) in reply to StupidGuy
    StupidGuy:
    EvanED:
    StupidGuy:
    So... Why wouldn't one use memccpy ?

    Because you have to know n before calling memcpy.

    Sorry, but i wrote of memccpy, NOT memcpy!

    And therefore I included the manpage... You needn't go over the string 2 times...

    Example - pseudo: startpoint_for_string2 = memccpy(buffer,string1,0,strlen(buffer)); if (startpoint_for_string2) { memccpy(startpoint_for_string2,string2,0,...); } buffer[sizeof(buffer)-1] = '\0';

    Mixing up memcpy and memccpy is a real WTF :-)

    Where is the problem ?

    Smacks head This is why I shouldn't post on the internet after 3 hours of sleep.

    I noted the memccpy at the end, but for some reason assumed it was a typo. Sorry about that.

  • w0 (unregistered)

    Simple fix:

    char *str = NULL; int len = asprintf(&str, "%s: %s", header, value);

    Note: To remove the "\n\r", you might want to remove it from the format string. The get the length, you might use the return value of sprintf/asprintf. To allocate the buffer, you might leave it to the only one knowing the length of the string (sprintf is insecure). If asprintf() isn't available in your libc, COMPLAIN ! Sidenote: Wasn't it "\r\n" anyway ?

    Fix for asprintf (dirty, costly): int fd = fdopen("/dev/null", "w"); // fastest write on earth int len = fprintf(fd, "my format string", ...); char *buf = malloc(len * sizeof (char)); sprintf(buf, "my format string", ...); // whatever with buf free(buf); // don't be stupid

  • StupidGuy (unregistered) in reply to w0
    char *str = NULL; int len = asprintf(&str, "%s: %s", header, value); If asprintf() isn't available in your libc, COMPLAIN !
    it's NOT Posix, it's not stdlibc - it's just a GNU extension. And the behaviour is different on FreeBSD... Anyways, small embedded devices don't have a full glibc - I'd believe mostly uclibc or something similar. Besides you can't control the Size of the generated buffer. You only know that it's size will be /at least/ big enough to hold the string - so it could be one the one hand too big - on the other hand it could be alwys exactly the needed size - so you could have a chance of heap fragmentation. You just can't control this.
    sprintf is _insecure_
    *chchch*. Why ? If you /know/ the length of the incoming texts - it wouldn't be insecure.
    Sidenote: Wasn't it "\r\n" anyway ?
    [ ] you understood the meaning of "\r" and "\n"
    int fd = fdopen("/dev/null", "w"); // fastest write on earth
    Wow. Very portable. Shurely each embedded device has "/dev/null".

    sprintf ? Funny... So you'd

    • iterate over the strings one time for calculating the length (via fprintf),
    • use format Strings (so the format-Strings needs to be parsed, too)
    • sending the byte stream through the kernel (to /dev/null) - so 2nd iteration over the data
    • iterate again over the strings for sprintf
    • parse again a format-string ....

    hehe, I think the original method (the one posted by the thread-owner) was extremely faster :-)

  • brian (unregistered) in reply to Ev

    Finally, someone who actually knows "C". :-)

  • tamosius (unregistered)

    int len = strlen(buffer); buffer[len-2] = 0;

    Hm.... it makes me wonder what happens when length of the buffer <= 1?

    buffer[-1] = 0; <--- Oops!

  • tamosius (unregistered)

    char buffer[N]; ...... int len = strlen(buffer); buffer[len-2] = 0;

    Don't we have 'N' that specifies that buffer length? what's the point to use strlen() then?

    BTW - how many point do I get for this one? ;-)

  • (cs) in reply to mark
    mark:
    Since it's zero length, it just compiles to a no-op. If memset() is defined as a macro, the savvy compiler might throw it out altogether.
    GCC does this (even inlining memcpy if it's small enough).

    GCC and any decent C++ compiler should have caught all these errors (I don't use "C" much anymore, but I do a lot of "C" compiled as C++, which is a lot nicer).

    Addendum (2007-02-15 15:07): Whoops, shouldn't post while taking cold medicine. C++ will catch a few of the memset errors listed, but not most of them.

  • (cs) in reply to tamosius
    tamosius:
    char buffer[N]; ...... int len = strlen(buffer); buffer[len-2] = 0;

    Don't we have 'N' that specifies that buffer length? what's the point to use strlen() then?

    BTW - how many point do I get for this one? ;-)

    -1 because it's wrong, -1 because you didn't read earlier in the thread to see that this has already been posted an corrected. So that's, uh, negative two points.

    N is the maximum length of a string the buffer will support, strlen gives the string length that is ACTUALLY stored in the buffer.

    If you do, for instance,

    #define N 100 char buf[N] = "Hello World"; printf("N=%d, strlen(buf)=%d\n", N, strlen(buf));

    you'll see what I mean. Adding a '\0' at strlen(buf)-2 would drop off the "ld" from the end; adding it at N-2 would do nothing.

    (If you're too lazy, the output is "N=100, strlen(buf)=11")

  • DES (unregistered) in reply to Marco Schramp
    Marco Schramp:
    No assigning zero is correct. Actually '\0' == (char) 0 is true.

    It may be on your machine, but the C standard does not guarantee it.

  • StupidGuy (unregistered) in reply to DES

    I assumed the following:

    1.) Character constants are of type int.

    • therefore
    sizeof('a') == sizeof(int).

    2.) '\0' means a character constant (therefore an int) with all bits zero.

    3.) (char) 0 means a character (NOT a character constant) with all bits zero.

    4.) and integer with all bits zero is equivalent to a long with all bits zero is equivalent to a short with all bits zero is equivalent to a character with all bits zero ... and so on.

    Therefore the C-Standard guarantees that the condition '\0' == char(0) matches.

    5.) The only exception is the (in)famous NULL-Pointer.

    char *p; p = 0;
    

    If you don't know the machine, you can't say the bits of 'p' - because the NULL-Pointer just guarantees to compare unequal to a pointer to any object or function - not more. So the null-pointer is implementation-defined... whereas a char of value 1 isn't :-)

    Please tell me where I'm wrong according to standards...

  • anonymous (unregistered) in reply to EvanED
    EvanED:
    darin:
    mrprogguy:
    Unfortunately, the C++ spec manages to mangle things to the point where, for the purposes of definition, 0 != '\0' != NULL, which is just ridiculous.
    The problem with NULL is that it was inconsistently defined in many C and UNIX systems. Sometimes it was 0, sometimes (char*)0, sometimes (void*)0.

    So C++ uses 0 instead and deprecates NULL. 0 can be assigned to or compared with any pointer type without type casting first. It's not ridiculous, it's an improvement on the confusion that C implementations had.

    It's more than that though; it's that C++ doesn't allow implicit pointer conversions between unrelated types. In C, you can say char* c = (void*)0; (and thus = NULL), but in C++ you can't.

    Defining NULL as (void*)0 would have required C++ to make it so that compilers recognize the literal null pointer "(void*)0" and allowed conversions between that type. Stroustrup/the standards committee decided that this was undesirable, so defined NULL to be just 0.

    This is the real reason, rather than inconsistency between what NULL was in C implementations.

    (Finally, NULL isn't officially deprecated either. It may be with C++0x which introduces the nullptr keyword though, who knows.)

    NULL is neither in C nor in C++ standard. It is a pre-processor artifact and usage dates back to beginning of C. Try compiling C/C++ the following program on a Unix system see what happens (clue: it does not compile)

    int main(int c, char **v) { return NULL == 1; }

Leave a comment on “One Step Forward...”

Log In or post as a guest

Replying to comment #:

« Return to Article