Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Would you be open to sharing what you did with strings?

My central argument in the response there is that writing buf[len] = ‘\0’; is almost always a sign that you either don't know libc functions, aren't willing to use them, are trying to outperform them (the performance of libc functions is a legitimate complaint for some use cases), or what you're dealing with is not a string but some arbitrary binary blobs that you're trying to make strings out of (in that case, you can't blame the string representation or string handling functions for not knowing what the extent of your binary is; yes, you'll have to first create a string, knowing the length).

To put it more explicitly, if you always provide a valid buffer and size, snprintf() will always terminate your string. strlcat() and strlcpy() will always terminate your string. If you need formatted catenation, you can make a trivial wrapper around snprintf that takes a pointer to the end of your string and updates the "head"; this can be called successively without ever having to compute a length outside the wrapper. asprintf() will allocate and terminate your string. Things that need the length of your string (strspn, strchr, etcetra) will figure it out since it is implied by the already-present nul byte. strtok & co (they have their issues) also work without requiring you to do any manual termination.

What this means in practice is that you can have thousands of lines of string handling code that never manually terminates a string and only deals with lengths to the extent that your "business logic" needs to. Unless you're actually trying to use the string representation to your benefit by manually splicing it any which way, inserting nul bytes based on arcane computations.. in that case, it sounds like you got what you wanted. Yes, people actually do that sometimes: they figure out how easy it is to manipulate the string representation by hand and thus avoid library functions, and then they complain about doing it by hand.

There are always exceptions of course, so I'm giving you benefit of the doubt. That's why I'm curious to see what you were doing. Having to point out library functions however is a regular thing as people seem to always start out by hand-rolling it for some reason.

As for the question about string libraries.. well, I gotta point out that "small" wasn't a qualifier in the previous discussion. Popular libraries include sds, bstring, glib strings. Plan9port also has the extensible string library. There's icu for fancy unicode stuff but I have no experience with it and it probably isn't "small." There are plenty more if you look around, and I'll let you judge the size of the choices for yourself. I'm pretty sure one of these choices is always mentioned in these HN threads when someone asks for recommendations, including sds in the bchs thread.



I just always put the buf[len] = ‘\0’ to cover myself if I screwed up something. Generally I also use calloc if it’s standalone code as well.

I was copying strings from a file of allowed binary names into a list of char * and also logging the first two parameters to execve to disk, appending. It was fine but 100 times scarier than the same in Go would be.

I have used snprintf and strl* functions when I was doing fancier stuff, but have not tried asprintf. It has been a long time that I was doing large amounts of c code, and then I was either doing binary with Len always passed along or else calling some template library, but I do thank you for the lib recommendations.

My point is if you ask for a good string library that makes it as safe and easy as the same in Go, you will not see a pattern of answers t use well known strlib X.


Regarding buf[len] = '\0', I've personally had to use it in many scenarios following strncpy, which doesn't add a null terminator if the maximum length is reached. Do you know of any simpler way of getting a prefix up to a certain length?


snprintf. If you want to stick to the (safest) pattern of only passing the buffer size for the second parameter, you'd do this:

    snprintf(buf, sizeof buf, "%.*s", prefix_length, source_str);
Example:

    $ cat x.c
    #include <stdio.h>
    int main(void) {
      char buf[128], tinybuf[5];
      const char *copythis = "hello there\n";
      snprintf(buf, sizeof buf, "%.*s", 5, copythis);
      snprintf(tinybuf, sizeof tinybuf, "%.*s", 5, copythis);
      printf("buf: %s\n", buf);
      printf("tinybuf: %s\n", tinybuf);
    }

    $ cc -W -Wall -O3 x.c
    x.c: In function ‘main’:
    x.c:6:41: warning: ‘snprintf’ output truncated before the last format character [-Wformat-truncation=]
        6 |  snprintf(tinybuf, sizeof tinybuf, "%.*s", 5, copythis);
          |                                         ^
    x.c:6:2: note: ‘snprintf’ output 6 bytes into a destination of size 5
        6 |  snprintf(tinybuf, sizeof tinybuf, "%.*s", 5, copythis);
          |  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    $ ./a.out
    buf: hello
    tinybuf: hell


Thanks! I've never considered using snprintf in that way before; the default warnings are annoying, even though their intent is understandable.


The warning isn't a false positive here; truncation is going on in that line: the chosen prefix doesn't fit into tinybuf.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: