-8

If I have a pointer to a pointer like the variable 'bs' in this sample:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    char **bs = {"this", "is", "a", "test"};

    puts(bs);

    return 0;
}

the puts statement shows 'this' on the screen. How would I go about getting it to show 'is' and so forth. I though I could just do something like:

puts(bs+1);

What does adding a number to a pointer variable actually do? Does it point to the next address, or does it take the type of the pointer into consideration and uses the size of the variable it is pointing to to determine where the next variable starts in memory?

Matt Vaughan
  • 1,135
  • 2
  • 13
  • 19
  • 7
    This has been answered a thousand times here, and in numerous tutorials and texts. Please do some research ... including just *trying it*. – Jim Balter Apr 09 '13 at 08:48
  • it point to the next address....next adress is calculate as....baseaddress+ sizeofpointertype(1 for char,2 for int and so on..) – Amit Singh Apr 09 '13 at 08:49
  • OTOH, I would ignore all the stuff about address calculation and sizeof. C,has this invariant: `x[y] == *(x+y)`. Therefore, `x+y == &*(x+y) == &x[y]`. – Jim Balter Apr 09 '13 at 08:52
  • @JimBalter That thread answers maybe one of the four questions I had. Even then, not really... – Matt Vaughan Apr 09 '13 at 09:27
  • 1
    @MattVaughan This time you search for half an hour (unlikely that long), but then you learn how to search better, so next question you have takes a little less time, the one after even less, etc. Eventually it takes less time to find an already-posted solution online than it does to actually type up your question. Also note that no-one wants to answer (or read through for that matter) the same question (with possibly slight variations) over and over again (and then there's the already-mentioned cluttering of the site). – Bernhard Barker Apr 09 '13 at 10:38
  • 1
    If you look at the 5th answer in that thread, it links to http://stackoverflow.com/questions/394767/pointer-arithmetic with more answers. Some of those answers in turn link to other threads with more answers. Also, please note that SO is not a *forum*, it's a **repository**. This distinction is important when it comes to people asking questions just to save themselves time in doing research. – Jim Balter Apr 09 '13 at 21:07

1 Answers1

3

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.

Evaa
  • 283
  • 1
  • 2
  • 15
Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431