Pointer to constant string in struct - c

I'm writing in C.
In header1.h I declare a pointer to a constant string (a constant array of char), a new type with a struct and a variable of this type, and another variable:
const char *str_ptr = "Hello world!";
struct entry_t {
const char *text;
int var;
};
typedef entry_t entry;
extern entry start_entry;
int a;
In the file func.c I define the new variable:
#include "header1.h"
entry start_entry = {str_ptr, a};
void func(void)
{
//here i use start_entry
}
But the compiler report me the error
constant expression required
referring to the initialization of start_entry, and in particular of the const char* member.
Why? Where is the error?
Naturally if I define the string as
const char str[] = "Hello world!";
and then the entry variable as
entry start_entry = {&str, a};
everything is ok.
EDIT_1
I've made two errors in reporting my code:
var is const too
struct entry_t {
const char *text;
const int var;
};
in func.c entry is const too
const entry start_entry;
EDIT_2
I don't care of var, my interest is on const char * member (even if maybe the problem is the same: i write wrong code before, also with int var i had an error...).
I'm going to redefine all:
//header1.h
const char *str_ptr = "Hello world!";
struct entry_t {
const char *text;
};
typedef entry_t entry;
extern const entry start_entry;
//func.c
#include header1.h
const entry start_entry = {str_ptr};
void func(void)
{
//here i use start_entry
}
I can't understand why start_entry has a const char* member, i define it with a const char* but there is that error.

You can only use compile-time constants in initializers. The variable str_ptr is not even a run-time constant. What the const qualifier does is to prevent the string pointed to by str_ptr from being modified. The pointer itself can be reassigned. Therefor you need to initialize your start entry in an initialization function instead:
#include "header1.h"
entry start_entry;
void func(void)
{
//here i use start_entry
}
void init(void)
{
start_entry.text = str_ptr;
start_entry.var = a;
}

The problem is that with a C compiler you cannot initialize such statements in the global scope, it is considered as code outside a function:
(the same way that you cannot call a function to get tis return value and initialize a global outside a function)
entry start_entry = {str_ptr, a};
str_ptr is not really considered as a constant in C since it is seen as a pointer. The const qualifier does nothing here.
(the problem also occurs for me with a BTW, it looks that you are using some strange C++ compiler or a special C99 option see pmg comment)
When simply calling g++, your example works.
Note about your "naturally" statement:
entry start_entry = {&str, a};
compiles (with your compiler, gcc still rejects it because of a) but since you pass the address of the pointer, not the pointer itself => unspecified behaviour: not good.
If you want to do that, I suggest that you use a C++ compiler.
What works with simple gcc call:
char str_ptr[] = "Hello world!";
entry start_entry = {str_ptr, 12}; // considered as an array, value decided by the compiler, even if const keyword is missing
What doesn't
const char *str_ptr = "Hello world!";
entry start_entry = {str_ptr, 12}; // str_ptr is considered as a variable pointer even if const
entry start_entry = {"Hello world!", a}; // a is a variable: nocantdo
Where you are right is that char [] tells the compiler that the pointer is constant, even without the const keyword BTW, whereas the const keyword is not taken into account.
Notes:
I had to fix your code so it compiles with my gcc compiler:
typedef struct {
const char *text;
int var;
} entry;
extern entry start_entry;
Another point is: avoid declaring globals in header files as you'll get multiple inclusions or undefined symbols if you have more than one code file including it.

Related

How do I get a const pointer to a const struct?

