C++ typedef interpretation of const pointers
There's no point in analyzing typedef
behavior on the basis of textual replacement. Typedef-names are not macros, they are not replaced textually.
As you noted yourself
typedef CHARS const CPTR;
is the same thing as
typedef const CHARS CPTR;
This is so for the very same reason why
typedef const int CI;
has the same meaning as
typedef int const CI;
Typedef-name don't define new types (only aliases to existing ones), but they are "atomic" in a sense that any qualifiers (like const
) apply at the very top level, i.e. they apply to the entire type hidden behind the typedef-name. Once you defined a typedef-name, you can't "inject" a qualifier into it so that it would modify any deeper levels of the type.
Interaction between const pointer and typedef and function declaration in c
In const int* x
, const int
are the specifiers and *x
is the declarator. (This separation is specified by the formal grammar of C and is a reason why writing declarations as int* x
misrepresents the grammar.) This declaration says that *x
is a const int
, meaning x
is a pointer to const int
.
In typedef int* int_ptr
, typedef int
are the specifiers, and *int_ptr
is the declarator. The declaration says that *int_ptr
is an int
, and typedef
is a special specifier that modifies it so that int_ptr
is declared to be a type, rather than an object (variable).
In const int_ptr x
, const int_ptr
are the specifiers, and x
is the declaration. So this declaration says that x
is a const int_ptr
.
Here const
is modifying int_ptr
; const int_ptr x
says that x
is a const
pointer to an int
. In const int *x
, const
modifies int
, so it says *x
is a pointer to a const int
, meaning x
is a pointer to a const int
.
For the most part, when a function is declared with parameter type lists, the parameters must have compatible types in each declaration of the function. But there is an exception: C 2018 6.7.6.3 15 says:
… (In the determination of type compatibility and of a composite type, … each parameter declared with qualified type is taken as having the unqualified version of its declared type.)
This says that, when determining whether int add(const int_ptr x, const int_ptr y)
is compatible with int add(int_ptr x, int_ptr y)
, the const
qualifiers are ignored. Then the parameter types are the same, so the function declarations are compatible.
In int add(const int *x, const int *y)
, x
and y
are not qualified with const
. They point to const int
, but they themselves are not const
. That is, the pointer that is x
can be changed (it is not const
). The fact that it points to something that is const
does not make it const
. So the rule about ignoring qualifiers in function parameters does not apply here; there are no qualifiers on x
and y
. So int add(const int *x, const int *y)
and int add(int *x, int *y)
do not have compatible parameter types.
The reason for this rule about ignoring qualifiers in parameter types comes from the fact that qualifiers only affect objects, not values. If we have an object x
that is const
, it should not be changed (through that type). But, if we have gotten the int
value 3 from x
and are using it in an expression, there would be no meaning to saying 3 is const
. It is just a value being used in an expression; there is no memory assigned to it where we could store a new value that would change 3 to 4. Once the value of an object is retrieved from a const int
, it is just an int
.
Similarly, if we have a volatile int x
, the volatile
means the compiler must get the value of x
each time it is used in an expression, because volatile
means something could be changing the memory of x
in ways the compiler does not know about. But, once we have gotten the value of x
from memory, it is just a value. We are done with the “you have to get it from memory” part, so the volatile
has no more effect.
Since function arguments are always passed by value, the qualifiers are irrelevant to the caller. When a function is declared with void foo(const int x)
, the const
has a meaning inside the function: The compiler must issue a diagnostic if anything inside the function attempts to modify x
with its const
-qualified type. But the caller does not care: The caller only passes a value. When the function starts, it creates a local x
for itself, and that x
is const
, but it has no effect on the caller. So void foo(int x)
and void foo(const int x)
are compatible function declarations.
typedef and containers of const pointers
Short answer:
- is a list of pointers to constant ints.
- is a list of constant pointers to ints.
- is the same as 2.
const (and volatile) should naturally appear after the type they qualify.
When you write it before, the compiler automatically rewrites it internally:
const int *
becomes
int const *
which is a pointer to a constant int. Lists of these will compile fine since the pointer itself is still assignable.
Pointer to constant in C
And this is one of the reasons why I strongly recommend against typedef
-ing pointer types - the const
semantics apply to the pointer, not to what is being pointed to.
const pduong p;
means
struct _duong * const p; // p is a const pointer to non-const type
not
const struct _duong *p; // p is a non-const pointer to const type
In other words, you are declaring p
as a const pointer to struct _duong
, not as a pointer to const struct _duong
.
Personally, I would recommend you ditch the typedef
altogether and declare the pointer explicitly:
const struct _duong *p;
Typedefs are great for abstracting away details, and typedef
-ing pointer types is fine if the user doesn't have to be aware of the "pointer-ness" of the type. In this case, the user does have to be aware of p
's "pointer-ness" to use it correctly, so its type should not be hidden behind a typedef.
typedef pointer const weirdness
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
.
What are the rules to typedef and pointers in c++?
Seems like already answered in :
C++ typedef interpretation of const pointers
See also Is it a good idea to typedef pointers? for more of a discussion on typedef and pointers.
C typedef const argument
This typedef
typedef float vec3[3];
defines an alias for the array type float[3]
This declaration of the parameter
vec3 const vptr
declares vptr
as having array type const float[3]
.
Function parameters that are specified as having array types are adjusted to pointers to objects of the array element types.
So this declaration
vec3 const vptr
is adjusted to the type const float *vptr
. That is it is a non constant pointer to a constant object.
This relation
vec3 const vptr <==> float* const vptr // a constant pointer to an
object
is wrong. And this statement
vptr++; // no error
confirms that.
You can not get this declaration
const float* const vptr
using this typedef
typedef float vec3[3];
Related Topics
Where Are C/C++ Main Function's Parameters
What Is Dynamic Initialization of Object in C++
Reinterpret_Cast VS. C-Style Cast
Why Don't the Std::Fstream Classes Take a Std::String
Inheritance or Composition: Rely on "Is-A" and "Has-A"
How to Read Directly from Physical Memory on Windows
C/C++ Changing the Value of a Const
Error: Variable "Cannot Be Implicitly Captured Because No Default Capture Mode Has Been Specified"
Why Do I Need Double Layer of Indirection for MACros
How Portable Is End Iterator Decrement
Significance of Parentheses in Decltype((C))
Openmp Set_Num_Threads() Is Not Working
Behavior When Dereferencing the .End() of a Vector of Strings
How to Validate Input Using Scanf
Is There Any Compiler Barrier Which Is Equal to Asm("" ::: "Memory") in C++11