31

please consider the following code:

typedef struct Person* PersonRef;
struct Person {
  int age;
};

const PersonRef person = NULL;

void changePerson(PersonRef newPerson) {
  person = newPerson;
}

For some reason, the compiler is complaining about read-only value not assignable. But the const keyword should not make the pointer const. Any ideas?

Patryk
  • 22,602
  • 44
  • 128
  • 244
Johannes
  • 871
  • 1
  • 10
  • 16
  • 3
    "But the const keyword should not make the pointer const." Huh? Short answer: don't use typedefs, they are only there to confuse you. Remember them until you need them. – wildplasser Dec 14 '11 at 12:29
  • 16
    @wildplasser: "Don't use typedefs" is not good advice. Perhaps "Don't hide pointers behind typedefs" is more appropriate... – Oliver Charlesworth Dec 14 '11 at 12:32
  • 1
    I beg to differ. It *is* a good advise. Hiding structs behind a typedef is just as confusing as hiding pointers. It only serves to pollute your mental namespace. Even without syntax highlighting I read "struct person *p" easier and faster then "pPerson p". – wildplasser Dec 14 '11 at 12:36
  • @wildplasser: How do you cope with C++ then? – Oliver Charlesworth Dec 14 '11 at 12:49
  • 6
    @struct wildplasser If struct you find typdef:ed structs confusing, struct I am afraid struct you will have problems using any struct programming language but struct assembler. The rest of the struct programmers in the struct world apparently don't agree with struct you. – Lundin Dec 14 '11 at 13:32
  • @Lundin I like the cynism in your comment, but note that by it you proved yourself how coercing the set of type specifiers to a single dimension can confuse. `struct` and `union` could be considered *metatypes*, defined Records actual *types*, and variables *instances* of types. `struct you` makes no sense. It makes sense to declare `struct person` as some record, and then to declare a variable `struct person you`. Note how you won't have to say `struct` again, just use `you`. Note also how much more neat it is to have seperate namespaces for struct and variables - no need for naming schemes. – Jo So Dec 19 '13 at 13:49
  • But it would work if you instead did a macro `#define PersonRef Person*`? Or could you perhaps do some other array-macro trickery? Not as clean in my eyes - but hell, _anything_ would be better than to deal with the ugly ass star syntax, in my view... Especially when you seen like, over five nested levels of it in some codebases... The pointer syntax really bothers me but it seems basically impossible to avoid... :/ – Christoffer Bubach May 16 '22 at 10:47
  • Edit: No.. that wouldn't work for something like `PersonRef first, second` then, right? Urgh... so what, only way out would be a functional macro such as `#define Ref(name) (*name)` and `Person Ref(first),Ref(second)` I guess? Meh, humbug. :'( – Christoffer Bubach May 16 '22 at 10:52

6 Answers6

50

Note that

typedef int* intptr;
const intptr x;

is not the same as:

const int* x;

intptr is pointer to int. const intptr is constant pointer to int, not pointer to constant int.

so, after a typedef pointer, i can't make it const to the content anymore?

There are some ugly ways, such as gcc's typeof macro:

typedef int* intptr;
intptr dummy;
const typeof(*dummy) *x;

but, as you see, it's pointless if you know the type behind intptr.

lemzwerg
  • 772
  • 7
  • 15
Piotr Praszmo
  • 17,928
  • 1
  • 57
  • 65
  • so, after a typedef pointer, i can't make it const to the content anymore? – Johannes Dec 14 '11 at 12:38
  • @Johannes Yes; Add that to your arsenal of reasons *not* to hide pointers in typedefs. It's almost-never a good idea, and plenty of reasons it's a bad one. – WhozCraig May 11 '18 at 15:22
11
const PersonRef person = NULL;

is

struct Person*const person= NULL;

so you are consting the pointer and not the object.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
7

While the problem is already solved by the answers above, I do miss the reason why...

So maybe as a rule of thumb:

  1. The const always refers to it's predecessor token.
  2. In case there is no such, it's "consting" it's successor token instead.

This rule can really help you out for declaring a pointer to const pointers or something equally neat.

Anyway, with this in mind, it should get clear why

struct Person *const person = NULL;

declares a const pointer to a mutable struct.

Think about it, your typedef "groups" the struct Person with the pointer token *. So, for writing

const PersonRef person = NULL;

your compiler sees something like this (pseudo-code):

const [struct Person *]person = NULL;

As there's nothing to const's left, it deklares the token to it's right struct Person * constant.

Well I think, this is the reason why I don't like hiding pointers by typedefs, while I do like typedefs as such. What about writing

typedef struct Person { ... } Person;
const Person *person; /*< const person */
Person *const pointer; /*< const pointer to mutable person */

and it should be quite clear to compilers and humans, what you're up to.

Zappotek
  • 344
  • 2
  • 5
3

Never hide pointers behind typedefs, it is really really bad practice and will only create bugs.

One such infamous bug is that a typedef:ed pointer type that is declared as const will be treated as a "constant pointer to non-constant data", rather than "a non-constant pointer to constant data" which is what one intuitively expects. This is what happens in your program.


Solution:

typedef struct
{
  int age;
} Person;

const Person* person = NULL; // non-constant pointer to constant Person
Lundin
  • 195,001
  • 40
  • 254
  • 396
0

As an addition to Piotr's (accepted) answer, it's possible to avoid GCC-specific typeof:

static_assert(std::is_same<const int *, std::add_const_t<std::remove_pointer_t<intptr>> *>::value, "not same via type_traits");
static_assert(std::is_same<const int *, std::remove_reference_t<decltype(std::as_const(*intptr()))>*>::value, "not same via decltype");

By changing foo_t<T> to foo<T>::type above and/or using boost's version, it's even possible to do this in C++98, though it's only pretty since C++11.


Alternatively, use two different typedefs, which also works for cases other than plain pointers. For example, consider every container's iterator and const_iterator typedefs.

o11c
  • 15,265
  • 4
  • 50
  • 75
0

you are getting and error

error: assignment of read-only variable ‘person’

on statement

person = newPerson;

because you have declared person as const so its value is only read only ....const value can not be changed

if you are going to change that vatiable then why you are kepping it const?

remove const keyword your code will works fine

Jeegar Patel
  • 26,264
  • 51
  • 149
  • 222
  • yes, sure, i just wonder why the pointer ist const and not the content where it points to. that is what i expected... – Johannes Dec 14 '11 at 12:38