71

Why does C's printf format string have both %c and %s?

I know that %c represents a single character and %s represents a null-terminated string of characters, but wouldn't the string representation alone be enough?

Krupal Shah
  • 8,949
  • 11
  • 57
  • 93
Red Banana
  • 742
  • 2
  • 7
  • 13
  • 50
    Probably to distinguish between null terminated string and a character. If they just had `%s`, then every single character must also be null terminated. **char c = 'a';**, in this case `c` must be null terminated. This is my assumption though :) – Mahesh Jun 01 '12 at 07:18
  • 23
    @Mahesh: Why not put that as an answer, Especially, when *It is the answer* – Alok Save Jun 01 '12 at 07:20
  • 4
    Huh? What if you just want to put a single character, why should you need to append a NUL to it? Or did I not get something in this question? – Christian Rau Jun 01 '12 at 07:21
  • 2
    @Mahesh your assumption is the answer to this question I believe – Krishnabhadra Jun 01 '12 at 07:21
  • @Mahesh Yeah, I'd have upvoted it as an answer like I did the comment haha. – Paul Jun 01 '12 at 07:21
  • 1
    `%c` expects a datum of `char` type - a value primitive. `%s` expects a datum of `char *` type, a pointer. The difference between these is enormous in C, in spite of the fact that they both apply to text. – Russell Borogove Jun 01 '12 at 19:02

11 Answers11

99

Probably to distinguish between null terminated string and a character. If they just had %s, then every single character must also be null terminated.

char c = 'a';

In the above case, c must be null terminated. This is my assumption though :)

Mahesh
  • 34,573
  • 20
  • 89
  • 115
  • you can assume inside a comment, but not in an answer.. Still +1 since assumption is correct.. ;) – Krishnabhadra Jun 01 '12 at 07:24
  • 13
    Not a real answer because that could be avoided by `%.1s` – Jens Gustedt Jun 01 '12 at 07:35
  • @JensGustedt Then your solution could make C work with only **%s**? – Red Banana Jun 01 '12 at 09:20
  • @GustavoBandeira, in sense that if you'd always have your `char` inside an addressable object, yes. – Jens Gustedt Jun 01 '12 at 09:42
  • Yeah, this is probably the same reason the Delphi format function doesn't have %c. Because Pascal strings are not null terminated. – Peter Turner Jun 01 '12 at 13:01
  • 1
    A char is a single byte in memory and never terminated. Your assumption is wrong. – Moog Jun 01 '12 at 14:11
  • 3
    @Merlin I didn't say that. Probably, my sentence formation might be confusing. As you might know, the format string for the format specifier `%s` must be **null-terminated**. If there is no `%c` in the language, then in my above example, identifier `c` must be null terminated to be able to use with the format specifier `%s`. – Mahesh Jun 01 '12 at 14:24
  • 1
    @JensGustedt: I don't know whether implementations of *printf can be relied on not to read past the number of bytes being displayed. Granted, if "%.1s" were the way we standardly expressed "%c", they would soon start to do so. – dubiousjim Jun 01 '12 at 14:36
  • 7
    @mahesh Confusing an answer is worse than giving a wrong answer ... I can't believe you are arguing about it. – Moog Jun 01 '12 at 14:50
  • 42
    -1 this is a poor answer... The reason that `printf("%s", c)` would not work is not because of null-termination, but because `printf` expects `%s` to point to a memory location, while `c` is a character-value. Null-termination *is* the reason `printf("%s", &c)` wouldn't work, but even if it did, having a `%c` identifier would *still* make sense, because requiring `&c` is inconsistent with the way every other variable-type is printed. So just saying *"it's because of null termination"* is incorrect. – BlueRaja - Danny Pflughoeft Jun 01 '12 at 17:37
  • 9
    I don't see how 74 people thought this was the correct answers since it doesn't mention that you would have to pass a `char*` to even get the one character to print with `%s`. Just reiterating what BlueRaja said – Ruan Mendes Jun 02 '12 at 09:00
  • @JensGustedt, but then must `c` be a `char`, but `c` will in the most case be a int, and `%c` works for `char` and `int` (because of type promotion). – 12431234123412341234123 Mar 30 '17 at 10:59
  • single character is not null terminated. String needs to know either size OR a special 'null' character to let the system know about its termination – siddhusingh Dec 16 '17 at 09:53
