Strange uint8_t[200] array behavior. Nano/328P

Hi there!
To my mind I need a sort of help.

I am trying to use char[200] array without a luck (Arduino Nano, Arduino IDE 1.6.7/Ubuntu, chip MEGA328P AU 1436) .
I define array as
volatile uint8_t GVV_photo[181]={0,0…};
outside setup() and loop() sections.

Printing it in the very beginning to serial results in [0]=0,[1]=0,… BUT(!)
GVV_photo[178]=0
GVV_photo[179]=0
GVV_photo[180]=5
GVV_photo[181]=90
GVV_photo[182]=1
GVV_photo[183]=0

So I have a real problem!

Try to uncomment 4 lines - program suffers from partial crash (repeats some parts of print again and again in cycle)

P.S. I’ve tried to check if there are no free SRAM left, but it shows 1500bytes+ after array creation.

Serial.println(freeRam());

int freeRam () {
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

array_crash.c (1.11 KB)

You define GVV_photo[] with 181 elements, but then in serialPrintPhoto() you do this:

  for (i=0; i<255; i++)
  {
    Serial.print("GVV_photo["); Serial.print(i,DEC);Serial.print("]=");Serial.println(GVV_photo[i],DEC);
  }

Sometimes trying to march through 255 elements of a 181 element array doesn’t work well. Try:

 for (i=0; i<sizeof(GVV_photo); i++)
  {
    Serial.print("GVV_photo["); Serial.print(i,DEC);Serial.print("]=");Serial.println(GVV_photo[i],DEC);
  }

and see what happens.

This code

volatile uint8_t GVV_photo[181];

tells the compiler to allocate 181 bytes to the array GVV_photo. Those bytes are indexed as [0], [1], [2], ..., [179], [180]. When you try to read GVV_photo[181], you're fetching a byte that's not a part of GVV_photo. There's no telling what that byte of RAM might hold.

Your post says,

dormouse:
I am trying to use char[200] array

but it appears that your trying to use a 181-character array, rather than 200. Why do you think you're using 200?

I see this line in your post:

GVV_photo[180]=5

I think that's something that got copied wrong, or that was output from some other version of the program. Please verify it.

Finally, you'll want to read the sticky post, "How to use this forum - please read," shown at or near the top of the subject listings for each section of the forum. Please pay special attention to the parts that tell you how to post code and output inside of [ code ] tags.

I really don’t see why it is so hard to simply post the code, instead of forcing everyone to download it.

#define photoDoNotMake 0
#define photoMake 1
#define photoDone 2

volatile uint8_t GVV_photo[181]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

void setup() {
 Serial.begin (9600);
 Serial.println("Start");
 Serial.println("calling photoInit");
 photoInit(10);
 serialPrintPhoto();
}

void loop() {  };
void serialPrintPhoto()
{
  uint8_t i;
  for (i=0; i<255; i++)
  {
    Serial.print("GVV_photo["); Serial.print(i,DEC);Serial.print("]=");Serial.println(GVV_photo[i],DEC);
  }
}
void photoInit(uint8_t angle)
{
  uint8_t i;
  uint8_t cangle=angle;
  Serial.print(" Entering photoInit(");Serial.print(angle,DEC);Serial.println(")");
//  for (i=0; i<255; i++)
//  {
//    if (i==cangle) {GVV_photo[i]=photoMake;cangle+=angle;} else {GVV_photo[i]=photoDoNotMake;};
//  }
  Serial.println(" Exiting photoInit");
}

Thank you!
I’ve corrected the code this way:

#define photoMax 181

array definition to:
volatile uint8_t GVV_photo[photoMax]={…}

and all cycles to:
for (i=0; i<photoMax; i++)

Now everything works as expected. I forgot to truncate cycles length after I’d cut array size down to 181 from the whole byte.

to: tmd3
strings like ‘GVV_photo[photoMax-2]=5;’ are for test usage only, not for production of course. Thank you for this anyway.

to: econjack
sizeOf() calculates value during compilation for a simple array (which defined without pointer/alloc). So maybe it is the more readable way than #define approach.

sizeOf() calculates value during compilation for a simple array (which defined without pointer/alloc). So maybe it is the more readable way than #define approach.

Actually, a more flexible way is to use the following parameterized macro:

#define ELEMENTCOUNT(x)  (sizeof(x) / sizeof(x[0]))

The bad thing about the way I used it before is that it only works for 1-byte objects because sizeof returns a byte count, not necessarily an element count. The macro above is typeless. That is, it can be used with any data type and will return an element count, so you could use it as in:

 for (i = 0; i < ELEMENTCOUNT(GVV_photo); i++)
  {
    Serial.print("GVV_photo["); Serial.print(i,DEC);Serial.print("]=");Serial.println(GVV_photo[i],DEC);
  }

econjack:
That is, it can be used with any data type and will return an element count

Actually, at the risk of being pedantic, it will work with an array of any data type, it won't work with any non-array type.

It will also only work with the actual array variable, where the compiler knows the size of the array. In most cases, a pointer to a value is interchangeable with an array, but this is not one of those cases: once you treat the array as a pointer (perhaps you're passing in an array as a function parameter) it won't work. It also won't work where a function parameter is an unbounded array (using in the parameter declaration without an explicit size.)

But as long as you are using it with an actual array where the size can be determined at compile time, that is a very helpful macro to understand.

dormouse:
I forgot to truncate cycles length after I'd cut array size down to 181 from the whole byte.

As you discovered, that's why it's a good idea to use a defined constant instead of hard-coded values, as you are presenting in your latest post. You've learned an important lesson: remember it always.

And just a minor point: when you #define a constant like your photoMax, popular convention is to make it all capitals: either PHOTOMAX or PHOTO_MAX. The compiler couldn't care less, it will work with any capitalization you use, but making it all caps makes it easier to recognize that it's a defined constant value and not an arbitrary variable.

Thank you for answers.

Is there a way to get sizeOf-like dynamically (including pointer references) in the same way that free() does? I mean free() knows how much it is freeing at any moment.

P.S. About #define - it also has many underlying problems. For example - try to compile

#define READ_PORT PINС
uint8_t portCache;
void setup() {
}
void loop() {
 portCache |= READ_PORT & 0x03;
}

will fail to compile with "stray '\320' in program" (at line before the last line)

or

#define SQR(x) (x*x)
uint8_t val=1;
void setup() {
 Serial.begin(9600);
 Serial.println("Start!");
 Serial.println(SQR(val+1),DEC); 
}

void loop() {
}

will printout to serial monitor:
Start!
3

I mean free() knows how much it is freeing at any moment.

Only because malloc() tracks how much space it allocates.

PaulS: How to get this information from malloc()? Where does it store it? Interesting questions, I have never thought about that.

Have found and hope will check later:

There are two main methods for implementations, the "buddy system" and the "boundary tag method".

In the (rarely used) buddy system, the pointer value itself is sufficient for free() to determine the size.
In the (commonly used) boundary tag method, the allocated size is stored at a fixed offset before the pointer.

and article about malloc

dormouse:
Is there a way to get sizeOf-like dynamically (including pointer references) in the same way that free() does?

No, not without something like STL. The fact is, a pointer doesn't contain information about the size of what it is pointing to.

You can always write a function that expects you to pass a pointer and the size to it.

dormouse:
P.S. About #define - it also has many underlying problems.

It's not so much that it has underlying problems, it just has some quirks and you have to understand it's strengths and weaknesses. Take your one example:

#define SQR(x) (x*x)
uint8_t val=1;

   ...

 Serial.println(SQR(val+1),DEC);
}

SQR is a macro, not a function. If it were a function, when you write SQR(val+1) the compiler generates code to take val, add one to it, and then pass the result (which is 2) to the function to be processed. The function would then multiply the value 2 by itself, and return the expected answer of four.

But since SQR is a macro, what it's doing is performing character substitutions BEFORE COMPILE TIME, which can cause some interesting side effects if you don't understand what it's doing. The macro expansion is done in a pre-processor phase before the code is compiled.

In this case, if you write the SQR function with some string (and it's only a string at this point, it hasn't been compiled yet!) the pre-processor will take whatever you gave the macro, and replace it with that thing, and asterisk, and that thing again. So, SQR(val+1) will quite literally expand into the text "val+1*val+1" which will then get compiled. Because of the order of operations (multiplication is performed before addition) this will be evaluated as if it were written val + (1 * val) + 1 which when the val is finally evaluated becomes 1 + (1 * 1) + 1, which really is 3.

The thing to learn here is that the arguments to a macro are not evaluated before the macro is expanded, like they would be in a function call. Instead, the arguments to the macro are simply substituted into the resulting string, exactly as if you had copy/pasted the argument strings into the source code. When writing a macro, it's a good idea to put parenthesis around the individual arguments. For example, if the macro were defined as:

#define SQR(x)  (x)*(x)

then the resulting expansion would literally be expanded as

(val+1)*(val+1)

which would give you the 4 your are expecting.

Another important think about such a macro is that the the terms may be evaluated more than once. Take for example:

SQR(gatVal()+1)

In this case, there is a function in the macro call. This will literally be expanded to:

getVal()+1*getVal()+1

and this will result in getVal() getting called twice! That may or may not be a problem. If getVal() simply returns a fixed value, it's not a problem, other than not being very efficient. But if getVal() does other processing, like reading a line from a file, sending an output message, moving an actuator, waiting for an line of input, or something like that, it could cause very strange behavior. Again, this is because a macro expansion is very different from a function call - if SQR were a function, the generated code would call getVal() once, add one to the result, and then call the SQR function.

A macro can be very powerful, but it can also be easily misused and yield surprising results if you don't understand what is really going on.

dormouse:
... strings like 'GVV_photo[photoMax-2]=5;' are for test usage only, not for production ...

That's not about coding style. It's about an apparent disconnect between the posted code and the posted output. The code initializes element number [180] to zero, and then seems to print its value as 5. That suggests that the output and the code aren't really connected, which suggests that analyzing either is a waste of effort.

I am trying to use char[200] array without a luck (Arduino Nano, Arduino IDE 1.6.7/Ubuntu, chip MEGA328P AU 1436) .

I define array as:

volatile uint8_t GVV_photo[181]={0,0...};

Actually you are using a uint8_t[181] array.

@dormouse: Please use code tags.

Read this before posting a programming question

ShapeShifter: Thank you! You have explained in details preprocessor behaviour to everyone.

I wanted to show that even simplest and obvious at first sight #define expressions are potentially dangerous. Unfortunately most programmers can't solve both riddles I've given without googling and compiling at all.

Actually, at the risk of being pedantic, it will work with an array of any data type, it won't work with any non-array type.

Very true, which is why I said:

...it can be used with any data type and will return an element count,...

which I thought made it clear that it was to be used with an array.

econjack:
which I thought made it clear that it was to be used with an array.

Yes, you would think so. But I never make assumptions when dealing with beginners and programming - there are so many ways things can be misunderstood. I try to make things as clear and explicit as possible, and things still get misunderstood. :wink: