Why is "&" needed in Spark.variable()?

Hi,
I wonder why does “&” need to write before a variable in Spark.valiable.
For example,

Spark.variable(“analogvalue”, &analogvalue, INT);
Spark.variable(“temp”, &tempC, DOUBLE);
Spark.variable(“mess”, message, STRING);

But why string does not need to write “&” ?

Thanks,
Daisuke

Daisuke, the & prior to a variable name denotes its address. So &analogvalue means the address of the variable analogvalue in memory. The Spark.variable() function needs that address to know where to get the variable value. :smile:

2 Likes

Continuing on from @peekay123 ...

Because a, "string" in C, like in the case of message in the example you gave, is actually already a pointer to an array of chars. In this case, the & is implicit -- it is logically assumed by the compiler that, since message is a label associated with an array, it can only hold a pointer -- the address-of -- that array.

Yes, it's a little confusing until it becomes habit, especially if you're coming at it from higher level languages, like Python, BASIC or PHP, to name but a few.

In other words, the following two declarations are identical, the second being simply a kind of convenient short-hand, which we tend to use for string constants, because it's much easier to type and read ...

char string[] = {'A', ' ', 's', 't', 'r', 'i', 'n', 'g', '\0'};
char *string = "A string";

In each case, the reference string is a pointer to an array of chars, as opposed to a variable holding an actual value, as in the case of an int or even a single char, as in char singleChar = 'A';

