Why do 1, 2, and 3 work when 4 generates a segmentation fault? (See below.)
char c[10];
char* d;
1.
scanf("%s", &c);
printf("%s\n", &c);
2.
scanf("%s", c);
printf("%s\n", c);
3.
scanf("%s", &d);
printf("%s\n", &d);
4.
scanf("%s", d);
printf("%s\n", d);
Repeating the code in the question:
1.
This is likely to work as expected, but in fact the behavior is undefined.
scanfwith a"%s"format requires an argument of typechar*.&cis of typechar (*)[10], i.e., it’s a pointer to achar[10]array. It points to the same location in memory as the address of the 0th element ofc, but it’s of a different type. The same thing happens with theprintf: the"%s"format tells it to expect achar*argument, but you’re passing it achar(*)[10]argument.Since
scanfis a variadic function, there’s no required type checking for arguments other than the format string. The compiler will (probably) happily pass thechar (*)[10]value toscanf, assuming that it can handle it. And it probably can, on an implementation where all pointers have the same size, representation, and argument-passing mechanism. But, for example, a C compiler for an exotic architecture could easily makechar*pointers bigger than pointers to larger types. Imagine a CPU whose native address points to, say, a 64-bit word; achar*pointer might be composed of a word pointer plus a byte offset.2.
This is better.
cis an array, but in this context an array expression “decays” to a pointer to the array’s first element — which is exactly whatscanfwith a"%s"format requires. The same thing happens passingctoprintf. (But there are still some problems; I’ll get to that after the other examples.3.
Since
dis a singlechar*argument,&dis of typechar**, and again, you’re passing arguments of the wrong type. If all pointers have the same representation (and the same argument-passing mechanism), and the input for thescanfis short enough, this might happen to “work”. It treats thechar*object as if it were an array ofchar. Ifchar*is 4 bytes, and the input string is no more than 3 characters long, this will probably work — as if you had used achar[4]and written the calls correctly. But it’s extremely poor practice to store character strings directly into a pointer object, and there’s a huge risk of writing past the end of the object, with unpredictable results. (Those unpredictable results include writing into memory that isn’t being used for anything else, which could appear to work; such is the nature of undefined behavior.)(The C standard gives special permission to treat any object as an array of characters, but in this case it’s a very bad idea.)
4.
Here the types are all correct, but unless you’ve initialized
dto point to a sufficiently large array ofchar, it’s likely to fail spectacularly (or, worse, appear to work “correctly”, which means you’ve got a subtle bug that will probably show up later).And now we get to what I mentioned above about other problems.
For example 4, I mentioned that
dneeds to point to a “sufficiently large” array. How large is “sufficiently large”? There’s no answer to that.scanf("%s", ...)reads a whitespace-delimited sequence of characters with no upper bound on its length. If I run your program and hold down thexkey, for example, I can provide an input string longer than any buffer you’ve provided, with unpredictable results (undefined behavior again).The
scanffunction’s"%s"format cannot be used safely (unless your program runs in an environment where you can control what will appear on the standard input stream).One good way to read text input is to use
fgetsto read a line at a time, then use other functions to analyze the result.fgetsrequires you to specify the maximum length of the input; if the actual input exceeds the limit, it’s truncated and left to be read by later calls. It’s not quite as convenient asscanf, but it can be done safely. (And never use thegetsfunction; likescanf("%s", ...), it cannot be used safely.)Suggested reading:
Section 6 of the comp.lang.c FAQ does an excellent job of explaining C arrays and pointers, and how they’re related (and not related). Section 12 discusses C standard I/O.
(I’m sorry this answer is so long; I didn’t have time to make it shorter.)