Why does variable revert to previous value?

I'm trying to pass a parameter (that could be either an integer or a string) to a function . Since I haven't figured out how to accept both with the same variable in a function I am passing it as a char string/array and checking to see if it is a number at which point I convert it to an int and process. Example: I may type "ON" for the parameter or I may type "01" (the hex command for on for the hardware I am interfacing).
Two questions arise here:

  1. why does "sizeof(chars)" always return 4?

  2. when I decide that the char string is a number I convert it to the decimal equivalent of that ASCii character. However, it immediately changes back to ASCii. I have some guesses as to why this happens but don't really understand...

Here is the code:

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

  printer("1234567");
}


void loop() 
{
}


void printer(char chars[])
{
  Serial.println(chars);
  Serial.println(sizeof(chars));
  Serial.println("\n\n");

  if(chars[0] >= '0' && chars[0] <= '9')
  {
    for(int i = 0; i < (sizeof(chars) - 1); i++)
    {
      Serial.println(chars[i], DEC);
      chars[i] -= '0';
      Serial.println(chars[i], DEC);
      Serial.println(chars[i], DEC);
      Serial.println("\n\n");
    }
  }

  Serial.println(chars[0], DEC);
  Serial.println(chars);
}

And here is the serial monitor output:

1234567
4


49
1
49


50
2
50


51
3
51


49
1234567

Can someone shed some light on this? Thanks!

Firstly you didn't mention you were using the Due - this is important!

Anyway sizeof (char []) is the size of a pointer variable. Since you get a
value of 4 you must be on the Due.

I think you wanted to use strlen() to get the string length?

