Question

Isn't char* the only valid argument type for the %s format specifier used in the format specifier string of scanf()?If so,in my program why each one of these work exactly the same for both scanf()'s and printf()'s %s format specifier:

scanf("%s",&list[i]);
scanf("%s",list[i]);   
scanf("%s",&list[i][0]);

I'll appreciate if you clear the following confusions that arise from this premise:

1) Why is &name[i] working given it is of type char (*)[].Isn't &name[i][0] is the only valid argument as it is of type char* ?

2) Does name[i] decompose/translate into char* when we pass it as an argument for %s?Is it same as passing &name[i][0],which is the address of the first character of each name we enter?Is it why it works?

#include<stdio.h>
#include<string.h>

int main(void)
{

char list[3][10];
int i;
printf("Enter the three names \n");

for(i=0;i<=2;i++)
scanf("%s",&list[i]);  //Why this works?
//scanf("%s",list[i]);   //Does it decompose into char* type?
//scanf("%s",&list[i][0]);

for(i=0;i<=2;i++)
printf("%s\n",list+i);  //All of these printf() work as well
//printf("%s\n",list[i]);
//printf("%s\n",&list[i][0]);

}
Was it helpful?

Solution

list[i] is a char[10], and undergoes array-to-pointer conversion when passed as an argument, so in that place, it is completely equivalent to &list[i][0].

&list[i] is a char(*)[10] and as such an invalid argument for the %s conversion specifier in printf or scanf, invoking undefined behaviour.

However, since the first byte in list[i] has the same address as the array list[i], usually it works(1), since printf and scanf don't know about the types of their arguments and interpret them according to the format string.

(1) It will fail if the representation of a char* is different from that of a char(*)[10]. That would be uncommon, but is possible.

OTHER TIPS

Variable argument lists -- like used for printf() or scanf() -- cannot check their variable arguments for type-correctness. To the contrary, these functions rely on the format string stating the correct type, because the format string defines the number and type of arguments pulled from the stack.

(Check the documentation for <stdargs.h> for details on how variable argument lists work.)

If you state "%s" as format specifier, scanf() will pull a char * from the stack and write the standard input to that address, no matter what you actually gave as a parameter, or whether you gave a parameter at all.

Needless to say, if your format specifiers don't match your arguments in number, type and order, you might be lucky and actually get a result, but you're definitely invoking undefined behaviour.

(This is talking about the general "why doesn't this give me an error" question, assuming that you don't have a modern compiler and the appropriate warning enabled, like -Wformat for GCC. As for the type conversions in your specific case, see Daniel's answer.)


PS: While we are at it, using scanf() in combination with %s is pretty lethal in any case, because a too-long input would clobber your program. At least limit the input, e.g. via %10s. Better yet, read the input via fgets() and do proper input parsing in memory, since scanf()'s abilities to recover gracefully from malformed input are severely limited.

As long as the argument provides the correct address, it will "do the right thing". However, if you make the example more complex, or use them for arguments to functions that take specific arguments, then you will find out that some of them are not "correct".

The argument to scanf when using a %s format should be the address of a char array, with enough space to store the string being read. In your example &list[i] does not actually provide that, it is the address of a pointer to an array of char. It just so happens that that address is the same as the address to the char array representing the array. &list[i][0] is a pointer to a single character, but it is followed by another 9, so it "works", but it's again technically not correct. list[i] is the only absolutely correct variant. But all alternatives have the same address, so will technically work in this case.

When it comes to printf, again we want the address of a char array.

In this case, both list+i and list[i] mean exactly the same thing - when an array is used in this way, list makes it a pointer to the first element, and +n will give you the nth element, just like the bracket notation will. &list[i][0] is giving the address of a single char, but it's the same address as the first element of the whole array, so will produce the same result.

By the way, I don't think gcc, which does have an understanding of the format string and CAN check and produce warnings for mixing up the format string and the corresponding arguments, will complain about most of those forms. But not all forms are technically correct.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top