2

I am working through a textbook teaching myself C programming using the Code::Blocks v20.03 IDE.

I am really bamboozled by a small program that is to read in three small strings into a 2d char array and then just print them out to the screen. The code for the program is shown below.

#include <stdio.h>

int main(void)
{
   char *colors[3][10] = {'\0'};

   printf("\nEnter 3 colors seperated by spaces: ");
   scanf("%s %s %s", colors[0][0], colors[1][0], colors[2][0]);

   printf("\nYour entered: ");
   printf("%s %s %s\n", colors[0][0], colors[1][0], colors[2][0]);
   return 0;
}

This compiles with zero errors or warnings and when executed produces the following output.

You entered: (null) (null) (null)

Using the IDE's Watches window shows that nothing is written to the array. I understand that an array name is treated as a pointer to the first element in the array. I also understand the use of subscripts/indexes to access elements within the dimensions of the array (e.g. arrays of ints) and so the need to keep the array second dimension at zero in this char array.

Sadly, this has me completely foxxed and so I need help to fill in my gap in understanding.

Best regards,

Stuart

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Stuart
  • 121
  • 7
  • 8
    E.g. `colors[0][0]` is a pointer. But it's a *null* pointer. It can't be used for `scanf` (or `printf`). I suspect you really want `char colors[3][10];` (note that there's no `*`). And then pass only `colors[0]` (etc) to both `scanf` and `printf`. – Some programmer dude Aug 05 '22 at 12:17
  • 2
    Always check the return value of scanf. It can indicate that your input is wrong. – pts Aug 05 '22 at 12:24
  • 2
    *Never* use `"%s"`. Always restrict it with a width specifier. eg, if `b` points to an array of size N, use a modifier of size N - 1. eg `char b[32]; scanf("%31s", b);`. Rather than hard coding the width, you may wish to construct the format string with `sprintf` or similar. – William Pursell Aug 05 '22 at 13:39
  • Thank you all for replying so quickly. Your explanations are very helpful. – Stuart Aug 06 '22 at 09:41

3 Answers3

5

When using scanf with the %s specifier, you must provide a pointer to a memory address for scanf to write to. Therefore, you must first create a memory buffer which has sufficient space for storing the entire string. The simplest way of doing this would be to declare an array of type char.

However, the line

char *colors[3][10] = {'\0'};

will not declare an array of type char. Instead, it will create a multidimensional array of pointers.

If you want to create an array of type char, you can do this:

char color[10];

If you want to create an array of arrays of type char, you can do this:

char colors[3][10];

Now you can use the line

scanf( "%s %s %s", colors[0], colors[1], colors[2] );

which is equivalent to

scanf( "%s %s %s", &colors[0][0], &colors[1][0], &colors[2][0] );

due to array to pointer decay. In most situations, if you use the name of an array in an expression, it will decay to a pointer to the first element of the array.

Note that using %s is dangerous, because if the user types more data than fits in the memory buffer (in this case more than 9 characters), then you will have a buffer overflow, which causes undefined behavior.

Therefore, it would be safer to write it like this:

scanf( "%9s %9s %9s", colors[0], colors[1], colors[2] );

This limits the number of matched characters to 9 characters per string, so scanf will never write more than 10 characters (9 characters plus the terminating null character) into a memory buffer.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • Thank you for such a clear and illuminating reply. This has definitely been a great help. Thank you also for providing the links. Again, very useful/helpful. – Stuart Aug 06 '22 at 09:59
4

You have a two-dimensional array of null pointers

char *colors[3][10] = {'\0'};

So The call of scanf

printf("\nEnter 3 colors seperated by spaces: ");
scanf("%s %s %s", colors[0][0], colors[1][0], colors[2][0]);

invokes undefined behavior.

It seems you mean the following

char colors[3][10] = {'\0'};

printf("\nEnter 3 colors seperated by spaces: ");
scanf(" %9s %9s %9s", colors[0], colors[1], colors[2]);

printf("\nYour entered: ");
printf("%s %s %s\n", colors[0], colors[1], colors[2]);
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Thank you for a very swift reply. Your explanation has helped clarify my understanding. I implemented all the changes you presented and the little program now works as it should. – Stuart Aug 06 '22 at 09:39
1

You are not derefencing the array element you want. colors[0][0] returns the value in that element of the array, not a pointer to it. Try &colors[0][0] instead.

  • 1
    Instead of `&colors[0][0]`, you can simply write `colors[0]`, which is equivalent. The expression `colors[0]` will [decay](https://stackoverflow.com/questions/1461432/what-is-array-to-pointer-decay) to `&colors[0][0]`. – Andreas Wenzel Aug 05 '22 at 16:05
  • 1
    Thank you for your prompt reply. The link in particular is very helpful. – Stuart Aug 06 '22 at 09:34