You cannot write to a string constant - that's not allowed (on the Due nothing
happens when you write, on the Uno it happens to overwrite the constant (basically
the behaviour is "undefined" so you can't expect it to do any particular thing.)

The reason one of the printlns seems to catch the new version of chars is simply
due to compiler optimizations.
So don't overwrite a string constant, use strlen() not sizeof().

flyinguy18:
Example: I may type "ON" for the parameter or I may type "01" (the hex command for on for the hardware I am interfacing).

You're trying to make a flexible interface, don't fall into that trap - make the users type in reasonable responses and reject anything else. I strongly suggest changing that function to accept a number, and if the user types something else, reject it with some kind of error message.

I fell into this trap recently, I had a date field for the users to enter, and I thought "it would be nice if they could enter whatever date format they want" so I tried to make the field work with whatever they put in - 10/12/13, Oct 12 2013, 2013-10-12, etc, etc... but I eventually realized that can not work, for a lot of reasons. The main reason is eventually, some doofus user is going to put "mary had a little lamb" in that box, so regardless of what lengths you go to accept poorly formatted user input, eventually you're going to have to put error messages for some things, and you don't want users entering ambiguous data - for example if someone is working in Europe, they might know they are using an American system, but might still enter dates the European way - so, if they said 10-11-12 - is that October 11, 2012, November 10th 2012, or November 12th 2010?

Eventually I decided, since our system is international, to restrict users more than normal rather than giving them the free-form entry I originally thought would be good. Now, that field works like this - you must enter at least three letters for the month, and you must enter a 4-digit year - everything else is rejected.

So, what I'm saying is, I think "let users enter whatever they want and try to make the system figure out what they meant" is an anti-pattern - something that seems good 'on paper' but causes problems in practice.

As for the code you posted, why no loop?

MarkT:
Firstly you didn't mention you were using the Due - this is important!

Anyway sizeof (char []) is the size of a pointer variable. Since you get a
value of 4 you must be on the Due.

I think you wanted to use strlen() to get the string length?

Sorry forgot to mention the Due!
And used outside of that function "sizeof(chars)" returns the number of bytes occupied in the array, exactly what I need. I will try strlen() instead.

MarkT:
So don't overwrite a string constant

Ok, makes sense. I was thinking that it automatically separated the constant string into a normal array...

jasmine2501:
So, what I'm saying is, I think "let users enter whatever they want and try to make the system figure out what they meant" is an anti-pattern - something that seems good 'on paper' but causes problems in practice.

As for the code you posted, why no loop?

I am not concerned about "users" as there is no outside input to the program. This is only a function for my fellow engineers (some of which are used to a config software with the hex commands I mentioned).
No loop because it was throwing a fit so I isolated it to a separate sketch to debug and don't want to flood the serial monitor :slight_smile:

flyinguy18:
This is only a function for my fellow engineers

That's the last group of folks I would provide a flexible interface for. If they are used to entering numbers, force them to enter numbers. Avoid this problem - you won't hurt the users by restricting them to enter reasonable values. I'm saying, remove that complexity from your program, because it has nothing to do with the problem you're trying to solve, and it's making it hard for you to debug the important stuff.

And used outside of that function "sizeof(chars)" returns the number of bytes occupied in the array,

You haven't posted all the code, but I suspect that outside the printer() function you are referring to a global array called "chars", inside that function it's a local pointer to an array.

Change the name of the parameter to anything except "chars" then try sizeof(chars), I think you will get what you want.

That said unless the string in chars is always the full length of the array sizeof() is of no use anyway (and if it was you'd just use a constant), so strlen() is the way to go.

I convert it to the decimal equivalent of that ASCii character. However, it immediately changes back to ASCii.

I assume you're referring to this

chars[i] -= '0';
      Serial.println(chars[i], DEC);

If you use DEC and println() it will ASCIIize the value (probably adds the '0' right back again). Use Serial.write() if you want the binary value to be transmitted.


Rob

hey sry for the small interruption but i have a small question regarding variable size arrays and i was hoping if someone here can help me. I have the following variables:
char* words[20];
char* tracks[20];
where they represent the number of tracks and words saved in a database on a SD card. My question is that, I would like the array of words and tracks to be of variable size and not static, that it is I don't want to specify the size of the array. Can you plz help me :frowning:

ferrari1991:
hey sry for the small interruption but i have a small question regarding variable size arrays and i was hoping if someone here can help me. I have the following variables:
char* words[20];
char* tracks[20];
where they represent the number of tracks and words saved in a database on a SD card. My question is that, I would like the array of words and tracks to be of variable size and not static, that it is I don't want to specify the size of the array. Can you plz help me :frowning:

Two methods for that:

  1. Allocate an array way bigger than you'll ever need - uses more memory
  2. Use a linked list or some other expandable storage object - uses more code

If you go with option two, look first at how you're going to be searching your list, because you may want to build a binary tree, red-black tree, or something else like that - the linked list is just the simplest example of a collection you can add to.

If it's a one-time thing then just malloc() the amount of space you need when you know, if it's supposed to grow then as jasmine2501 said, a linked list would work and malloc() elements as you need them.

If it has to both grow and shrink a lot more admin will be needed.


Rob

jasmine2501:
Eventually I decided, since our system is international, to restrict users more than normal rather than giving them the free-form entry I originally thought would be good. Now, that field works like this - you must enter at least three letters for the month, and you must enter a 4-digit year - everything else is rejected.

I would have used three fields: one each for year, month, and day, in that order.
If I had to use only one field, I would make it yyyy-mm-dd.
This way, the months do not depend on language.
(By the way, China and several other Asian countries do not use names for the months. They just number them, 1 to 12.)

Graynomad:
If it's a one-time thing then just malloc() the amount of space you need when you know, if it's supposed to grow then as jasmine2501 said, a linked list would work and malloc() elements as you need them.

If it has to both grow and shrink a lot more admin will be needed.


Rob

Thanks both of you for the fast reply, i got what u r saying but i feel that it is very complicated..basically what im trying to do is to have
char* words[999];
char* tracks[999];
but when i defined these variables like that, i dont get anything in serial monitor because currently i only have 15 words and 15 tracks in the memory card...so the arduino is reading garbage...however the intended of my project is to fill the sd card from time to time with words and tracks without needing to define their size...that is i wont their to be [999] but wont fill it with 999 right away...is that possible??
thanks again

odometer:
I would have used three fields: one each for year, month, and day, in that order.
If I had to use only one field, I would make it yyyy-mm-dd.

In the new interface there will be a date picker, problem solved :slight_smile:
That's the unfortunate consequence of selling your soul to the machine, sometimes you don't get to do the best thing right away. I'm a hired gun, so I have to hit what they want to me hit. I would give anything to be able to do things the way I want all the time. But I guess that's why I get Arduinos and lights and airplanes and see what I can do with them!

And yeah... the "growable" array is another good way to do what we need here. You just allocate a bigger array when you need it, then copy everything from the old array into the new one, and destroy the old one. You need to keep track of the current size and the last element to do that, so you know where to place new elements in the empty spaces. The drawback of that method is you need twice the size of the array available to grow it. If your array is bigger than half the empty memory, you won't be able to grow it. (This is how "ArrayList" works in the .Net Framework)