74

%s prints out chars until it reaches a 0 (or '\0', same thing).

If you just have a char x;, printing it with printf("%s", &x); - you'd have to provide the address, since %s expects a char* - would yield unexpected results, as &x + 1 might not be 0.

So you couldn't just print a single character unless it was null-terminated (very inefficent).

EDIT: As other have pointed out, the two expect different things in the var args parameters - one a pointer, the other a single char. But that difference is somewhat clear.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • 8
    you should mention that `%s` takes a pointer while `%c` takes a character itself. – Matt Jun 01 '12 at 07:21
  • And what is more important you only could print a character if its address can be taken. That would exclude printing the returns from functions, e.g, and also register variable. – Jens Gustedt Jun 01 '12 at 07:27
  • 1
    You could avoid the issue of the null termination by providing a precision. – Jens Gustedt Jun 01 '12 at 07:30
38

The issue that is mentioned by others that a single character would have to be null terminated isn't a real one. This could be dealt with by providing a precision to the format %.1s would do the trick.

What is more important in my view is that for %s in any of its forms you'd have to provide a pointer to one or several characters. That would mean that you wouldn't be able to print rvalues (computed expressions, function returns etc) or register variables.

Edit: I am really pissed off by the reaction to this answer, so I will probably delete this, this is really not worth it. It seems that people react on this without even having read the question or knowing how to appreciate the technicality of the question.

To make that clear: I don't say that you should prefer %.1s over %c. I only say that reasons why %c cannot be replaced by that are different than the other answer pretend to tell. These other answers are just technically wrong. Null termination is not an issue with %s.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • It is a real one... why would you prefer having `%.1s` over `%c`? What you're doing is re-inventing a marker for `printf`... – Luchian Grigore Jun 01 '12 at 07:35
  • 5
    @LuchianGrigore: No, I don't say that anybody should prefer that, in the contrary. I say that `%.1s` would have the functionality of printing just one character that musn't even be null terminated, so the null termination is not a valid objection to "introduce" `%c`. – Jens Gustedt Jun 01 '12 at 07:38
  • +1 For me this is a very good point, which talks about the *necessity* rather than the *convenience* @LuchianGrigore: *why would you prefer having `%.1s` over `%c`?* Because it doesn't add one more additional format descriptor. Using precision in format descriptor is a functionality that needs to be provided irrespective of whether you need it for a single character. So eventually that would save you an additional format specification. – Alok Save Jun 01 '12 at 07:38
  • 4
    @Als Would `%.1s` be expecting a `char` or a `char*`? – Luchian Grigore Jun 01 '12 at 07:48
  • @LuchianGrigore: That was not the real reason being discussed or stated in your answer when i saw it. – Alok Save Jun 01 '12 at 07:55
  • @Als I know, but my question is about *this* answer. You want some marker that tells printf to print a single char. You want to pass `char x;` to the function. So there's no real advantage to using `%.1s` over `%c`, because using, say `%.2s` would require a different parameter type. – Luchian Grigore Jun 01 '12 at 07:57
  • 2
    @LuchianGrigore, nobody says that. My answer simply states that null termination is not the right argument, but that taking the address of the object is an argument. – Jens Gustedt Jun 01 '12 at 08:12
  • 1
    +1 correct. Consider `printf("%c", 'a' + 8)` - you can't write that as ``printf("%.1s", 'a' + 8)` – MSalters Jun 01 '12 at 10:55
  • 1
    -1 This answer does not [code for readability first](http://stackoverflow.com/a/1949293/19308). – Joshua Drake Jun 01 '12 at 13:50
  • 3
    @JoshuaDrake, did you read my answer. I find downvoting for that really excessive. I don't say that anybody should use `%.1s` for printing a character. This was not the question. The question was whether `%c` was strictly necessary. And I give an affirmative answer to that, just stating that the reasons are different that the ones given in the other answers. – Jens Gustedt Jun 01 '12 at 17:40
  • 3
    @JoshuaDrake: -1 for not reading the answer. The question asks Why does X exist in C, and this answer clearly explains that X MUST exist in order to work with rvalues. Without consideration for rvalues, the language/library designers probably would (and just as probably SHOULD) have left it at %.1s as it makes the printf cleaner (adn thus more readable). Without c, printing the result of a function that returned a single character could end up being quite complex (particularly if calling multiple functions with side effects and which are order dependent). – jmoreno Jun 01 '12 at 18:17
  • @JensGustedt The question, to me, was generally "why do they have this?" I can accept your reading, however, based on the closing bit of "wouldn't string representation alone be enough", and given your edit I'll happily flip the bit. – Joshua Drake Jun 01 '12 at 18:25
  • @Shanshui If you read the my answer, you'll see that yes it does. – Ruan Mendes Jun 07 '12 at 00:25
  • @Juan Mendes and @Shanshui strictly speaking the answer is no, because `char` is a keyword. ;-) – Patrick Schlüter Dec 27 '14 at 19:17
  • Jens don't remove that answer. It is the only interesting answer given. The `rvalue` thing is a big deal, but that can only be appreciated with experience. – Patrick Schlüter Dec 27 '14 at 19:20
