Pointer to a const structure, can members still be modified?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP



Pointer to a const structure, can members still be modified?



I have a structure which I want to pass to some external c code via some callback function that they register with my program. However, I want to pass that structure as read-only. My concern is that they can still modify the structures which I have pointer to inside the original structure that I pass. Explained with small example below:


struct s1
int a;
int b;
;

struct s2
int x;
struct s1 *y;
;

void f(const struct s2 *o)

//o->x=10; //error
o->y->a=20; //no error
o->y->b=30; //no error


int main()

struct s1 o1 = 10, 20;
struct s2 o2 = 30, &o1;
f(&o2);



So, How do I improve my code design so that they cannot modify anything w.r.t the structure I pass?





Do you want to allow them to read some parameters? If not, you may use forward declaration approach to hide some members from public code, make it visible only in private section of your library. Based on your answer I will give you official answer.
– tilz0R
Aug 10 at 7:30





What you can do is define the struct in a .c file. From all header function you typedef a void* for your type and other functions that belong to a public api of you type. Then from the callback you cannot see the members and only use the public functions of the type.
– hetepeperfan
Aug 10 at 7:30





@tilz0R Yes, they should be able to read all the members. They'll take different actions depending on what the value of that member is.
– Raman
Aug 10 at 7:34





To fully hide, you will have to create getter functions in your library.
– tilz0R
Aug 10 at 7:36




5 Answers
5



To properly handle this situation, you can only use forward declaration to hide members together with getter and setter functions.



Focus on code below and check:


struct s1


struct s2


struct s1


mylib.c



mylib.h:


#ifndef __MYLIB_H
#define __MYLIB_H

//Create forward declaration only
//Implementation is in .c file
struct s1;

//Create user structure
struct s2
int x;
struct s1* y;
;

int get_a_from_s1(struct s2* s);
void set_a_to_s1(struct s2* s, int a);

#endif /* __MYLIB_H */



mylib.c:


#include "mylib.h"

//Now implement structure
struct s1
int a, b;
;

//Make getter
int
get_a_from_s1(struct s2* s)
return s->y->a;


//Make setter
void
set_a_to_s1(struct s2* s, int a)
s->y->a = a;



main.c:


#include <stdio.h>
#include "mylib.h"

int main(void)
struct s2 s;
int a;

....

s.y->a = 5; //error

//Set s1.a value from s2 structure
set_a_to_s1(&s, 10); //OK

//To view members of s1 inside s2, create member functions
a = get_a_from_s1(&s); //OK

printf("a: %drn", a);

return 0;



Of course, please make sure that ->y is not NULL or you have undefined behavior.


->y


NULL





For the record, this kind of design pattern with incomplete type of struct s1 in the header is called opaque type or opaque pointers.
– Lundin
Aug 10 at 9:41


struct s1



You cannot. Even if you pass the struct s2 by value, you will get in the function a pointer to a non const struct s1, simply because it is what s2 contains per its definition.


struct s2


struct s1


s2



And once you have a pointer to a non const object you can change that object. What I mean here and what other answers mean, is that it is not a language problem - more exactly the language can nothing for you here - but a design problem. If for any reason it is not acceptable that the struct s1 can be changed from f then you have to find a different design where you do not pass a non const pointer to it, be it member of a const struct or not. Here a simple way would be to pass the individual members:


struct s1


f


void f(int x, const struct s1 *y)
y->a = 20; // error



It may not be what you expect, but it is the best I can say for C language.





Yes, I also think that passing all members one by one is the only way.
– Raman
Aug 10 at 7:36



You can change the second struct declaration like so:


struct s2
int x;
struct s1 const *y;
;



The added const ensures that y is read-only.


const





Yes, But that way I myself can't modify y.
– Raman
Aug 10 at 7:19





Yes, you can if you cast it back! ((struct s1 *) (o->y))->b=30;
– memo
Aug 10 at 7:21





@It'scominghome One scenario would be if you want to publish it as part of an API or library and make sure that users of it don't change it accidentally.
– memo
Aug 10 at 7:23





@memo very ugly and may introduce undefined behavior.
– tilz0R
Aug 10 at 7:25





@tilz0R I just answered the question, not that I would recommend it
– memo
Aug 10 at 7:27



I would write another function that takes in a const struct s1 * to modify the values of s1.



In f(), const struct s2 *o means in the struct s2 being pointed to by o, the values of its members ie, x and y cannot be changed.


f()


const struct s2 *o


struct s2


o


x


y



With o->y->a=20, you are not modifying the value of y. You are just using that address to modify a member of the structure being pointed to by y and y's value, which is an address, remains the same.


o->y->a=20


y


y


y



So those 2 lines will give no error.



I could see no way to avoid this unless you could make the struct s2 definition to


struct s2


struct s2
int x;
const struct s1 *y;
;



in which case y is a pointer to a constant struct s1.


y


struct s1



See the spiral rule and visit the cdecl website.






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

make 2 or more post in bootsrap

Store custom data using WC_Cart add_to_cart() method in Woocommerce 3

Firebase Auth - with Email and Password - Check user already registered