Help with pointers

Hi everyone,

I am trying to understand an Arduino sketch in order that I might tweak it a bit. I am having trouble understanding some of the code. The code involves pointers and I took the original code and boiled it down to a very basic sketch in order to show my confusion.

Below is the code:

char* foo[] = {"T1", "Test2", };
char* foo2[] = {"Sample1", "Sample2", "S3" };


void setup() {
 Serial.begin(9600);
 
    
    

    Serial.print("sizeof(foo} = ");
    Serial.println(sizeof(foo));
    Serial.print("sizeof(foo2} = ");
    Serial.println(sizeof(foo2));
      
    Serial.print("*foo = ");
    Serial.println(*foo);      
    Serial.print("sizeof(*foo} = ");
    Serial.println(sizeof(*foo));

    Serial.print("*foo2 = ");
    Serial.println(*foo2);     
    Serial.print("sizeof(*foo2} = ");
    Serial.println(sizeof(*foo2));

 
}

void loop() {
  

}

The code creates 2 ponters, foo and foo2:
If I run the code I get the following results:

sizeof(foo) = 4
sizeof(foo2) = 6

*foo = T1
sizeof(*foo) = 2

*foo2 = Sample1
sizeof(*foo2} = 2

The original code was using (as an example):
sizeof(foo)/sizeof(*foo)
to get the number of items in the arrays.

What I am interested in knowing is what these returned numbers are?

sizeof(foo) is returning what? The size of the pointer?
Why does *foo = T1? (I would have thought *foo(0) would = “T1”)

And why do both sizeof(*foo) and sizeof(*foo2) both equal the same number (2)?

Any help would be appreciated.

Thanks

Sam

sizeof(foo) is returning what?

The number of bytes in the array.

Why does *foo = T1?

foo is a pointer. It lives at some location in memory. What is stored in that memory location is not the data. It is the address where the data lives. *foo is, then, the data at the address stored in foo. When you print *foo, you print the data at the address that foo points to.

And why do both sizeof(*foo) and sizeof(*foo2) both equal the same number (2)?

Both pointers are the same size. Think of it like this. You have a package of 3 x 5 cards. On each one, you write the name and address of a friend. No matter how far away the friend lives, or how big the friend's house is, the card that you wrote the address on is the same size.

