For type system reasons, and for compatibility with B.
B is a programming language that served as the immediate ancestor of C. The salient thing about B is that it had no type system: all values in B are machine words (corresponding to the C type int
). In B, there were two ways to represent strings in source code: string literals0, which evaluated to a pointer to a block of memory holding the string, and multi-character literals, which packed multiple character codes directly into a single machine word. The latter were famously used in Kernighan’s original “Hello, world” program:
main( ) {
extrn a, b, c;
putchar(a); putchar(b); putchar(c); putchar('!*n');
}
a 'hell';
b 'o, w';
c 'orld';
Since the two kinds of values behaved so differently, yet could not be distinguished at the type system level (because there was none), they had to use different syntaxes.
As C is an evolution of B, it simply inherited all this baggage and could not change it without breaking compatibility. Although some breaking changes to the syntax were made in C after all, there apparently wasn’t a compelling enough reason to make one here. The weak typing of C does maintain a certain kind of continuity with B after all.
The above, though, raises the question of why such distinction was made in B. Since B was conceived as a simplified version of BCPL, one may think there might be some answers in materials about that language. But according to the manual, in BCPL character literals and string literals were not differentiated by delimiters, but by their length:
A string constant of length one has an Rvalue which is the bit pattern representation of the character; this is right justified and filled with zeros.
A string constant with length other than one is represented as a BCPL vector [i.e. array]; the length and the string characters are packed in successive words of the vector.
So the delimiter distinction between character literals and string literals was first made in B. As to why, and why the syntax was chosen the way it was, we are probably resigned to rely on speculation, as neither Users' Reference to B nor A Tutorial Introduction to the Language B nor The Development of the C Language elaborate on that particular topic. My hypothesis would be:
- Because B allowed multiple characters in its character literals, it could no longer rely on differentiating characters from strings by the length of the literal (and because again, B had no type system to transparently inter-convert between them), and thus a syntactic distinction was necessary.
- Character literals, as conceptually more lightweight (not requiring additional storage), were assigned the glyph that was (visually) simpler and took fewer keystrokes to type. (I shamelessly stole this one from @Toby Speight.) This may have been influenced by DEC assemblers, as suggested in @dave’s answer.
This explanation is mostly conjecture, but it seems we may have a hard time finding a better one.
0 Contemporaneous documentation used the term “constant” instead of “literal”, since it was the only kind available back then anyway. In fact, the C standard uses the term “constant” for literals to this day, and only very recently a paper was put forward to change the terminology.