the puts statement shows 'this' on the screen.
That's just (bad) luck, the code invokes undefined behavior, if the compiler doesn't refuse to compile it at all.
The warnings produced by clang (by default) for the code are
$ clang badpoint.c
badpoint.c:6:18: warning: incompatible pointer types initializing 'char **' with an
expression of type 'char [5]' [-Wincompatible-pointer-types]
char **bs = {"this", "is", "a", "test"};
^~~~~~
badpoint.c:6:26: warning: excess elements in scalar initializer
char **bs = {"this", "is", "a", "test"};
^~~~
badpoint.c:8:10: warning: incompatible pointer types passing 'char **' to parameter of
type 'const char *'dereference with * [-Wincompatible-pointer-types]
puts(bs);
^~
*
/usr/include/stdio.h:688:32: note: passing argument to parameter '__s' here
extern int puts (__const char *__s);
^
3 warnings generated.
gcc generates a warning for each excess initializer, while clang gives only one for the first of the three, otherwise the warnings from gcc (also by default) are a bit less informative, but the same.
So what's going on?
You are trying to initialize a scalar (char**
), and provide {"this", "is", "a", "test"}
, a brace-enclosed initializer-list containing four _initializer_s. Per 6.7.9 (11)
The initializer for a scalar shall be a single expression, optionally enclosed in braces. The initial value of the object is that of the expression (after conversion); the same type constraints and conversions as for simple assignment apply, taking the type of the scalar to be the unqualified version of its declared type.
That violates a "shall" requirement, and hence invokes undefined behavior.
If the compiler does not terminate the translation there, it probably just ignores the excess initializers - clang and gcc both do - and treats the declaration as if it was
char **bs = "this";
Which causes the warning about initialization from an incompatible type, since that tries to initialize a char**
with a char[5]
, but per 6.5.16.1 the type of the initializer must be compatible with char**
.
Nevertheless, if the compiler successfully translates the program, it is likely to result in bs
containing the address of the first char
in the string literal (the array "this"
is converted to a pointer to its first element on the right hand side of an assignment operator).
Then the call
puts(bs)
Passes a pointer of the wrong type (char**
) to puts
, which is a constraint violation and requires a diagnostic message from the compiler (and makes the program invalid).
But the pointer happens to contain the correct address, and the undefined behavior manifests as the program printing "this"
.
How would I go about getting it to show 'is' and so forth
By correcting the program. As is, the strings "is"
, "a"
, and "test"
do not even appear in the object file produced by gcc or clang.
One way to correct it would be changing the declaration to
char *bs[] = {"this", "is", "a", "test"};
So that bs
is an array of four char*
, pointing to the (respective first elements of the) four strings.
Then you have to adjust the call to puts
, dereferencing or subscripting bs
,
puts(bs[0]);
To print "this"
;
for(int i = 0; i < 4; ++i) {
puts(bs[i]);
}
To print all four strings on separate lines.