I like to create scenarios in my code where I declare a static global struct inside a .c file that everyone will share, it contains configuration stuff. Right underneath the declaration, I'll create a constant pointer to this struct and put this in the .h file so that everyone can get access to it.
Sometimes, within a .c file, I like to have a global pointer to the specific configuration that that .c file cares about, that way I don't have constantly keep referencing the global struct, because sometimes I'll get this configuration from a different source on different projects.
The issue that I have is that I can't define this "local global" pointer because the initializer element is not constant. Here is an example.
typedef struct
{
int Value;
} mystruct_t, *pmystruct_t;
static const mystruct_t GlobalStruct;
const pmystruct_t pGlobalStruct = &GlobalStruct;
const int *ValuePtr = &pGlobalStruct->Value;
int main()
{
*ValuePtr = 10;
return 0;
}
I tried reading on the const keywords for pointer in C, and I thought I understood it, but apparently it's still a mystery to me. The line of code that I tried, and may gotten me closer to that piece of code to compile is
const mystruct_t const *pGlobalStruct = &GlobalStruct;
However, it still doesn't compile because ValuePtr initializer element is not constant (the error I get).
The end goal here is to have ValuePtr be a constant, where no one can change where it is pointing to, but allow change the elements of the struct that it is pointing to.
EDIT: I want ValuePtr to use pGlobalStruct
The definition
const pmystruct_t pGlobalStruct = &GlobalStruct;
makes the variable (the pointer itself) pGlobalStruct constant, not the structure that it points to.
This is only one of the many flaws that hiding pointers behind type-aliases have.
And to make the definition of ValuePtr valid as well, then you need to make both the pointer variable as well as the structure it points to constant:
const mystruct_t * const pGlobalStruct = &GlobalStruct;
Now pGlobalStruct is a constant pointer to a constant mystruct_t object.
Do not hide pointers behind the typedefs.
If your pointer is referencing constant object you cant assign it with the value. You can only initialize the object.
const int x = 10; //initialization
cosnt int *ptr = &x;
How do I get a const pointer to a const struct?
All possible combinations below:
struct str
{
int x;
float b;
};
struct str a;
const struct str b;
const struct str *c;
struct str * const d;
const struct str * const e;
a - not const structure
b - const structure
c - not const pointer to const structure
d - const pointer to not const structure
e - const pointer to const structure
In your case
const mystruct_t * const pGlobalSttruct = &GlobalStruct;

Modifying a const pointer in C

I am trying to understand why the following code compiles and runs fine. I would expect any assignment using data inside f not to compile with a gcc error assignment of member ‘i’ in read-only object. Is there some kind of exception, because data.i is allocated dynamically?
#include <stdio.h>
#include <stdlib.h>
struct a {
int *i;
};
void f(const struct a *data) {
data->i[0] = 55;
}
int main() {
struct a data;
data.i = malloc(2 * sizeof(int));
f(&data);
printf("%d\n", data.i[0]);
return 0;
}
const front of a struct will make it read-only. If the struct contains pointer members, then those pointers themselves will turn read-only. Not what they point at.
That is, const struct a will make the member i behave is if it was declared as int *const i;, meaning that the pointer itself cannot be changed to point elsewhere. The pointed-at data is still of read/write int though.
If you want to restrict access to i inside a function, you should make that function take a const int* parameter and pass the i member to that function.
In the below code, const indicates what data points to is not to be modified. data->i[0] = 55; does not modify the pointer data->i. Instead that line of code modifies the memory pointed to by data->i. This is allowed as pointer .i is int * and not const int *.
struct a {
int *i;
};
void f(const struct a *data) {
data->i[0] = 55;
}
You cant modify i but you can modify the objects referenced by i.
To prevent it you need to:
struct a {
const int *i;
};

Copy const struct in flash to "normal" struct in RAM

