16

I need to concatenate some strings, and I need to include NULL bytes. I don't want to treat a '\0' as a terminating byte. I want to save my valuable NULL bytes!

In a code example, if

    char *a = "\0hey\0\0";

I need to printf in a format that will output "\0hey\0\0".

-AUstin

austin
  • 998
  • 2
  • 12
  • 22
  • 4
    How are you going to work out how long the string is? –  May 23 '11 at 06:40
  • its predetermined. i have allocated space, say 8 bytes. i want to smack together a few strings of various, given sizes, using a printf(%s%s%s...) – austin May 23 '11 at 06:44
  • 10
    If the array has embedded nulls it is not a string. Do not call it a string. Call it an array of (unsigned) char (with embedded nulls). As it isn't a string you cannot reliably use any *string function* on that object: no `"%s"` conversion to `printf` (or `scanf`), no `fputs`, no `fgets`, no `strcpy`, no `strcat`, ... – pmg May 23 '11 at 11:05

3 Answers3

13

How about:

int i;
for(i = 0; i < 4; i++)
    printf("%c", a[i]);

If you want a 'printf-like' function to use this when you specify %s in a format string you could include the above code in your own function. But as @Neil mentioned, you'll struggle finding an alternative to looking for null bytes to determine the length of strings. For that I guess you could use some kind of escape character.

sje397
  • 41,293
  • 8
  • 87
  • 103
  • 2
    Why would it take lots of code? It's a trivial for loop? It's hard to see an alternative to this. – David Heffernan May 23 '11 at 06:55
  • 1
    i say lots of code because to do this for 10 strings means 10 for-loops. not the end of the world of course. it just seems less ideal, however i admit im not working with the most ideal situation – austin May 23 '11 at 07:25
  • @austin - Does it not mean one function and ten calls? – Bo Persson May 23 '11 at 16:37
  • 2
    I recommend using putchar() instead of printf()'s for this. Characters don't need to be formatted, so putchar() is a lot more efficient. – Owl Aug 25 '16 at 00:47
  • Or how about [`fwrite(a, 1, 4, stdout);`](https://en.cppreference.com/w/c/io/fwrite). That's even better than `putchar`; you don't need to loop over the bytes one at a time at all. – Peter Cordes Jan 03 '19 at 16:29
8

The issue here is that the length of the string a cannot be easily determined. For example, your code..

char *a = "\0hey\0\0";

.. allocates seven bytes to the string, the last being the NULL terminator. Using a function like strlen would return 0.

If you know the precise length of the string, then you can write or iterate over the bytes thus:

#ifdef ESCAPE_NULLS
    int i;
    for (i = 0; i <= 6; i++)
        if (a[i] == 0)
            printf("\\0");
        else
            printf("%c", a[i]);
#else
    write(1, a, 6);
#endif

But you have to know about the 6.

The alternative is not to use NULL-terminated strings, and instead implement an alternative storage mechanism for your bytes; for example a length-encoded array.

#include <stdio.h>

typedef struct {
    int length;
    char *bytes;
} bytearr;

void my_printf(bytearr *arr)
{
    #ifdef ESCAPE_NULLS
        int i;
        for (i = 0; i <= arr->length; i++)
            if (arr->bytes[i] == 0)
                printf("\\0");
            else
                printf("%c", arr->bytes[i]);
    #else
        write(1, arr->bytes, arr->length);
    #endif
}

void main(void)
{
    bytearr foo = {
        6, "\0hey\0\0"
    };
    my_printf(&foo);
}

Graceless, but hopefully you get the idea.

Edit: 2011-05-31

Rereading the question I just noticed the word "concatenate". If the NULL characters are to be copied faithfully from one place in memory to another (not backslash-escape), and you know the total number of bytes in each array beforehand, then you can simply use memcpy.

#include <string.h>
char *a = "\0hey\0\0";   /*  6 */
char *b = "word\0up yo"; /* 10 */
char *c = "\0\0\0\0";    /*  4 */
void main(void)
{
    char z[20];
    char *zp = z;
    zp = memcpy(zp, a, 6);
    zp = memcpy(zp, b, 10);
    zp = memcpy(zp, c, 4);
    /* now z contains all 20 bytes, including 8 NULLs */
}
Matty K
  • 3,781
  • 2
  • 22
  • 19
  • you have an if/else statement in the first part of your response. does this imply that a printf("%c") on a NULL byte will not work? – austin May 23 '11 at 07:28
  • It depends if you want to faithfully pass the null byte through to STDOUT, or backslash-escape it (which was how I read the original question). – Matty K May 26 '11 at 14:40
  • @MattyK : Shouldn’t`write(1, a, 6);`be`write(1, a, sizeof(char)*6);`? – user2284570 Oct 21 '15 at 13:00
  • Sure, if you're writing proper, portable code. It's easy to be lazy and assume `sizeof(char)==1` – Matty K Oct 22 '15 at 06:48
  • 2
    Don't mix POSIX `write()` with stdio `printf`. Use `fwrite`. And don't use `printf("%c"` for singe bytes, use `putchar`! Or better, search for the next `0` and use `fwrite` for all the non-zero bytes with one function call. Or copy-and-escape into a local buffer and fwrite that. And BTW, `memcpy` isn't `strcat`: `memcpy` returns its first arg unmodified, not the *end* of where it wrote. So your update repeatedly overwrites the start of `z`. But yes, memcpy is the right tool for the job, if you use the right destination pointer. – Peter Cordes Jan 03 '19 at 16:27
0
char *a="\0hey\0\0";
int alen = 7;

char buf[20] = {0};
int bufSize = 20;

int i=0;
int j=0;
while( i<bufSize && j<alen )
{
    if(a[j]=='\0') {
        buf[i++]='\\';
        buf[i++]='0';
        j++;
    }
    else {
        buf[i++] = a[j++];
    }
}

printf(buf);
daalbert
  • 1,465
  • 9
  • 7