11

I have a struct which contains some pointers. I want the value of these to be unmodifiable. But simply writing const infront doesn't make the structs members unmutable

typedef struct{
  int *x;
  int *y;
}point;

void get(const  point *p,int x, int y){
  p->x[0]=x;//<- this should not be allowed
  p->y[0]=y;//<- this should not be allowed
}

Can someone point me in the right direction.

EDIT:

So it would seem that there is no simple way of using the function prototype to tell that everything belonging to the struct should be unmodifiable

monkeyking
  • 6,670
  • 24
  • 61
  • 81
  • 4
    What do you want unmodifiable? The pointed-to `int`s? Then `const int *x;` means you can't modify the pointed-to value through that pointer. The pointers? Then `int * const x;` forbids modifying the pointers. – Daniel Fischer Nov 01 '12 at 16:39
  • If my int *x, is an array, then I want the values within this array to be unmodifiable. – monkeyking Nov 01 '12 at 16:46
  • Then you need `const int *x;` in the struct definition. Note that the values in the array can still be modified through other pointers (which may invoke undefined behaviour, if `x` points to an element of `const int arr[3] = { 15, 7, 3 };` or so). – Daniel Fischer Nov 01 '12 at 16:50
  • `int *x` is not an array, it's a pointer. If you had declared `int x[20]` then the array would have been part of the `const`ed area. But `x` is a pointer and its `const`ness is independant of the one of `p`. – Patrick Schlüter Nov 01 '12 at 16:50
  • 1
    Your edit comment is wrong in the sense that the `const` does exactly that: it tells that everything belonging to that `struct` isn't modified by that function. The error in your thinking is to confuse areas pointed to by p and those by p->x and p->y , *they are not part of the tructure*. Look at my ascii art below, your structure is only the drawn out part, the rest is distinct. – Patrick Schlüter Nov 01 '12 at 17:13
  • @tristopia I understand why it wont work. And this is why Im complaining. I want to enforce unmutability for the struct and everything related to that struct. Including pointeradresses and the actual data at these adresses. – monkeyking Nov 01 '12 at 21:37

4 Answers4

9

You can do it without typecasting by defining a const point type and a mutable point type, then use a transparent union:

typedef struct{
    const int *  x;
    const int *  y;
} const_point;

typedef struct{
    int *  x;
    int *  y;
} mutable_point;

typedef union __attribute__((__transparent_union__)) {
    const_point cpoint;
    mutable_point point;
} point;

Then, you declare your function parameters using either the point or const_point type (never the mutable_point type).

point type object will transparently cast to a const_point type, but not the reverse. This allows you to have a greater degree of type safety.

See here for an example in gcc: http://toves.freeshell.org/xueg/

Note that transparent union was not supported in the last version of C++ I checked (not sure about the latest C++ standard) so you can expect portability issues.

It can also make the code harder to read, and maintain, especially if you have more complex struct. e.g.: you could have point type where either x or y is const, or you may need to embed your point structure into a another struct, e.g. rectangle, for which you might have to define multiple struct for multiple type depending on their constness.

All in all, I'm not sure it's always worth the extra trouble.

Droopycom
  • 1,831
  • 1
  • 17
  • 20
  • It could be worth the trouble if you like to use references so that you can instantiate it with rvalues (since you can use rvalues with constant references). That's probably the only case where it seems to be quite useful for me. – meneldal May 18 '15 at 07:17
4

If I understand your question correctly, you would like to get automatic propagation of constness of the entire struct object to the objects pointed by that struct members. I.e. if the struct object is not const, the arrays should be modifiable, while if the if the struct object is const, the arrays should not be modifiable.

If so, then, unfortunately, it is not achievable in C language.

In C++ it can be done by forcing the user to use accessor member function to access the data members (instead of accessing data members directly). But in C it simply can't be done.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
2

To explain what you need to establish, when you write

point str;

point *p=&str;

Here p is a pointer to str which is of type point

When you declare it as const, it means that p is a constant pointer. This does not restrict the pointers that the structure may contain.

If you want the const ness to apply inside the structure, you have to define the pointers inside the structure also as const

typedef struct{
   const int *  x;
   const int *  y;
}point;

Again to push home my point declare the parameter as

    void get(point * const  p,int x, int y) 
   //Constant Pointer ( *to prevent p from pointing to anything else*)

    //    AND

   //Make the pointers inside point structure constant
   //( *to prevent the int pointers x & y from pointing to anything else*)

If the structure it is pointing to is also const use

      void get(const point * const p, int x, int y)
     //Constant Pointer to constant structure 
Desert Ice
  • 4,461
  • 5
  • 31
  • 58
  • If my members of my struct are declared const, how do I input values in it in the beginning of my program. – monkeyking Nov 01 '12 at 16:55
  • By typecasting to a non const pointer. `(int*)(p->x) = &whatever`. While it might look like it would defeat the purpose of the `const`ness. It can be very useful on big projects where you want to make sure that changes to a structure will be done only in a controlled unique place. – Patrick Schlüter Nov 01 '12 at 16:57
  • Since the content of the pointer is a constant, it should be initialized during the declaration. – Desert Ice Nov 01 '12 at 16:59
  • 1
    IMO there's no point to `const`ing the pointer itself (or any other function parameter). The constness of the pointed to area is important, it tells the reader that there are no side effects via this pointer, but the constness of the pointer itself conveys no useful information, neither for compiler nor for the programmer. – Patrick Schlüter Nov 01 '12 at 17:07
  • @PatrickSchlüter It just prevents accidental reassignment of the pointer within the function. e.g. `if(myStruct = NULL)` at the top of the function would be a pretty obscure bug for anyone simply reading through the code. – Dan Bechard May 28 '16 at 10:23
  • In `void get(const point * const p, int x, int y)`, The pointer `p` is passed by value, so the function cannot modify it to point elsewhere. The `const` is redundant, should be `void get(const point *p, int x, int y)` instead. – biscuits Feb 05 '19 at 22:44
1

That's because you change the memory content pointed to by another pointer than p.

p points on a structure containing 2 pointers to int. You don't change the memory p is pointing to, but another memory area. So the compiler is fine with that.

       +----------+
p ->   |    x     |  -> wherever  
       +----------+
       |    y     |  -> another place in memory
       +----------+

The constness od p is not inheritable. If you had written p->a = array; then the compiler would have complained. The const is only a contract saying that you won't change the memory through that pointer.

Patrick Schlüter
  • 11,394
  • 1
  • 43
  • 48