Using pointers with arrays [Resolved]

Ever since I learned C pointers have confused me. Here is a simplified version of a much more complex sketch to illustrate what I want to do. This works:

#include<stdio.h>
char testdata[18] = {"LOG,123,12.34567\0"};

void setup() {
  Serial.begin(9600);
  delay(500);
  float toprint = testfunc(testdata);
  Serial.print(toprint, 5);
}

float testfunc(char *point) {
  float data = (point[8] & 0x0f) * 10 + (point[9] & 0x0f) + (point[11] & 0x0f) * 0.1 + (point[12] & 0x0f) * 0.01 + (point[13] & 0x0f) * 0.001 + (point[14] & 0x0f) * 0.0001 + (point[15] & 0x0f) * 0.00001;
  return data;
}

void loop() {
  //Nothing needed for demonstration purposes.
}

In my real program, testdata is actually a char array containing data received over Wi_Fi, from which I want to extract an unsigned int and a float. In the example the float is 12.34567.

If I compile the above for either an ESP8266 or a Mega 2560 it compiles and correctly prints 12.34567 to the serial monitor (although with the ESP8266 it also prints a few junk characters before the 12.34567, I am not to concerned about this as it is not core to my problem).

If I instead use, for example:

#include<stdio.h>
char testdata[18] = {"LOG,123,12.34567\0"};

void setup() {
  Serial.begin(9600);
  delay(500);
  float toprint = testfunc(testdata);
  Serial.print(toprint, 5);
}

float testfunc(char *point) {
  float data = atof(point[8]);
  return data;
}

void loop() {
  //Nothing needed for demonstration purposes.
}

Then for an ESP8266 I get:

invalid conversion from 'char' to 'const char*' [-fpermissive]

If I compile for a Mega 2560 it compiles without error but prints 0.00000

I said ‘for example’ because I have tried many variations on using a pointer to tell atof where to get its data, none of them work. I’m hoping you won’t ask me what variations I’ve tried, there are too many! Everything I could think of. I am hoping that someone here who understands pointer use can tell me the one thing I didn’t try which will work.

I thank you :slight_smile:

  float data = atof(&point[8]);
  return data;
float data = atof(point[8]);

point[8] is the single character in the 9th position of the array. atop wants a pointer. There are two ways. You can use pointer math or you can use the address-of operator &

float data = atof(point +8);

Or

float data = atof(&point[8]);

And note you can probably just write:char testdata[] = "LOG,123,12.34567";
The ‘\0’ is added by the cString for you and the size of the array provided by the compiler.

You don’t need the curly {} brackets as you are using a cString. You would if you had provided the various elements of the array one by one:char testdata[18] = {‘L’, ‘O’, ‘G’, ‘,’,...};

AWOL, Delta_G,
Thank you both.

  float data = atof(&point[8]);

Works in the example but not in my original code. However, now I have something to aim for, now I know that is the correct way to do it I can look elsewhere for the problem, knowing that bit is OK.

J-M-L,
Thanks for the extra info, much appreciated.

That's all for now, I have a beer waiting for me :slight_smile:

Update, partly so those of you that helped know what happened and partly so anyone else reading this with similar problems can learn from my mistakes.

I was making 2 mistakes at the same time.
First, the thing I asked about; pointers. I find pointers confusing. I still find them confusing, but maybe a little less so (why does this code need an &, but in other situations it works without a &, such as in my first example). So, I tried many variations of:

atof(&point[8]);

Unfortunately, none of them worked, including the correct one, because of another thing I was doing wrong:

I have always thought that char and uint8_t were interchangeable. Well, they are much of the time. Only much of the time. Although I used char in the sample code I used uint8_t in the original code. I now know one situation where they are not interchangeable. If you want to store characters, not numbers, use char, even if int8_t or uint8_t seem to work OK.

(Oh, and the beer was very nice thank you :slight_smile: )

PerryBebbington:
If you want to store characters, not numbers

It's not about what you want to store, it's about what type the function you're calling is expecting to see.

Perry,

I suspect your confusion is, in part, due to the somewhat inconsistent way c/c++ addresses data. For all simple, integral data types (char, int, long, bool, etc.), the variable name, when used by itself, evaluates to the VALUE of the data:

int x = 10; // x has the value 10

Serial.print(x); // x has the value 10, so this prints "10"
Serial.print(&x); // &x is the memory ADDRESS of x, Serial.print receives a pointer to x

But for some complex types, arrays in particular, the variable name evaluates to a POINTER to the data, which is the memory ADDRESS of the first element of the array.

char x[10] = "abcd"; // x is a 5-element array of char
Serial.print(x); // x here evaluates to the ADDRESS of x[0]. i.e. - Serial.print receives a pointer
Serial.print(&x[0]); // This is exactly equivalent to the previous line, as it explicitly gets the address of x[0]

All c++ object names evaluate to pointers, just like for arrays.

Then there's the confusion of references, which also use the "&" symbol, but for a slightly different purpose...

So, in deciding whether or not you need an & (AddressOf operator), look at the type of the data. If it's a simple type, and you want the value, no ampersand. Otherwise, add the ampersand to get a pointer to the data instead. For complex types, you most often only need the & if you want to get a pointer to one element of an array, in which case you need to also specific WHICH element, by providing [n] indexing.

Regards,
Ray L.

RayLivingston:
All c++ object names evaluate to pointers, just like for arrays.

You were doing well, but went off the beam with that one. First, the name of an object that's an instance of a class can be defined to evaluate to just about anything using operator overloading, for example:

operator uint32_t() const { return internalVariable; }

Even if that's not the case, calling a function with an object name as an argument (without specifying it as a pointer or reference in the function's defintion) will use Call by Value. Meaning a copy of the object will be placed on the stack before the function is called. If the class has a defined Copy Constructor, it will be used for this. Otherwise the default copy constructor will be used, perhaps with unexpected results.

RayLivingston:
All c++ object names evaluate to pointers, just like for arrays.

no. gfvalvo explains it in previous comment. I just needed to protest loud

Thank you Ray and gfvalvo for the extra information. Because I have trouble with pointers I have a sketch containing examples of what works, all this has been added to it for future reference.

Much appreciated.