Pointer declartion (non-volatile memory) initialization (volatile memory) - Page 1
Forum section: electronics / general

author: zer0c00l
author_rank: Contributor
timestamp: November 22, 2020, 11:07:20 pm
content: Consider astructwith the following data membersCode:struct Data{unsigned int count;const char *Name;};A variable of typeDatais created later in the codeCode:PERSISTENT(struct Data log);wherePERSISTENT() maps variable to the battery backed SRAM memory spaceWhen*Nameis later assigned to in the codeCode:log.Name = "Sensor1";where does the character stringSensor1gets stored. The pointer*Nameis stored in battery backed SRAM, but does the string it points to gets stored in MCU memory?. If that is the case if the MCU is restarted the string gets lost, but the pointer (stored in battery backed SRAM) still points to that address which is empty now. Would that be the case?

author: helius
author_rank: Super Contributor
timestamp: November 23, 2020, 12:29:43 am
content: The string is a literal part of the code, so the usual thing that happens is to put it in program ROM, or flash. However, if this compiled code is being loaded by some method other than running out of ROM, such as a bootloader or debugger probe, it could be stored in RAM.If you had assignedlog.Nameto a non-literal, it would be in RAM as you described. The compiler cannot know the size of the string eventually assigned to the pointer, so it cannot allocate space for it in battery-backed RAM, of course.

author: Nominal Animal
author_rank: Super Contributor
timestamp: November 23, 2020, 03:25:08 am
content: Whetherlog.Namepoints to RAM or Flash afterlog.Name = "Literal";, can actually be manipulated by the C linker, especially if using ELF-based tools. This is because string literals are stored in a specific ELF section, typicallyrodata.strx.y, and the linker can choose where it actually ends up; typicallyrodata.What helius wrote above, matches basically all hosted environments (those where you write normal C application code).In the Arduino environment, depending on the core (AVR or one of the 32-bit ARMs), one can change this easily. For example,log.Name = F("Literal");ensures the string is in Flash, whereas normallylog.Name = "Literal";causes the string literal to be in RAM.A better option is to use an enum ("enumeration constant") instead. For example,Code:typedef enum {DATA_NAME_UNKNOWN = 0,DATA_NAME_FOO,DATA_NAME_BAR,DATA_NAME_WHOTSITS,DATA_NAME_NOMINAL,DATA_NAME_ANIMAL,DATA_NAME_IS,DATA_NAME_STUPID,DATA_NAME_ETC,DATA_NAME_MAX = DATA_NAME_ETC} data_name;typedef int data_type;struct data {data_type count;data_name name;};If you don't assign any specific values to the enums, the compiler will assign one in ascending order. In the code, they are equivalent to the integer constant values assigned to them. It is common to have the first one (or "invalid"/"unused" or similar) as zero, and a maximum as the last one, so that one can check if e.g.int imatches a known enum:if (i >= 0 && i <= DATA_NAME_MAX) { /* Within valid range */ } else { /* Outside valid range */ }.Furthermore, if you use a variable of some enum type as the argument toswitch(), most C compilers will warn you if you forget one of the constants (and do not have adefault:case). Very useful, don't you agree?In some C library code, you'll see enums defined thus:Code:enum {FOO = 1,#define FOO FOOBAR,#define BAR BARBAZ,#define BAZ BAZ};In C, enums are an integer type, and can be used in expressions without any explicit casts. The reason why one would define a preprocessor macro with the same name as an enum constant, is that that way, the library user can check if a constant is known via#if defined(BAR) ... #endif. This is very useful for modular portable code, which can check if features are available at compile time or not.