View previous topic :: View next topic |
Author |
Message |
Prefekt
Joined: 21 Oct 2010 Posts: 85
|
char* func(void) PIC18F2525 vs. PIC24FJ... |
Posted: Sun Nov 18, 2018 8:38 am |
|
|
If I run this code on a PIC24FJ...
Code: |
char* func(void){
char ret[25];
sprintf(ret, "function1");
return ret;
}
void main()
{
while(1){
fprintf(PORT1, "ret: %s\r", func());
delay_ms(1000);
}
|
the print is : "function1"
If I run this on a PIC18F2525 the print is: "L<2>nction1"
<2> is HEX 02;
The first 2 chars are anything but not "fu", why? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19561
|
|
Posted: Sun Nov 18, 2018 9:03 am |
|
|
There is an issue with what you are trying to do.
The array 'ret', is only reseved while you are physically running code inside the function 'func'. The memory area it uses can be re-used as soon as you exit the function. Problem is that if the compiler decides to use this area for something else (like temporary storage in the fprintf), the data stored here will be corrupted....
If you want to return a pointer to a char array from a function, this needs to be to a static array, or a RAM area that has perhaps been allocated by malloc. Otherwise the data can become overwritten....
On the PIC24, the stack is used for temporary storage, so it happens not to be overwriting the area. The PIC18, happens to be re-using this area. |
|
|
Prefekt
Joined: 21 Oct 2010 Posts: 85
|
|
Posted: Sun Nov 18, 2018 9:56 am |
|
|
Thanks for the explanation.
Is this only an issue in the compiler version for PIC10/1216/18 or also for PIC24? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19561
|
|
Posted: Sun Nov 18, 2018 10:24 am |
|
|
Technically for everything.
You have to understand how RAM is used. There is a 'pool' of available RAM, and when a variable is allocated, it uses space inside this. While you are in the function that uses it, or any 'sub function', this is maintained and protected from re-use. As soon as you exit the function that allocated this RAM, it becomes available for other functions to use.
If you look in the 'symbols' list you will find the address being allocated. So there will be an entry for func.ret, and an address where this is stored. You will then probably find that there is an entry like @FPRINTFxxxxx.xxx which is allocated the same RAM.
This is the 'scope' of the variable.
A static variable has an allocation that is permanent, and therefore reserved from any other use.
The same happens on C compilers on the PC etc.. Returning a pointer to a local variable, is a 'well known' error in C programming.
<https://stackoverflow.com/questions/19042552/returning-a-pointer-of-a-local-variable-c>
Your pointer is copied, but it points to an area that is no longer preserved.
This is not a feature of a particular chip, it's a feature of C.... |
|
|
dluu13
Joined: 28 Sep 2018 Posts: 395 Location: Toronto, ON
|
|
Posted: Sun Nov 18, 2018 10:45 am |
|
|
Scope and lifetime of variables is something you should study.
There's another way to get around this, but you no longer have a return value. Instead, you pass the pointer to an array to func() as a parameter. Since arrays are passed by reference, you will modify that array, and also the array will still be in scope when you exit the function.
Code: | void func(char *ret){
sprintf(ret, "function1");
}
void main()
{
char mystr[25];
while(1){
func(mystr);
fprintf(PORT1, "ret: %s\r", mystr);
delay_ms(1000);
}
} |
|
|
|
Prefekt
Joined: 21 Oct 2010 Posts: 85
|
|
Posted: Sun Nov 18, 2018 11:10 am |
|
|
Ah, yes I remember.
Normally I develop in c#.
Thank you for support! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19561
|
|
Posted: Sun Nov 18, 2018 11:32 am |
|
|
C#, explicitly does not allow returning references to local variables, by
reference. This is done to ensure that a reference cannot outlive the
referenced variable.
Because of this the returned variable always physically points to data,
and is therefore 'protected' from re-use so long as this exists.
The allocation is by 'data use' and so long as something is still using the
data, it is protected. |
|
|
|