Any given card is a pointer. It has a location in the deck, but that address is rarely important. The data on the card (what the card points to) (the friend's name and address) is.

Thanks for the reply, but I must be a little (or a lot) dense because I still do not get it.

The number of bytes in the array.

What, exactly has 2 bytes in foo but 6 bytes in foo2?

Can you give me an example?

To me foo has 7 bytes, 'T1' and 'Test2'

foo is a pointer. It lives at some location in memory. What is stored in that memory location is not the data. It is the address where the data lives. *foo is, then, the data at the address stored in foo. When you print *foo, you print the data at the address that foo points to.

If I am printing the data at the address that foo points to, would I not be printing 'T1Test2'?

Both pointers are the same size.

Is there any situation where the pointers would not be the same size? And what do we mean by 'The same size'. For example, if my pointer points to address 52678 and another pointer points to address 56743, how do my returned numbers relate to these addresses?

Thanks again.

Sam

This actually is kind of tricky, because it mixes up * and with the same variable. In functions they are pretty much interchangable.

I'll make up addresses, but your code is producing variables that look like this in memory:

Address 0x110 - 0x112 = "T1"
Address 0x113 - 0x118 = "Test2"

Address 0x120 - 0x128 = "Sample1"
Address 0x129 - 0x130 = "Sample2"
Address 0x131 - 0x133 = "S3"

foo @ Address 0x0 - 0x3 = {0x110, 0x113}
foo2 @ Address 0x4 - 0x9 = {0x120, 0x129, 0x131}

So the sizeof(foo) and sizeof(foo2) are pretty clear, as they're the size of the total array.

The sizeof(*foo) would be clearer if it was written sizeof(foo[0]). You can see from the made up address map that sizeof(foo[0]) is simply 2.

When an array is referred to without , it is just an address. If you want to reference elements of that array, you can do it like array[index] or *(array + index). They are functionally equivalent.

sonofbc:
Thanks for the reply, but I must be a little (or a lot) dense because I still do not get it.

What, exactly has 2 bytes in foo but 6 bytes in foo2?

Can you give me an example?

To me foo has 7 bytes, 'T1' and 'Test2'

Hopefully my example helped you understand.

In C you can't have an array with different length elements in it. In C the arrays have to be a number of the same length item, in this case arrays of pointers to other memory.

The compiler is actually doing a lot of work for you when you define and initialize foo. First, it's making arrays in memory to store your strings. Since memory requires 2 bytes to address it in the AVR, arrays of 2 byte addresses are what is actually being stored in foo.

The thing is, if you said sizeof(**foo) you still wouldn't get the sizeof("T1")...you'd get sizeof(char) because that's all *foo points to.

To avoid all this confusion with people who get my code, I just explicitly define this stuff:

char T1_string = "T1";
char Test2_string = "Test2";
char *foo = {T1_string, Test2_string};

Not only is that clear to the newest programmer, but I can now reuse strings if I want to save memory (something the Arduino compiler doesn't seem to do on its own). Also, if these strings never change, you really want to put them in program memory.

Thanks Bob,

I will try to see if I have this straight:

So, when I return size0f(foo), I am actually returning the number of the 2 byte pointers held with in the foo pointer array?

So, if each element address needs 2 bytes and my foo array has 2 distinct element addresses we get (for foo) 2 x 2 = 4 and for sizeof(foo2) we get 2 x 3 (3 elements) = 6.
Am I on the right track?

If possible, can you show me how I would return the address of each element in foo? and also how I would reference the actual data in foo.

So, I would have thought I could have used the dereference operator * like this:

sizeof(*foo(0))

but I get this error:

'foo' cannot be used as a function

The reason I asked, is in the original sketch, the author does not expliciy define the strings as you did in the last example but uses only (as an example):

char* foo = {"T1", "Test2", };

Thanks for all your time and help.

Sorry for being so slow to understand.

Sam

sonofbc:
The code creates 2 ponters, foo and foo2:

No, it does not. It creates two arrays of pointers. The size of a pointer in this case is 2. So you have 2 times 2 or 3 times 2 bytes.

sonofbc:
So, I would have thought I could have used the dereference operator * like this:

sizeof(*foo(0))

but I get this error:

'foo' cannot be used as a function

because to dereference a pointer to an array element, you have to use the square brackets!

sizeof(*foo[0])

Thanks to all for your help. I think I now have just enough knowledge to go out and make myself really dangerous to code everywhere!

:slight_smile:

Just remember nearly all languages allow you to shoot yourself in the foot, but C (and C pointers in particular) hands you the gun, round in the chamber, safety-catch off, and a hair-trigger.

If you’re lucky, it’s a .177, but it’s just as likely to be a howitzer.

sonofbc:
If possible, can you show me how I would return the address of each element in foo? and also how I would reference the actual data in foo.

You seem to be on the right track, and I see other people have already helped you with the vs (). I program in several languages each day, and those kinds of syntax differences get frustrating.

Here is the code that you would need to get the address and data in each element.

  for(int str_num = 0; str_num < sizeof(foo)/sizeof(foo[0]); str_num++)
  {
    Serial.print("foo[");
    Serial.print(str_num);
    Serial.print("] = ");
    Serial.print(foo[str_num]);
    Serial.print(" @ address ");
    Serial.println((int)foo[str_num], HEX);
  }

This results in the following output on my screen:

foo[0] = T1 @ address 27F
foo[1] = Test2 @ address 282

Your addresses will be different, but you’ll notice there is no difference in the reference other than how Serial.print() interprets it. You’re passing the same number either way, just the first time it calls a function that prints out all of the characters as ASCII starting at the address until it encounters a 0. In the second call you’re telling it to interpret the address as a 16bit number that it should print out in HEX.

BTW - Great job with your post. This place yells enough at the people who don’t read the stuck posts before posting…it’s nice to praise someone that made a simple example and posted it in code tags.