views:

1479

answers:

4

Hello,

We have the following code fragment:

char tab[2][3] = {'1', '2', '\0', '3', '4', '\0'};
printf("%s\n", tab);

And I don't understand why we don't get an error / warning in the call to printf. I DO get a warning but not an error, and the program runs fine. It prints '12'.
printf is expecting an argument of type char *, i.e. a pointer to char. So if I declared char arr[3], then arr is an address of a memory unit which contains a char, so if I called printf with it, it would decay to pointer to char, i.e. char *.
Analogously, tab is an address of a memory unit that contains the type array of 3 char's which is in turn, an address of memory unit contains char, so tab will decay to char **, and it should be a problem, since printf is expecting a char *.

Can someone explain this issue?

Addendum:

The warning I get is:
a.c:6: warning: char format, different type arg (arg 2)

+1  A: 

You seem to have explained it yourself, I don't see what's left to say.

tab is an array of two char *'s. Each element of tab is a string that printf can accept, but tab itself is not acceptable, since it is a pointer to a pointer to a char.

sykora
But it works! and it prints `12'.
Leif Ericson
tab and tab[0] point to the same location (the "12" string), so they both work, despite the mismatch.
Carson Myers
Your problem seems to be that the compiler accepts the code. See Neil Butterworth's answer for why this is so. Frankly, I'm surprised the compiler is aware of the semantics of printf() and is doing any kind of type checking on its arguments.
Ari
+4  A: 

Example Source

#include <stdio.h>

int main( void ) {
  char tab[2][3] = {'1', '2', '\0', '3', '4', '\0'};
  printf("%s\n", tab);

  return 0;
}

Compile Warning

$ gcc test.c
test.c: In function ‘main’:
test.c:5: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘char (*)[3]’

Pointers are Pointers

The %s argument to printf indicates to the function that it will be receiving a pointer (to a string). A string, in C, is merely a series of bytes terminated by an ASCII-Z. The tab[2][3] variable is a pointer. Some compilers will issue a warning about the pointer mismatch. However, the code should still print out 12 because printf's code traverses memory starting at the pointer it was given (printing characters as it goes) until it finds a zero byte. The 1, 2, and \0 are contiguously set in memory, starting at the address represented by the tab variable.

Experiment

As an experiment, what happens when you compile and run the following code:

#include <stdio.h>

int main( void ) {
  char tab[2][3] = {'1', '2', '\0', '3', '4', '\0'};
  printf("%s\n", tab[1]);

  return 0;
}

Don't be afraid of experimenting. See if you can come up with the answer based on what you now know. How would you reference tab now (in light of the experiment) to get rid of the warning and still display 12?

Dave Jarvis
This is easy. It is the right way of calling printf. tab[1] is of type char[], and it is the same as calling printf with char *. So everything is optimal.
Leif Ericson
Exactly. So to display "12", you would use tab[0].
Dave Jarvis
+3  A: 

The tab parameter matches the elipsis in the printf() call. C and C++ compilers are undfer no obligation to check such parameters.

anon
+2  A: 

Your assumption that tab will decay to char ** is wrong: tab has type char [2][3], ie it will decay to char (*) [3]. It's important to understand that although arrays and pointers often behave alike, they are not the same thing. printf() expects a char *, so it takes the bits of the char (*) [3] and interprets them accordingly. Although it works on your platform, the C standard doesn't guarantee this: both pointers reference the same memory location, but their representation need not be identical.

Check my answer to this related question for details.

Christoph
+1, I knew it was already answered somewhere, but I didn't find the question. Only thing similar I found is this: http://stackoverflow.com/questions/232303/so-you-think-you-know-pointers
quinmars