28

The printf function is a variadic function, meaning that it has variable number of arguments. Arguments are pushed on the stack before the function (printf) is called. In order for the function printf to use the stack, it needs to know information about what is in the stack, the format string is used for that purpose.

e.g.

printf( "%c", ch );    tells the function the argument 'ch' 
                       is to be interpreted as a character and sizeof(char)

whereas

printf( "%s", s );   tells the function the argument 's' is a pointer 
                     to a null terminated string sizeof(char*)

it is not possible inside the printf function to otherwise determine stack contents e.g. distinguishing between 'ch' and 's' because in C there is no type checking during runtime.

Nathaniel Waisbrot
  • 23,261
  • 7
  • 71
  • 99
AndersK
  • 35,813
  • 6
  • 60
  • 86
  • 9
    but you are completely wrong with your `sizeof` specifications. First, `ch` is promoted to an `int` (or on some platforms to an `unsigned`) and `printf` interprets that `int` as a character. So the size of that parameter "on the stack" is `sizeof(int)`. For the second you are wrong again, it is `sizeof(char*)` and nothing else. `sizeof(int)` can (and nowadays often is) different from that. Third, there is not necessary something like a "stack" involved. How these parameters are passed is to the discretion of the platform ABI , and everything may well happen in registers. – Jens Gustedt Jun 01 '12 at 08:21
  • 2
    @AndersK: "technically" Jens is entirely right. In fact, you might as well drop the word "technically"; he's just plain right. – Keith Thompson Jun 03 '12 at 05:24
  • 1
    "A little inaccuracy sometimes saves a ton of explanation" -- Hector Hugh Munro – AndersK Jun 07 '12 at 05:43
14

%s says print all the characters until you find a null (treat the variable as a pointer).

%c says print just one character (treat the variable as a character code)

Using %s for a character doesn't work because the character is going to be treated like a pointer, then it's going to try to print all the characters following that place in memory until it finds a null

Stealing from the other answers to explain it in a different way.

If you wanted to print a character using %s, you could use the following to properly pass it an address of a char and to keep it from writing garbage on the screen until finding a null.

