arm gcc const string array not placed in rodata - c

On ARM-GCC in C
when defining a const string array and accessing it directly the strings are correctly placed in the .rodata section.
If I however have a struct with a pointer to the string array placed in .data section and uses ram. How can I put myStrings into a read only section and save ram?
const char * myStrings[] = {"String1", "String2"}; //.rodata
const char * myStrings2[] = {"String3", "String4"}; //.data
typdef struct {
const char ** strings;
int a;
} mystruct_t;
const mystruct_t mystruct = {myStrings2,2};
void main()
{
for(uint8_t i=0;i<2;i++)
{
printf("%s",myStrings[i]);
printf("%s",mystruct.strings[i]);
}
}
edit: provided minimal code.

as #Dmitri pointed out, only the pointers to the strings are stored in ram. This can be avoided by declaring them constant as well
const char * const myStrings2[] = {"String3", "String4"};
typdef struct {
const char * const * strings;
int a;
} mystruct_t;

Related

Declaration of char pointer array without knowing the size at programm start

I have char* Array in a struct that strores song titles. Depending an the album the array size may vary. To be "safe" i defined the array with the size of 99. The code example below shows my problem
Is there a better way (i am sure there is) to solve the array size problem? Somehow creating the array after knowing the exact size of the array?
Thank you.
struct AudioObject{
int count;
const char* band;
const char * titles[99];
};
void fillStruct(AudioObject *a);
void printStruct(AudioObject * a);
void main(void){
AudioObject aO;
fillStruct(&aO);
printStruct(&aO);
}
void fillStruct(AudioObject *a){
a->count=3;
a->band="Iron Maiden";
const char *arr[3]={"The Wicker Man","Ghost of the Navigator","Brave New World"}; // example input
for(int i=0;i<a->count;i++){
a->titles[i]=arr[i];
}
}
void printStruct(AudioObject * a){
printf("Interpret: %s\n",a->band);
printf("Tracks: %d\n",a->count);
for(int i=0;i<a->count;i++){
printf("Title: %s\n",a->titles[i]);
}
}
In case this needs to be read-only, you should let titles be a const char** (or pedantically, const char*const *const) pointing to a separate array such as:
const char* IRON_MAIDEN_TITLES[] = { "...", };
If you do that, you probably wish to store the size of each such title array too, in a separate variable in the struct.
Another option is to skip const and allocate anything dynamically. You can for example make the last member of the struct a flexible array member:
struct AudioObject{
int count;
char* band;
char * titles[];
};
Then when you know the size, you allocate the whole object like this:
size_t number_of_titles = 666;
AudioObject* ao = malloc(sizeof *ao + sizeof(const char* [number_of_titles]));
and then strcpy (ao->titles[0], "something");

populating struct fields in a separate function in C

getting empty values in the struct for this implementation since pointers are freed after call to myFunc ends. what's a good way of populating a struct when its fields are populated in a different function?
struct Poke {
char *name;
char *type;
};
void myFunc(struct Poke *p) {
char fish[5] = "fish";
char *name = fish;
char fillet[8] = "fillet";
char *type = fillet;
p->name = name;
p->type = type;
}
int main () {
struct Poke p;
myFunc(&p);
printf("%s\n", (&p)->name);
printf("%s\n", (&p)->type);
}
So you realize that the memory allocated for fish and fillet is deallocated when the function returns.
So you need memory that persists after the function call.
So you go and do some research and discover C's memory allocation functions like malloc and free. You will also need C's string handling functions like strcpy.
Go read about all the functions you can find in the include headers "stdlib.h" and "string.h".
One way is by allocating memory for the strings inside the structure itself, like this:
#include <stdio.h>
#include <string.h>
struct Poke
{
char name[64];
char type[64];
};
void myFunc(struct Poke *p)
{
char fish[5] = "fish";
char fillet[8] = "fillet";
strncpy(p->name, fish, 64);
strncpy(p->type, fillet, 64);
}
int main ()
{
struct Poke p;
myFunc(&p);
printf("%s\n", p.name);
printf("%s\n", p.type);
return 0;
}
You either need to make the strings static (static const for completeness) so they are persistent:
void myFunc(struct Poke *p)
{
static const char fish[5] = "fish";
char *name = fish;
static const char fillet[8] = "fillet";
char *type = fillet;
p->name = name;
p->type = type;
}
Or you need to define your structure members as char arrays and copy the string in:
struct Poke
{
char name[5];
char type[8];
};
void myFunc(struct Poke *p)
{
strcpy(p->name, "fish");
strcpy(p->type, "fillet");
}
The issue in this particular case is that char fish[5] = "fish"; creates a local variable and copies the string "fish" into it. So assigning char *name = fish; then p->name = name; stores the address of this local variable in your struct (and the same goes for p->type).
You can avoid this by directly storing the addresses of the string literals:
char *name = "fish";
char *type = "fillet";
And on a somewhat unrelated note, you don't need to dereference the address of p here:
printf("%s\n", (&p)->name);
printf("%s\n", (&p)->type);
The following is sufficient:
printf("%s\n", p.name);
printf("%s\n", p.type);

Write to double const pointer

I want to implement my own string implementation for education. For that I defined a struct named string as follows:
struct string {
const char *const data;
const int length;
};
I use functions to create these string structs and then I assign them to variables.
In order to override the const int length I use the following trick:
*(int *) &result.length = // a int
Now I also want to write to the const char *const data.
As far as I know the first const makes sure that you cant edit the items at which the pointer points, and the second const is that you can't point the pointer to a different memory location. These are properties of an immutable string. So my question is: How can I assign something to the const char *const data like I did to the const int length?
Edit: result as shown above is an instance of the struct string
Form the struct string at its declaration and initialize it.
Also recommend to store the size and not the length and use size_t.
#include <stdio.h>
#include <stdlib.h>
struct string {
const char * const data;
const size_t size;
};
struct string string_copy(const char *src) {
size_t size = strlen(src) + 1;
char *copy = malloc(size);
if (copy) {
memcpy(copy, src, size);
} else {
size = 0;
}
struct string retval = {copy, size}; // ****
return retval;
// or return a compound literal (C99)
return (struct string){ copy, size};
}
void string_free(struct string s) {
free((void*)s.data);
}
int main(void) {
struct string a = string_copy("Hello");
printf("%zu <%s>\n", a.size, a.data);
string_free(a);
// do not use `a` subsequently
return 0;
}
I do not recommend to initialize with a string literal like struct string retval = {"World", 6}; as that limits the usefulness of struct string.
Using a opaque struct has many advantages #Jonathan Leffler that exceed this approach - mainly to keep other code from messing with the struct string.

warning with const operator (in struct)

it may be a nooby question but i cant figure out why i get a warning
struct4.c:32:15: warning: assignment discards ‘const’ qualifier from pointer target type [enabled by default] crea[i].size = wsize[i%5];
compiling this:
struct shirt {
char *size;
char *colour;
} ;
typedef struct shirt Camicia;
void loadshirt (Camicia * const crea, const char *wsize[] , const char *wcolour[]);
int main (void) {
Camicia collezione[50];
const char *sizearray[] = {"xs","s","m","l","xl"};
const char *colourarray[] = {"black","blue","yellow","orange"};
loadshirt(collezione,sizearray,colourarray);
printf("%s\n",collezione[4].size);
printf("%s\n",collezione[4].colour);
return 0;
}
void loadshirt (Camicia * const crea, const char *wsize[] , const char *wcolour[]) {
int i=0;
while (i<50) {
crea[i].size = wsize[i%5];
crea[i].colour = wcolour[i%4];
i++;
}
}
You defined data members of the structure as pointers to non-const character strings.
struct shirt {
char *size;
char *colour;
} ;
However in the function you are assigning pointers to const character strings to pointers to non-const character strings
crea[i].size = wsize[i%5];
crea[i].colour = wcolour[i%4];
See declarations of wsize and wcolour in the parameter list
const char *wsize[] , const char *wcolour[]
You may not do that.
Define the data members as pointers to const strings
struct shirt {
const char *size;
const char *colour;
} ;
Or define the parameters as having type of pointers to non-const strings
char *wsize[] , char *wcolour[]
In this case you have to change also the definitions of the corresponding arguments
char *sizearray[] = {"xs","s","m","l","xl"};
char *colourarray[] = {"black","blue","yellow","orange"};
In C strings literals have types of non-const arrays.
Here your function takes a const pointer to a Camicia
void loadshirt (Camicia const * crea, const char *wsize[] , const char *wcolour[]) {
And 3 lines later you try to modify crea :
crea[i].size = wsize[i%5];
crea[i].colour = wcolour[i%4];
You can't do that.
When the compiler says something along the lines of X discards 'const' qualifier it means exactly that. Something is const, but you're trying to modify it as if it weren't.
It's important to try to understand the compiler's error messages, you'll save a lot of time.
Now if you want to fix the function, first you'll need to remove the const qualifier from crea in the list of parameters.
But also note that here wsize and wcolour are const, while Camicia is defined like this:
struct shirt {
char *size;
char *colour;
} ;
typedef struct shirt Camicia;
Either make your struct Camicia store const char* or modify the other parameters to be char*. Since you're using string literals in main, you'll probably want everything to be const char*.
to avoid the runtime seg fault events,
the following code will work correctly.
this code takes into account that the arrays in main()
are actually an array of pointers to char* (I.E. strings)
#include <stdio.h>
#include <stdlib.h>
#define MAX_SHIRTS (50)
struct shirt {
const char *size;
const char *colour;
} ;
// struct shirt * const says the pointer is const,
// not that the contents of where the pointer points is const
void loadshirt (struct shirt * const, const char **, const char **);
int main (void) {
struct shirt collezione[MAX_SHIRTS];
// create two arrays of const pointers to consts
const char const *pSize[] = {"xs","s","m","l","xl"};
const char const *pColour[] = {"black","blue","yellow","orange"};
loadshirt(collezione, pSize, pColour);
printf("%s\n",collezione[4].size);
printf("%s\n",collezione[4].colour);
return 0;
}
void loadshirt (struct shirt * const crea, const char **pSize , const char **pColour)
{
int i=0;
for(i=0; i<MAX_SHIRTS; i++)
{
crea[i].size = pSize[i%5];
crea[i].colour = pColour[i%4];
}
}
You are making a non-const pointer to a const pointer, you can't modify the contents the const pointer points to, but if you discard const qualifier you can do it through the new non-const pointer, so the compiler is warning you about that.

Initialize a structure with an array in a variable

How can we initialize a structure with an array (using its variable)?
This version works well:
MyStruct test = {"hello", 2009};
But this version is bugged:
char str[] = "hello";
MyStruct test = {str, 2009};
You cannot assign arrays in C, so unfortunately there's no way to do that directly. You may use strcpy to copy the data, though.
typedef struct {
char name[20];
int year;
} MyStruct;
int main() {
MyStruct a = { "hello", 2009 }; // works
char s[] = "hello";
MyStruct b = { "", 2009 }; // use dummy value
strcpy(b.name, s);
return 0;
}
The definition of MyStruct should contain a first member of type char const *.

Resources