Here is what I have, and there is still a problem - the compiler doesn't recognize the "my_reg" structure type.
../Source/functions.c:609:16: error: 'my_reg' undeclared (first use in this function)
ModBusIDReg = (my_reg)const_ModBusIDReg;
// define structure in flash with constants
struct
{
const unsigned char Reg00[32];
const unsigned char Reg01[32];
const unsigned char Reg02[32];
const unsigned short Reg03;
const unsigned short Reg04;
} const_ModBusIDReg =
{
"My String 1" ,
"My String 2" ,
"My String 3" ,
0 ,
0
};
// define structure in RAM, tag name "my_reg"
struct my_reg
{
unsigned char Reg00[32];
unsigned char Reg01[32];
unsigned char Reg02[32];
unsigned short Reg03;
unsigned short Reg04;
} ModBusIDReg;
// This statement is located in InitSys function.
// Both of the files where the structures were
// defined are included at the top of the file
// where InitSys function resides.
// Make a copy of const_ModBusIDReg from
// flash into ModBusIDReg in RAM.
ModBusIDReg = (my_reg)const_ModBusIDReg;
Any ideas on how to do the direct assignment ?
A type cast like this should resolve the compiler error:
ModBusIDReg = (my_reg)const_ModBusIDReg;
Or you could use memcpy():
memcpy(&ModBusIDReg, &const_ModBusIDReg, sizeof(my_reg));
(Sidenote at using memcpy(): In some cases, memory alignment might be an issue. But I'm no c expert. Using compiler-specific attributes like packed in the case of GCC might help, when you define the struct, depending on the platform, compiler, variable types used.)
You can also copy the members of the struct individually in a custom copy function, and initialize the unused parts if there are any. This would be clean, and very clear/explicit, and similar to a copy constructor / assignment operator used in C++ (deep copies are handled properly).
edit:
Since I can't add a comment above after your last edit, I add the comment here:
./Source/functions.c:609:16: error: 'my_reg' undeclared (first use in
this function) ModBusIDReg = (my_reg)const_ModBusIDReg;
In C you can use typedef in your struct type declartions, in order to avoid repeating the struct keyword throughout your code (explained in detail over here or there):
typedef struct { ... } foo;
foo x;
Here an example illustrating the mentioned ideas from above:
/* gcc -Wall -o copy_test copy_test.c && ./copy_test */
#include <stdio.h>
#include <string.h>
typedef struct {
int var;
} my_struct;
int my_copy_func(my_struct *dst, const my_struct *src)
{
if (!dst || !src)
return -1;
dst->var = src->var;
return 0;
}
int main()
{
const my_struct a = { .var = 42 };
my_struct b, c, d;
c = (my_struct)a;
memcpy(&b, &a, sizeof(b));
my_copy_func(&d, &a);
printf("b.var = %d\r\n", b.var);
printf("c.var = %d\r\n", c.var);
printf("d.var = %d\r\n", d.var);
return 0;
}

c make a copy of an array of const structs

I have an array of structs and some of the struct members are constant
I would like to make a deep copy of the array.
The copy will also have the same members of the struct be constants.
How do I initialize the values into the new array without violating the const directive.
Below is an excerpt of my code. The actual program is quite long but I think I've included all the relevant code. In this example keys is declared as a global variable that I would like to make new instances from. I'm treating it sort of like a template with preset values.
When I try to compile this in Debian with gcc I get the following error:
main.c:216:9: error: assignment of read-only location ‘*(new_keys + (unsigned int)((unsigned int)index * 540u))’make: *** [main.o] Error 1
The makefile is using the following qualifier:
CFLAGS=-c -g -std=gnu99 -D_XOPEN_SOURCE=700
Interestingly. I can compile the same code in Xcode without an error or warning even with Xcode set for C Language Dialect GNU99[-std=gnu99]
I can make my code work simply by getting rid of the const keywords. But these values truly should be const. Once these arrays are initialized they will never change. I would like to understand how to do this properly and also I would like to know why this works in Xcode and not gcc.
Header File
typedef struct {
char * name;
char * alias;
int number;
} pair_int_t;
typedef struct {
const char * const name;
const kind_t kind; // kind is enum type
const pair_int_t * const enum_array;
bool received;
const char * const alias;
} key_descriptor_t;
typedef key_descriptor_t keys_descriptor_t[_END_OF_KEYS+1];
body of .c program before main()
const pair_int_t command_list[] = {
{.name = "numeric data response", .alias = "num", .number = RES_NUMERIC_DATA},
{.name = "read attribute", .alias = "readat", .number = CMD_READ_ATTR},
//..
}
// declare a global variable *keys* with constants assigned to it
key_descriptor_t keys[] = {
[_COMMAND] = {.name = "command", .kind = ENUMERATED, .alias = "com", .enum_array = command_list},
[_RESPONSE] = {.name = "response", .kind = ENUMERATED, .alias = "res", .enum_array = command_list},
[_UNIT] = {.name = "unit number", .kind = NUMBER, .alias = "uni", .enum_array = NULL},
//..
}
int initialize_new_keys(keys_descriptor_t new_keys) {
int index;
for (index = _FIRST_KEY; index <= _END_OF_KEYS; index++){
new_keys[index] = keys[index]; // line 216, keys is a global variable
}
return index;
}
main program
int main(int argc, const char * argv[]){
keys_descriptor_t 2nd_set_of_keys;
initialize_new_keys(2nd_set_of_keys);
}
You can only initialize const variables at the time you define them. Creating an array with const members, sending it to a function, and then trying to assign to those members signifies that you didn't really mean const in the first place. There's no syntax in C for "const except for the first assignment".
A reasonable alternative is to make an opaque type, encapsulate the definition in a separate translation unit, and then access all the members purely through an interface of functions. That way, even though things may not be const, they still can't be changed (except through deliberate subversion, which is at least no better than not calling things const in the first place) because no code that uses them has access to the definition of the struct, or therefore to the members.
EDIT: In response to the question in the comment, "is there a way to create and initialize one complex constant variable to the value of another existing constant variable of the same type?", there sure is - just initialize it normally:
struct mystruct {
const int a;
const int b;
};
static const struct mystruct m = {1, 2};
int main(void) {
struct mystruct n = m;
return 0;
}
Obviously the more complex your struct is, the more complex this may become.
"2nd_set_of_keys" is a struct, it should be a static array (or can be a pointer and reserve memory dynamically), the same problem occurs with the "new_keys" parameter.
Although a little weird, you can copy data to const variables (overriding the const verification) by using memcpy().

const struct declaration

Can someone tell me the difference between these two versions of a declaration of a structure?
struct S
{
uint8_t a;
};
and
const struct S
{
uint8_t a;
}
Followed by:
void main(void)
{
struct S s = {1};
s.a++;
}
Hint, i've tried both versions for S in Visual Studio C++ 2010 Express so I know that both compile with errors.
Is the "const struct" doing nothing at all? "const struct S s = {1};" certainly does, but that's not the issue at the moment.
Regards
Rich
/********************************************/
I've just worked out what
const struct <typename> <{type}> <variable instances a, b, .., z>;
is doing:
When const is present before the "struct", all variable instances are const, as though they'd be defined with:
const struct <typename> a, b, z;
So it does do something, but not when there's no instance definitions in-line with the struct declaration.
Rich
A declaration of structure just defines the data type.
const qualifier appies to a variable not a data type. So adding const preceeding a struct declaration should be redundant at the most.
With:
const struct S
{
uint8_t a;
};
The const qualifier there is nonsense, and may even cause a compilation error with some C compilers. gcc issues a warning.
The intent appears to be to declare the data type struct S. In this case, the proper syntax is:
struct S
{
uint8_t a;
};
const struct S
{
uint8_t a;
};
is not a valid construct.
This
const struct S
{
uint8_t a;
} x;
could possibly be valid as you're declaring a variable x that is now const, meaning it cannot change.
The const qualifier applies to variables or members.
To instantiate a const variable, just specify const during instantiation.
What const does, is:
during compilation, verify that only reads are performed on the const variables
if the const variable is created with a value which can be resolved during compilation, put the variable in the program memory
When const is applied to members, like in:
struct T {
int const i;
int j;
};
You can only (legally) assign the value i during the creation of the structure.
You may be able to modify a const value (if the program memory sits in RAM and not ROM) by casting it to a non-const type (const-cast) but this is something you shouldn't do.
The typical usage of const-cast is when you use a library which does not specify the constness in function declarations, and your code does. At this point if you want to use it you have to trust it and cast parameters before calling its functions.
It is nonsense to use const keyword before struct.
If you are using gcc compiler, it shows you the following warning:
warning: useless type qualifier in empty declaration [enabled by default]
This is the only use I can think of:
const struct S {
int a;
int b;
} s;
This declares a struct and immediately creates an instance for it named s and at this point, a and b in s are initialized to 0 (please note that at this point s is a global variable in the translation unit which it has been declared in and can be externally linked to).
printf("a = %d\t b = %d\n", s.a, s.b); // a = 0 b = 0
If you try to set members of s, you will fail:
s.a = 1; //error: assignment of member ‘a’ in read-only object
So, s is not really useful here...unless you do something like:
const struct S {
int a;
int b;
} s = { 1, 2 };
Now let's create another instance of the same struct (declaration is still same as above):
struct S other;
other.a = 1;
other.b = 2;
printf("a = %d\t b = %d\n", other.a, other.b); // a = 1 b = 2
The compiler will not complain anymore as other is not const! only s is const!
Now, what that const do, you may ask? let's try to change s:
s = other; // error: assignment of read-only variable ‘s’
That is all to it. If you did not need the compiler to allocate storage for s at the point of declaration and still needed an instance of s to be const you would just add const at the point of instantiating struct S (note the capital S!!)
Bonus 1
const struct S {
int a;
int b;
};
note that there is no small s anymore. At this point, GCC will warn you that const qualifier does not do anything!!!!
Bonus 2
If you want every instance of the struct to be const, that is its members can only be initialized at the point of definition you can do like (using typedef):
typedef const struct S {
int a;
int b;
} s;
// The wrong way
s thisIsAnS;
thisIsAnS.a = 1; //error: assignment of member ‘a’ in read-only object
// The correct way
s thisIsAnS = { 1 , 2 }; //compiles fine, but you can not change a or b anymore
Conclusion
To me, this is just syntactic sugar and only adds unnecessary complexity to the code. But who am I to judge...
When you declare
const var;
then it allocate the some memory space for it but
struct var;
it was just an declaration compiler does not allocate any space for it.
so it shows the error and in const struct you didn't declare any varible see the code so it shows error.

Resources