char c = 'c';
printf('%.1s', &c);
Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • 1
    This is not entirely true. using %s for a character can do completely unexpected things since it interprets a character as a pointer to a character, which likely points to an invalid location. – Matt Jun 01 '12 at 07:22
  • Just to be clear (since a lot of people confuse these concepts), a string is terminated by a null *character*, not to be confused with a null *pointer*. – Keith Thompson Jun 03 '12 at 05:21
  • @KeithThompson Just to confuse people: null character `'\0'` and null pointer are both represented internally as `0` :) – Ruan Mendes Jun 07 '12 at 00:20
  • 2
    @JuanMendes: No, they can both be represented *in source code* as `0`. Their internal representations can be quite different; a null character is typically 8 bits, while a null pointer is typically 32 or 64 bits. Furthermore, a null pointer's internal representation is *not* necessarily all-bits-zero (though it very commonly is). – Keith Thompson Jun 07 '12 at 05:05
  • 1
    @Keith The language in question is C, and I've never heard of the internal representation being anything other than a 0. That is why you can use any of the following. `if (pointer)` or `if (pointer != 0 )` or `if(pointer != NULL )` Also, I think 0s using 8, 16, 32 bits aren't very different. http://bytes.com/topic/c/answers/213647-null-c – Ruan Mendes Jun 07 '12 at 18:04
  • @JuanMendes: The [ISO C standard](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) does not say or imply anything about the representation of a null pointer. A *null pointer constant*, such as a constant `0`, exists in source code; when used in a pointer context, the compiler will translate it to whatever internal representation is used for a null pointer. See also section 5 of the [comp.lang.c FAQ](http://c-faq.com). It's true that *most* implementations use all-bits-zero, but it's not required. – Keith Thompson Jun 07 '12 at 21:20
  • @JuanMendes: The link you provided, http://bytes.com/topic/c/answers/213647-null-c, has several answers that are consistent with what I just said, that a null pointer isn't necessarily all-bits-zero (including two that I wrote myself). The content appears to have been copied without attribution from the comp.lang.c Usenet newsgroup. – Keith Thompson Jun 07 '12 at 21:22
7

For %s, we need provide the address of string, not its value.

For %c, we provide the value of characters.

If we used the %s instead of %c, how would we provide a '\0' after the characters?

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
RolandXu
  • 3,566
  • 2
  • 17
  • 23
4

Id like to add another point of perspective to this fun question.

Really this comes down to data typing. I have seen answers on here that state that you could provide a pointer to the char, and provide a

"%.1s"

This could indeed be true. But the answer lies in the C designer's trying to provide flexibility to the programmer, and indeed a (albeit small) way of decreasing footprint of your application.

Sometimes a programmer might like to run a series of if-else statements or a switch-case, where the need is to simply output a character based upon the state. For this, hard coding the the characters could indeed take less actual space in memory as the single characters are 8 bits versus the pointer which is 32 or 64 bits (for 64 bit computers). A pointer will take up more space in memory.

If you would like to decrease the size through using actual chars versus pointers to chars, then there are two ways one could think to do this within printf types of operators. One would be to key off of the .1s, but how is the routine supposed to know for certain that you are truly providing a char type versus a pointer to a char or pointer to a string (array of chars)? This is why they went with the "%c", as it is different.

Fun Question :-)

trumpetlicks
  • 7,033
  • 2
  • 19
  • 33
2

C has the %c and %s format specifiers because they handle different types.

A char and a string are about as different as night and 1.

Philip
  • 5,795
  • 3
  • 33
  • 68
  • As you mention %s and %c are totally different then pls tell me why the `scanf(%s, &ch)` and `scanf(%c, &ch)` statements returns same result.Consider ch is a char like `char ch`; – Prince Vijay Pratap Feb 22 '16 at 16:32
2

%c expects a char, which is an integer value and prints it according to encoding rules.

%s expects a pointer to a location of memory that contains char values, and prints the characters in that location according to encoding rules until it finds a 0 (null) character.

So you see, under the hood, the two cases while they look alike they have not much in common, as one works with values and the other with pointers. One is instructions for interpreting a specific integer value as an ascii char, and the other is iterating the contents of a memory location char by char and interpreting them until a zero value is encountered.

Gerasimos R
  • 2,056
  • 1
  • 12
  • 22
2

I have done a experiment with printf("%.1s", &c) and printf("%c", c). I used the code below to test, and the bash's time utility the get the runing time.

    #include<stdio.h>
    int main(){
        char c = 'a';
        int i;
        for(i = 0; i < 40000000; i++){
            //printf("%.1s", &c); get a result of 4.3s
            //printf("%c", c); get a result of 0.67s
        }
        return 0;
    }

The result says that using %c is 10 times faster than %.1s. So, althought %s can do the job of %c, %c is still needed for performance.

Ray
  • 1,647
  • 13
  • 16
1

Since no one has provided an answer with ANY reference whatsoever, here is a printf specification from pubs.opengroup.com which is similar to the format definition from IBM

%c

The int argument shall be converted to an unsigned char, and the resulting byte shall be written.

%s

The argument shall be a pointer to an array of char. Bytes from the array shall be written up to (but not including) any terminating null byte. If the precision is specified, no more than that many bytes shall be written. If the precision is not specified or is greater than the size of the array, the application shall ensure that the array contains a null byte.