The asterix in front of *string means that we are declaring a pointer. What we are pointing to, is an array of chars, created by the shorthand, "A string". Thus, string on its own, without the asterix, is the pointer itself, which holds only the address in memory where the array of chars can be found. For Spark.variable, this is what we want -- to tell the function where to find our string. (Because we may change the contents of the string's array of chars, you see.)

And last but not least, if we were to type &string, we would be saying, "here is the address a the pointer (that points to our array or chars)", which is not what we want.

Hope that makes sense -- and more so that I got it 100% correct!

P.S: You can even write the following, to mean the same as the two examples above. All three version result in identical compiled code, ...

char string[] = "A string";

When you use the "string" syntax, the compiler automatically adds a null terminating character on the end, which we had to do ourselves, when we declared the array manually, above. By convention, all C strings end with a null -- character code zero.

Of trivial interest, perhaps -- In other languages, like Pascal for example, the length of the string is stored in the first array element instead and there is no terminating null. Historically therefore, Pascal strings were limited to 255 characters in length. C strings essentially have no such limit.

3 Likes

Having said all that, I have been considering recommending an additional Spark.variable format, such that we can provide a **string pointer to a string pointer. Something like,

Spark.variable(const char *name, char **stringPointer, STRING);

Why? Because at the moment, the only way to change the contents of the string that Spark.variable accesses is to change the individual chars within a single string array. However, in cases where we have a selection of strings to choose from, perhaps stored in an array, like so …

const char responses[][11] = {
  "response 1",
  "response 2",
  "response 3",
  "response 4"
};

… it would be more efficient to change only a pointer into that array of strings, like so …

char *whichReponse = responses[0];
void loop()
{
  if (digitalRead(BUTTON1) whichResponse = responses[1];
  else if (digitalRead(BUTTON2) whichResponse = responses[2];
  else whichResponse = responses[0];
 ...
}

Am I making sense?

The various response strings would not have to be in an array, of course. We could instead simply …

char *response0 =  "response 0";
char *response1 =  "response 1";
char *response2 =  "response 2";

char *whichReponse = response0;
void loop()
{
  if (digitalRead(BUTTON1) whichResponse = response1;
  else if (digitalRead(BUTTON2) whichResponse = response2;
  else whichResponse = responses0;
 ...
}

In either case, Spark.variable would need to look in the address pointed to by the pointer whose address was given in the Spark.variable set-up call, as opposed to the current case, where that pointer address is a one-time static value, which will always point to the same address.

Otherwise, as is the current case, we have only one way to send different responses via an api variable, thus …

void loop()
{
  if (digitalRead(BUTTON1) strcpy(whichResponse, response1);
  else if (digitalRead(BUTTON2) strcpy(whichResponse, response2);
  else strcpy (whichResponse, responses0);
 ...
}

I am obviously assuming that multiple calls to Spark.variable, to set new pointer addresses for the same variable name, will not work, as I am sure is the case. So if I am right, we cannot simply …

void loop()
{
  if (digitalRead(BUTTON1) Spark.variable("mess", response1, STRING);
  else if (digitalRead(BUTTON2) Spark.variable("mess", response20, STRING);
  else Spark.variable("mess", response0, STRING);
 ...
}

… not that being allowed to do so would be remotely efficient, anyway.

3 Likes

Maybe instead of or in addition to a STRINGPTR version for Spark.variable() I’d like to propose a STRINGCLASS version.
Especially since pointers seem to confuse a lot of non-C-people, adding a version that takes a String rather than a char* or char** might be more pleasing to them.

BTW: To avoid confusing my (less C affine) collegues I changed my way, how I write pointers

char* var   instead of    char *var   // to make clear that the type of var is
                                      // a (pointer to char)
char* var[] instead of    char **var  // to make clear that var is an array containing
                                      // (pointers to char) of unknown string 
                                      // length/location 
                                      // even if it is not absolutely correct
                                      // it made it easier for them to grasp  
                                      // they only have a pointer "*" to the string "[]"
char var[][]                          // for a collection of strings (stored all
                                      //  together in one mem area)
1 Like

Yes, that would be nice. Though I think it would be, Spark.variable(const char* mess, String message), with no need to add the type designator argument on the end. :wink:

I actually assumed that a String class version would be already on the todo list, as is the case for other functions.

Interesting. I prefer to see clearly that the label itself is a *pointer. The "char", to me, is just the type.

But I see your point. When reading the code out loud, it sounds better as, "char pointer, label" (your way) instead of "char, pointer label", my way.

Luckily, C compilers let us do it either way. :wink:

I wonder if the Google gods or their nemesis Facebook coders (I jest, of course) follow one or the other convention, as a rule? (I don't much care what the the MS folks do. LOL :-P)

I am sure they all use Boost smart pointers or the local equivalent since they are not trying to squeeze into a :spark: !

The Arduino String class is certainly easy to use but I think it is also easy to have memory problems such as running out of heap if you don't use them wisely.

I would rather talk someone through strcpy than char ** var--copy is easier to understand.

1 Like

Me too, and I would opt for strcpy for Spark.variable, if I were helping someone learn the ropes, definitely.

Still, having the option to be more efficient would literally help me sleep better, because I have one of those types of annoying little voices in my head, you see. :stuck_out_tongue:

I share your concerns about String, with such limited memory and I tend not to use it myself. But it does make life much easier for many and it costs very little to have all three options available, I think.

1 Like

I fully agree, that's why I'll suggest this as a topic for https://community.spark.io/t/firmware-tips-and-tricks/3649

One of the stated goals for the Core - as is for Arduino - is to open an easy to grasp avenue for non professional programmers, and that is the point where a String version could bridge the gap for them.
I too have tried to talk non-C-collegues through pointers, but it seem that this is not an easy concept to get, if you don't know enough about the ins and outs of the ways data has to travel through HW and SW.
So to avoid frustrating newbies with "voodoo" concepts :wink: it'd be nice to build them a bridge, but still tell them, that there might be more efficient ways, but to use them they have to be prepared to go down some rabbit holes.

Pointers aren’t as difficult for people to comprehend if you explain a bit about how memory works.

Let’s say I’ve got this code:

char myName[] = "timb"; char *pointyThePointer;

Our memory contents would look like this:

0x4300 = t {myName} [1 Byte] 0x4301 = i [1 Byte] 0x4302 = m [1 Byte] 0x4303 = b [1 Byte] 0x4304 = \0 [1 Byte] 0x4305 = Empty {pointyThePointer} [4 Bytes]

Now, if we did this: *pointyThePointer = myName, memory address 0x4305 would now contain this: 0x4305 = 4300 {pointyThePointer} [4 Bytes].

4300 is the address of myName. If we initialized Pointy without the asterisks and did pointyThePointer = myName, it would create an entire copy of the char array instead of just a reference to it.

Since pointy was initialized with an asterisks, what happens if we do this: pointyThePointer++? The value increments to 0x4301, which would point to i!

Now, if we try to print *pointyThePointer, it would be the same as printing myName, but what happens if we do this: Serial.print(pointyThePointer, HEX)'? The serial output would be "0x4300", because it contains the address of myName. So how can we get the address of myName directly? The ampersand:Serial.print(myName, HEX)’ would again return “0x4300”.

To put this another way, think of a variable as a file on your computer, a pointer (*) is like a shortcut/alias/soft link to that file and the address (&) is akin to knowing the directory path (C:\My Documents\swap.avi) to that file.

So the Spark functions want to know the memory address of the variable or string so they can directly read the contents.

Does that make sense?

@timb, thanks for that input, but please excuse my ignorance, after reading it, my C world got shattered and maybe you could point :wink: me back on track.

I didn't know that :open_mouth: , are you absolutely sure?
Is this a special feature of gcc, to allocate the needed space and perform an element-wise copy?
How would this element copy work with a pointer to a typdef type?

My impression would have been this:
Since pointyThePointer is not initialized and you dereference it by placing the * (deref operator) in front I'd expect that you are attempting to write a four byte unsigned integer into an unknown one byte char space. If you're lucky the compiler complains about the unallowed cast uint -> char (which I'd expect), but if not you might end up corrupting some unknown byte and "arm a timebomb".

On the other hand I'd expect this to actually do what you pointed out above.
The un-dereferenced (if this is a word :wink: ) pointyThePointer is just the label for the mem location 0x4305 and the assignment operator (=) pushes the address of myName (myName is equivalent to &myName[0] so 0x4300) into this mem location making the contents of [0x4305] equal to 0x00004300.

This said, if myName was not a char but a C++ class this sure would be a different kettle of fish, because then an overload of the = operator might act as described above (allocate mem, perform an element-wise copy and so forth)

So I'm not that sure about this :wink:

There’s lots of useful help in the already posted answers. But your question now having been answered I anticipate you will have others. What almost seems hidden at times is this: The Arduino (and Spark.io) programming language is C++. I program them in C most of the time - I don’t use the OO-features of C++ unless I have to. The best way to learn about pointers (what does the * mean, why the &, what’s this null-termination of char arrays for?) and all the other weirdness of C which, after a while, becomes 2nd nature, is to read a good C primer. I would suggest one of the older and more popular books such as The C Programming Language by Kernighan & Ritchie http://goo.gl/GDvpz2 - I don’t kow why it is so expensive - there are many other similar books but that’s the bible, in this house anyway.

I forgot to tag @timb in my original post, so I’ll do that now :wink:

Your first point (pun intended) is correct. I was half asleep and writing that post with my phone, so *pointyThePointer should just be pointyThePointer.

I’ll go into more detail on the second point in the morning when I’m on my laptop. :smile:

Nice post. Unfortunately, it seems that understanding how memory works is farther than many folks who need to know how pointers work are prepared to go. Trust me I've tried. Oh my, have I tried. :wink:

The world would be a much simpler place if everyone had to go through a military style bootcamp, where learning MIPS assembly language was a requirement for basic (literal -- but not as in string) survival. Alas, we don't live in that world. :stuck_out_tongue:

Err, nope. It wouldn't actually. Not in C anyway. The compiler would complain that it could not convert a char * to a char -- or something close to that. This would be a hard error, not a warning.

If Pointy was a String class, then yes. Then the = would do that, through the magic of operator overloading. But it is not.

Everything else in your post seems right to me, though. Hope it helps someone.

1 Like

Thanks @gruvin,

you have just restored my C world :wink:
Although I’m not sure what you mean with [quote=“gruvin, post:16, topic:3686”]
Everything else in your post seems right to me
[/quote]

I do agree to the bit about pointer arithmetic - despite the part about initializing a pointer with asterisk, which is not initializing the pointer, but filling the referenced mem location; initialization works without asterisk.

But in addition to my earlier points I’m also not sure about the Serial.print() bit.
Since pointyThePointer is a char*, I’d expect the compiler to complain since it sees a first parameter suggesting a string, but then a second parameter where there is no overload that takes a string AND an int. Either a scalar value (e.g. char, int, long, …) AND a base indicator (e.g. HEX, DEC, …) or a string and NO base indicator - unless there were predefined values to indicate char encoding rather than base numbers, but I’m not aware of anything like this.
To achive the proposed effect you’d have to do something like Serial.print((unsigned long)pointyThePointer, HEX);

So again, even people who seem to have worked with pointers for a long time already, happen to get stumbled by them - even more so will people without a firm C background.

I think you're pointing out the semantics, that setting the contents of RAM pointed to by a pointer should not be named, "initialising a pointer". I would agree with that. But it's just one word, eh? :stuck_out_tongue:

Regarding the Serial.print comment. Here's the part that @timb wrote ...

I think the intent of that, let's call it "pseudo code", was more about showing the difference between a pointer and what it points to. Technically, you are right. The compiler would complain, without a cast, like the one you suggest.

Alas, this is sooo true. In theory, I understand pointers and pointers to pointers, like the back of my hand. Unfortunately, every time I look at the back of my hand, it's not quite how I remember it from last time! :stuck_out_tongue:

I guess this all goes support @bko's comment in general, about how it's probably best to to stay away from pointers altogether for newcomers, where possible and in the context of Arduino and Sparkcores at least. C is a low level programming language. It is ideally suited to tiny MCU's with limited resources. But is does mean that our human brains have to take up a little more of the slack.

Want to learn all this stuff properly, once and for all?

For anyone wanting to get a really good handle on C and pointers and indeed the foundational concepts of programming in general AND in a fun way, I would highly recommend the free CS50 lecture videos produced by Harvard University -- the complete course and all related material, in fact. The most recently completed entire CS50 course is there. I find it highly entertaining and for me, it was actually a good exercise to go back to basics and have a couple bad habits re-adjusted.

CS50 is also available on iTunesU.

1 Like

Thanks for that again - and particularly for those CS50 links :smile:

I have not imagined all of you answered so much… I am beginner I absorb knowledge form you.
Thanks all

2 Likes