简体   繁体   中英

How is it possible to input a string into an array of pointers to character?

My problem is how is it possible to input a string into an array of character pointers? Is memory being dynamically allocated here or something? What is actually getting stored in the array ' name '?

char *name[20];
printf("Enter a string:");
scanf("%s",name);
printf("%s",name);

This code works fine. It prints the string I enter. How is it possible to treat char *name[20] just like char name[20] ?

Is memory being dynamically allocated here or something?

No.

This code works fine. It prints the string I enter. How is it possible to treat char *name[20] just like char name[20]?

The behaviour of the program is undefined.

It is undefined, because it violates the requirements of the scanf and printf functions.

The C standard says of %s specifier for both scanf and printf (quoting the standard draft document N1570):

the corresponding argument shall be a pointer to the initial element of a character array ...

name is not a pointer to the initial element of a character array. It is (actually an array, but) after decaying, a pointer to an initial pointer to character in an array of pointers to characters. Thus the requirements are violated and behaviour of the program is undefined.

what did you mean by "behaviour is undefined"?

It means that nothing about the behaviour of the program is guaranteed. As far as the language is concerned, the program might:

  • Produce output that you expect.
  • Produce output that you didn't expect.
  • Produce output that you want to be produced.
  • Produce some output that you didn't want.
  • Not produce output at all.
  • Crash
  • Not crash
  • Behave differently on another system.
  • Behave differently on the same system.
  • Behave differently when you're debugging it.
  • Behave differently only when you are on vacation.
  • Behave differently for any reason possible.
  • Behave differently for seemingly no reason at all.
  • Behave the same always
  • Behave exactly the same as if you had used char name[20] despite not having used it.
  • Not behave like that.
  • Have any behaviour whatsoever.

Undefined behaviour is to be avoided.

scanf and printf expected an array of characters, but you gave it an array of pointers. However, they can't tell the difference because they are variadic. This causes undefined behavior, so anything is possible.

What likely happened here is that scanf simply wrote the characters into the memory of the pointer array and printf treated the data as characters because neither of the functions know that the chunk of memory you gave it is supposed to store pointers instead of characters. Printing out the second element gave you the fifth character likely because your system has four byte pointers, so the second pointer element starts at the fifth byte and therefore a fifth character.

Again, your code exhibits undefined behavior, so the previous paragraph is only speculation. None of this is guaranteed by the standard and you shouldn't rely on it, ever.

Although eerorika's answer is perfectly correct, from what I feel OP may need some more detailed explanation of what's going on here…

What you declare with your char * name[20] is NOT an array that points to 20 characters . It IS an array of 20 pointers to memory (each pointer is treated as pointing to a character or an array of characters). The [20] in the declaration already specifies that you need 20 elements, and what is preceeding ( char * in your case) specifies what these elemnts are. So the correct declaration(in your context) is simply char name[20] .

Now the compiler sees that you need a block of memory of 20 characters and it reserves the memory for you (on stack, no dynamic allocation here). Then char name[20] is a continuous block of reserved memory that can hold 20 characters.

You get an address to the beginning of this block as &name[0] or simply name . The latter only since a static array variable – which is the entire block of 20 chars in your case – is implicitly convertible (not identical to) a pointer to the beginning of that block of memory, ie char * . (If you feel like you do not understand this last statement, you may consult eg this short article or google around a bit,eg, on difference between char array and char pointer in c .

The following illustrates what happens with the correct code and what with yours and why it is undefined behaviour (UB) and why it (un)luckily works in your case.

With the correct declaration char name[20] you get

char [20] memory block uninitialized

?character means that the content in every cell of the buffer is not yet specified but the compiler treat it as a character.

When you pass this into scanf and a user enters, say, "HelloKitty", this answer will be filled into the char [20] block of memory reserved by the compiller.

char [20] memory block after passing it to scanf

That's what you want to get.

Now here is what you actually get with your char * name[20] declaration. At the beginign the situation is like this

char * [20] memory block uninitialized

?pointer means that the content in every cell of the buffer is not yet specified but the compiler treat it as a pointer to character .

When you pass this into scanf and the user enters theirs "HelloKitty" this answer will be filled into the char * [20] block of memory reserved by the compiller as previously.

Now more attention is needed. This is possible only due to the fact that scanf is not type-safe. It takes whatever adress it gets (the address of name[0] in your case) and fill it in carelessly, according to what its format specifiers specify. So it hapilly fill an array of characters into a block of memory that is supposed to contain an array of poiners. Now, every character has a binary representation, indeed. So what happens under the hood is that the char * [20] memory block will be filled in with "zeros and ones" that represent the string "HelloKitty" (up to the length of that string).

char * [20] memory block after passing it to scanf

?!pointer means that the content is still treated as a pointer to character by the compiller, yet the value of that pointer (the address it technically points to) has been rewritten by calling scanf . (note: even though "HelloKitty" contains 11 characters including the terminating nul, not 11 cells are rewritten since sizeof each cell equals sizeof of a pointer. Typically sizeof of a pointer is 4 times that of char on a 32bit architecture, so only 2 cells and a part of the third one wil be rewritten)

Now why it all works fine for you is just because printf is just as ignorant of its actual types as its sister scanf . So printf takes the address of name[0] and (re)interpret its "zeros and ones" (back) to the "HelloKitty" string.

Practically, your program will not likely (see below) invoke UB, as long as you pass the name variable between printf and scanf (or like functions) only. The (binary) content of the memory block reserved for char * name[20] is somewhat (see below) guaranteed to be eqivalent to the (binary representation of) the "HelloKitty" string. As long as the content read by scanf does not exceed the memory block provided to it, the program does not get likely (see below) corrupted in any way. You may just as well write int i; scanf("%s",&i); printf("%s",&i); int i; scanf("%s",&i); printf("%s",&i); and the program will (likely) work fine as long as the user eneters less than 3 characters as an input. (supposing sizeof int is 4 chars ))

Technically, you code falls in UB. This is just what the standard says. The standard cannot treat every single case of "when the code would actually work if the programmer write something weird". And yes, treating char * [20] as char [20] in scanf is "something weird." Simultaneously, by specifying something UB, the standard gives some freedom to compiler implementers. If you were to write a compiler, you may decide to parse the format in scanf compile-time and generate a special set of instructions that rely on the fact that correct type is provided for name (the fact that I can hardly imagine such scenario - at least for the %s switcher - does not change anything). So the program is UB. Period. :)

ps.> Consider using scanf("%20s", name) in your code. This will prevent scanf from reading characters beyond the length of the memory block, which would result in yet another UB.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM