Last updated:2026-10-13
C++ const Explained: Constants, const Pointer vs Pointer to const¶
About the Author¶
WeChat official account:

1. The Meaning of const¶
A const type is a type declared with the type qualifier const; the value of a const-type variable or object cannot be updated.
2. The Role of const¶
- It can define constants
-
Type checking
-
The difference between const constants and
#definemacro-defined constants:A const constant has a type, so the compiler can perform safety checks; a #define macro definition has no data type — it is mere string replacement, so no safety check is possible.Thanks to the two experts who pointed out the problem here; see: issue -
const constants support all types.
- In other cases it is just a
const-qualified variable; do not confuse it with a constant.
-
-
Preventing modification, providing protection and making the program more robust
-
Saving space and avoiding unnecessary memory allocation
- A const initialized with an integral/enum type and a constant expression (i.e. a constant expression) behaves in use much more like a compile-time constant: if you never take its address and only use its value, it is usually optimized into an immediate, just like
#define; only taking its address or an odr-use across translation units puts it into memory, and there is only one copy in the whole program. - After
#defineexpands into a literal, it is compiled into instructions as an immediate; the instructions themselves occupy memory too, so multiple uses mean multiple occurrences (see the discussion in issue #5). - Note: not all const variables are constant expressions — only those of integer/enum type initialized with a constant expression are; other const variables are merely const-qualified variables (e.g. floating point, user-defined types);
constexpris the correct way to express compile-time constants.
- A const initialized with an integral/enum type and a constant expression (i.e. a constant expression) behaves in use much more like a compile-time constant: if you never take its address and only use its value, it is usually optimized into an immediate, just like
3. const Objects Are Local to a File by Default¶
Note: non-const variables are extern by default. To make a const variable accessible in other files, it must be explicitly specified as extern in the file.
Accessing a variable not modified by const across different files
// file1.cpp
int ext;
// file2.cpp
#include<iostream>
extern int ext;
int main(){
std::cout<<(ext+10)<<std::endl;
}
Accessing a const constant across different files
// extern_file1.cpp
extern const int ext=12;
// extern_file2.cpp
#include<iostream>
extern const int ext;
int main(){
std::cout<<ext<<std::endl;
}
Summary:
You can see that variables not modified by const need no explicit extern declaration! A const constant, however, requires an explicit extern declaration and must be initialized! Because a constant cannot be modified after its definition, it must be initialized at definition time.
4. Defining Constants¶
const int b = 10;
b = 0; // error: assignment of read-only variable ‘b’
const string s = "helloworld";
const int i,j=0 // error: uninitialized const ‘i’
The above contains two errors: + b is a constant and cannot be changed! + i is a constant and must be initialized! (Because a constant cannot be modified after its definition, it must be initialized at definition time.)
5. Pointers and const¶
There are four const forms related to pointers:
const char * a; // 指向const对象的指针或者说指向常量的指针。
char const * a; // 同上
char * const a; // 指向类型对象的const指针。或者说常指针、const指针。
const char * const a; // 指向const对象的const指针。
Summary:
If const is on the left side of*, then const modifies the variable the pointer points to, i.e. what the pointer points to is a constant;
if const is on the right side of*, const modifies the pointer itself, i.e. the pointer itself is a constant.
Another way to read it
Read the English from right to left, taking "to" as the divider: what comes before "to" describes the pointer's trait, and what comes after "to" describes the target's trait
const char * p; //p is a pointer to const char
char const * p; //同上
char * const p; //p is a const pointer to char
const char * const p; //p is a const pointer to const char
When the target's type is char, the content can be modified through the pointer, e.g. *char='y';
When the target's type is const char, the content cannot be modified through the pointer
Concrete usage is as follows:
(1) Pointer to a constant
ptr is a pointer to a const object of type int; the const qualifies the int type — the type of the object ptr points to — not ptr itself, so ptr need not be given an initial value. But you cannot modify the pointed-to object's value through ptr.
Besides, you also cannot use a void* pointer to hold the address of a const object; you must use a pointer of type const void* to hold the address of a const object.
Another key point: it is allowed to assign the address of a non-const object to a pointer to a const object.
Assigning the address of a non-const object to a pointer to a const object:
We cannot modify the value of val through the ptr pointer, even though it points to a non-const object!
We cannot use a pointer to a const object to modify the underlying object; however, if the pointer points to a non-const object, the object it points to can be modified in other ways. The value pointed to by a const pointer can be changed — just not through the const object pointer! Modify it as follows:
Summary:
1. For a pointer to a constant, the object's value cannot be modified through the pointer.
2. You cannot use a void*pointer to hold the address of a const object; you must use a pointer of type const void*to hold the address of a const object.
3. Assigning the address of a non-const object to a pointer to a const object is allowed; to modify the value of the pointed-to object you must do it through other means, not directly through the current pointer.
(2) Constant pointer
A const pointer must be initialized, and the value pointed to by a const pointer can be modified, but its pointing cannot.
#include<iostream>
using namespace std;
int main(){
int num=0, num1=1;
int * const ptr=# // const指针必须初始化!且const指针的指向不能修改
ptr = &num1; // error! const指针不能修改指向!
cout<<*ptr<<endl;
}
The code produces a compile error: a const pointer cannot change what it points to.
#include<iostream>
using namespace std;
int main(){
int num=0, num1=1;
int * const ptr=# // const指针必须初始化!且const指针的指向不能修改
*ptr = 1;
cout<<*ptr<<endl;
}
Nothing goes wrong with this code; it normally outputs 1.
Finally, when the address of a const constant is assigned to ptr, since ptr points to a variable rather than a const constant, an error is reported: a const int* -> int * error!
#include<iostream>
using namespace std;
int main(){
const int num=0;
int * const ptr=# // error! const int* -> int*
cout<<*ptr<<endl;
}
If the above is changed to const int *ptr, or to const int *const ptr, both work fine!
(3) Constant pointer to a constant
Having understood the previous two cases, this one is fairly easy to understand:
ptr is a const pointer that then points to a const object of type int.
6. Using const in Functions¶
const qualifying a function's return value
This is basically the same in meaning as const qualifying ordinary variables and pointers:
(1) const int
This by itself is meaningless, because the return value is itself assigned to another variable!
(2) const int*
The content pointed to by the pointer is immutable.
(3) int *const
The pointer itself is immutable.
const qualifying function parameters
(1) The passed-in parameter and the pointer itself are immutable inside the function — meaningless!
This indicates that the parameter cannot be modified inside the function body, but here it has no significance: var is itself a formal parameter and will not change within the function. The same applies when the passed-in formal parameter is a pointer.
When an input parameter uses "pass by value", the function automatically creates a temporary variable to copy the parameter, and that input parameter never needed protection in the first place, so do not add const.
(2) The content pointed to by the parameter pointer is a constant and immutable
Here src is an input parameter and dst is an output parameter. After adding const to src, if a statement inside the function body tries to change the content of src, the compiler will point out the error. This is one of the effects of adding const.
(3) The parameter is a reference, to improve efficiency while preventing modification.
For parameters of non-inner data types, a function declared like void func(A a) is bound to be rather inefficient, because the function body will create a temporary object of type A to copy the parameter a, and the temporary object's construction, copying and destruction all take time.
To improve efficiency, the function can be declared as void func(A &a), because "pass by reference" merely borrows the parameter's alias and needs to create no temporary object.
But the function void func(A &a) has a drawback:
"pass by reference" may modify the parameter a, which we do not expect. Solving this problem is easy — just add const, and the function finally becomes void func(const A &a).
By analogy, should void func(int x) be rewritten as void func(const int &x) to improve efficiency? Not necessary at all: parameters of inner data types involve no construction or destruction, and copying is very fast — "pass by value" and "pass by reference" are nearly equal in efficiency.
Summary:
1. For input parameters of non-inner data types, you should change "pass by value" to "pass by const reference" in order to improve efficiency. For example, change void func(A a) to void func(const A &a).
2. For input parameters of inner data types, do not change "pass by value" to "pass by const reference". Otherwise you neither achieve the efficiency gain nor keep the function easy to understand. For example, void func(int x) should not be changed to void func(const int &x).
This settles two interview questions:
- If a function takes a pointer, does that pointer need const, and what is the difference between placing const at different positions of the pointer;
- If the function's parameter is an instance of a complex type, what is the difference between passing it by value or by reference, and when should const be added to the passed reference parameter.
7. Using const in Classes¶
In a class, any function that does not modify data members should be declared const. If, while writing a const member function, you accidentally modify a data member, or call another non-const member function, the compiler will point out the error, which undoubtedly improves the robustness of the program.
A member function specified with the const keyword is called a constant member function. Only constant member functions are qualified to operate on constants or constant objects; member functions not specified with the const keyword cannot be used to operate on constant objects.
const member variables of a class must be initialized through the initialization list, as shown below:
class Apple{
private:
int people[100];
public:
Apple(int i);
const int apple_number;
};
Apple::Apple(int i):apple_number(i)
{
}
const objects can only access const member functions, while non-const objects can access any member function, including const member functions.
For example:
// apple.cpp
class Apple
{
private:
int people[100];
public:
Apple(int i);
const int apple_number;
void take(int num) const;
int add();
int add(int num) const;
int getCount() const;
};
// apple.cpp
Apple::Apple(int i) : apple_number(i)
{
}
int Apple::add(int num)
{
take(num);
return 0;
}
int Apple::add(int num) const
{
take(num);
return 0;
}
void Apple::take(int num) const
{
std::cout << "take func " << num << std::endl;
}
int Apple::getCount() const
{
take(1);
// add(); // error
return apple_number;
}
int main()
{
Apple a(2);
cout << a.getCount() << endl;
a.add(10);
const Apple b(3);
b.add(100);
return 0;
}
// main.cpp
Compile: bazel run basic_content/const/class_const/first_example:main
At this point an error is reported: getCount() is a const member function, and overload resolution inside it matches only the const version (int add(int num) const); only after that const version is commented out would the error come from calling the non-const add(). That is: const objects can only access const member functions, while non-const objects can access any member function (see issue #338).
When the call is changed to:
This proves that const objects can only access const member functions.
Besides initializing const constants through the initialization list as above, we can also use the following methods:
First: combine the constant definition with static, that is:
Second: initialize it outside:
Of course, if you compile with C++11, you can initialize directly at the declaration and simply write:
Both are supported in C++11!
Just add -std=c++11 when compiling!
This brings up static; here is a brief note on it:
In C++, static static member variables cannot be initialized inside the class. Inside the class there is only a declaration; the definition must be outside the class definition body, usually initialized in the class's implementation file.
Declared in the class:
Used in the class implementation file:
For this item, C++11 does not allow declaration together with initialization — that is, the usage above.
FAQ¶
What is the difference between a pointer to const and a const pointer?¶
const int* p is a pointer to a constant: the value cannot be changed through it. int* const p is a constant pointer: the pointer itself cannot be reassigned. Rule of thumb: const left of * protects the value, right of * protects the pointer.
Can a const object call a non-const member function?¶
No. A const object may only call const member functions, so the compiler can guarantee the object state is untouched. A non-const object can call both.
Should I use const or #define for constants?¶
Prefer const: it is type-checked, respects scoping and can be debugged. #define is a preprocessor text substitution with no type information.
Related Topics¶
- C++ Pure Virtual Functions and Abstract Classes | C++那些事
- C++ assert | C++那些事
- C++ Bit Fields | C++那些事
- Implementing C++ Polymorphism in C | C++那些事
- The Complete C++ const Family | C++那些事
- C++ Dereference Operator * | C++那些事
- C++ Initialization and Assignment | C++那些事
- Ways to Return Multiple Values in C++ | C++那些事
- C++ mutable Keyword | C++那些事
- C++ restrict and Compiler Optimization | C++那些事