4

I have read this post. But when I tried:

  printf("before null %c after null\n", 0);  // (ASCII=0) != '\0' ??

instead of getting:

before null 

I got:

before null   after null

So my question is: Is ASCII value 0 actually equal to '\0'?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
iamsuperwen
  • 57
  • 1
  • 4
  • 1
    Yes, but printf is printing your string piece by piece, so it doesn't end at the middle `\0`. It prints `"before null "`, then the `%c` then `" after null"`. – xanatos Jun 09 '18 at 05:59
  • 1
    Yes, you can always comment in your own question and/or own answers. And even on responses on your own questions. – xanatos Jun 09 '18 at 06:01
  • Thanks for answering. But I've tried `printf("before null \0 after null\n");` and the output is: `before null ` What's the difference between these two? – iamsuperwen Jun 09 '18 at 06:03
  • 5
    @iamsuperwen It is printf that evaluates the "%c" and decides what to do with the corresponding value (0). The NUL is *out-of-band* with respect to a string (similar to how SQL placeholders prevent SQL injection). Compare with `printf("before null %c after null \0 after silly null in string\n", 0);`. – user2864740 Jun 09 '18 at 06:05
  • user286 said what I wanted to say better :-) – xanatos Jun 09 '18 at 06:06
  • 3
    Similarly, `printf("%cn", '\\')` outputs `\n` (two characters), not a newline. – melpomene Jun 09 '18 at 06:37
  • Note that if you fed the output of the code fragment in the question through a program that prints all bytes visibly, you'd find that the output included a null byte, which doesn't display as a visible character (though your output seems to show a 'blank' where the null byte is printed). You could use `od -c`, `cat -v`, `xxd -g`, and no doubt other programs to see that null byte. – Jonathan Leffler Jun 09 '18 at 17:58

3 Answers3

5

Is ASCII value 0 actually equal to \0?

Yes


The differences in how the strings are stored in memory and handled by functions like printf() are important.

"before null %c after null\n"
"before null \0 after null\n"

Both are stored in memory with an implicit \0 terminator at the end. The fact that the second has an explicit \0 character in the middle changes things.

printf() will scan the string until "the end", printing components as it goes... in C "the end" typically means until the first \0 / nul character.

With the first variant, printf() copies characters to the output until it reaches the %c directive, at which point it looks at the arguments that were given to the function... it might find that you gave '\0', or it might find that you gave '+' - either way, it copies this to the output. It'll then continue copying characters to the output, seeking "the end" of the string.

With the second variant, printf() will start copying characters to the output, will find "the end" (denoted by the \0), and stop.

If you were to use snprintf(), then the results / outputs would contain the following: (again, with implicit \0 termination)

"before null \0 after null\n"
"before null "

If you were to subsequently print both of these, they would look the same, but the memory content would be different.

However, the output of printf() is the terminal (or a file)... what happens for \0 depends on your terminal emulator... it might simply not be shown, it might be displayed as a space, or it may have a funny box symbol...

The important thing to note, is that this occurs at run time - not compile time.

Attie
  • 6,690
  • 2
  • 24
  • 34
3

That's because printf is NOT actually replacing "Hello %s", "World" with "Hello World" then print. No. It does not concatenate.

Rather, it actually prints each character alone in order, and when it encounters an argument, it starting printing each character from it directly too without concatenating.

If you ever tried to print a single null character using putchar(), You'd notice that it prints a space instead, that's why printf prints a space too based on it. (Note that It'll print nothing on other systems like Linux).

Sample code of how printf actually work.


const char * x;
// while the current char != '\0'
while (*format)
{
    // if the current char == '%'
    if (*format == '%')
    {
        // increment the pointer so we can point to the next char and skip printing '%'
        switch (*(++format)) // then switch that next char (specifier).
        {
        case 'c':
            putchar(va_arg(args, char)); // if the argument is null, then it's putchar(0);
            break;
        case 's':
            // regular operation of printing a string argument.
            x = va_arg(args, const char*);
            while (*x) putchar(*x++);
            break;
        }
        // skips the format specifier so we don't print it (e.g 's', 'c'..)
        *format++;
    }
    // else: isn't a format specfier.
    else
       // print the current char (pointer) of the original string
        putchar(*format++); // increments it for the next operation.
}
va_end(args);

So returning to your question, It will print each character and when it comes to the argument 0 which is null, putchar() will either put a space or nothing based on your system.

You can say that printf arguments don't really have any relationship with the original string to terminate it, they don't know each other. Like when you printf("Hello %s, from SO!", "World");, "World" is actually terminated at the end with \0, but it will terminate just itself, not the other , from SO!.

And Yes, 0 is '\0'. they're the same character.

Beyondo
  • 2,952
  • 1
  • 17
  • 42
  • 1
    If `putchar(0)` puts a space character, your system is broken. It outputs a NUL character (on ASCII systems). How that affects terminals or applications is another matter. – Jens Jun 09 '18 at 07:33
  • My system isn't broken. It'll print a space on Windows systems and nothing on Linux. based on the OP code result, I tried to answer him as it looks he's using Windows system. – Beyondo Jun 09 '18 at 07:42
  • 2
    Then that's the Windows terminal *converting* the NUL to a space. If you redirect the output to a file and look at it with a hex editor, you'll find a NUL, not a space. – Jens Jun 09 '18 at 07:52
1

printf will not terminate the printf format-string at that character position for the %c format-specifier when given values 0 or '\0'. Instead, the terminal output for the nul will generally be a placeholder (e.g. a space or the like)

However you can insert a nul into the string and then output the string using the %s format-specifier and see that in fact decimal 0 is actually the ASCII value for the equivalent ASCII character '\0' and will terminate the string at the point of the nul-character (see: www.ASCIItable.com), e.g.

#include <stdio.h>

#define FMT "before null %c after null\n"

int main (void) {

    char buf[sizeof FMT * 2];

    puts (FMT);

    sprintf (buf, FMT, 0);
    printf ("using 0 : '%s'", buf);
    putchar ('\n');

    sprintf (buf, FMT, '\0');
    printf ("using \\0: '%s'", buf);
    putchar ('\n');

    return 0;
}

Example Use/Output

$ ./bin/str_printf_null
before null %c after null

using 0 : 'before null '
using \0: 'before null '
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • 1
    "_printf will not output a nul to stdout using the %c format-specifier and values 0 or '\0'_" - seems to for me...? – Attie Jun 09 '18 at 07:09
  • Well, that may indicate that how an OS handles output of a *nul-character* with `%c` is *implementation defined*. I tested on Linux. I'll also test on windoze and see if they implement it the same. Thanks for the info. – David C. Rankin Jun 09 '18 at 07:43
  • Hmm - I get "**Zoinks! You've taken a wrong turn.**" from your link. I also tested on Win7 and get the same output. How do I look at your code? – David C. Rankin Jun 09 '18 at 07:48
  • 2
    Oh yes, yes... We are saying the same thing, but differently. I'm not saying the `%c` won't output the byte, what I'm saying is that it will not have the effect of terminating the `printf` *format-string* at that character position. It simply uses a placeholder for the `nul` within the output. – David C. Rankin Jun 09 '18 at 07:51
  • 1
    @Attie - I updated in hopefully a more articulate manner to reflect the actual behavior. – David C. Rankin Jun 09 '18 at 07:55
  • ah, gotcha, thanks for clarifying... also see my answer regarding `snprintf()` followed by `printf()` – Attie Jun 09 '18 at 07:55