Const correctness in C vs C++
In addition to the differences you cite, and the library differences that
Steve Jessop mentions,
char* p1;
char const* const* p2 = &p1;
is legal in C++, but not in C. Historically, this is because C
originally allowed:
char* p1;
char const** p2 = &p1;
Shortly before the standard was adopted, someone realized that this
punched a hole in const safety (since *p2
can now be assigned achar const*
, which results in p1
being assigned a char const*
); with
no real time to analyse the problem in depth, the C committee banned any
additional const
other than top level const. (I.e. &p1
can be
assigned to a char **
or a char **const
, but not to a char const**
nor a char const* const*
.) The C++ committee did the further
analysis, realized that the problem was only present when a const
level was followed by a non-const
level, and worked out the necessary
wording. (See §4.4/4 in the standard.)
const in C vs const in C++
See the spec, in the compatibility appendix C.1.6:
7.1.6 [see also 3.5]
Change: const objects must be initialized in C++ but can be left uninitialized in C
Rationale: A const object cannot be assigned to so it must be initialized to hold a useful value.
Effect on original feature: Deletion of semantically well-defined feature.
Difficulty of converting: Semantic transformation.
How widely used: Seldom.
What is the difference between the const qualifier in C and the const qualifier in C++?
The most important difference is that in C++ a
const
variable is a constant expression (even prior the introduction of C++11constexpr
), but aconst
variable in C is not.Meaning that C++ allows you to do things like
const size_t n = 1; static int array[n];
but C does not allow that, supposedly for historical reasons.In C++,
const
plays part in determining linkage. This is different between C++ versions. According to cppreference.com (emphasis mine):Any of the following names declared at namespace scope have internal linkage:
- non-volatile non-template (since C++14) non-inline (since C++17) non-exported (since C++20) const-qualified variables (including constexpr) that aren't declared extern and aren't previously declared to have external linkage;
Whereas in C,
const
does not play part in determining linkage at all - only declaration scope and storage class specifiers matter.In C++, you can
const
qualify member functions. This isn't possible in C since it doesn't have syntax support for member functions.C allows
const
-qualified variables to be declared without an initializer. In C, we can writeconst int x;
without initializers, but C++ does not allow that. At a glance, this may seem like a senseless language bug in C, but the rationale is that computers have read-only hardware registers with values set by hardware, not software. Meaning that C remains suitable for hardware-related programming.
What is the difference between const int*, const int * const, and int const *?
Read it backwards (as driven by Clockwise/Spiral Rule):
int*
- pointer to intint const *
- pointer to const intint * const
- const pointer to intint const * const
- const pointer to const int
Now the first const
can be on either side of the type so:
const int *
==int const *
const int * const
==int const * const
If you want to go really crazy you can do things like this:
int **
- pointer to pointer to intint ** const
- a const pointer to a pointer to an intint * const *
- a pointer to a const pointer to an intint const **
- a pointer to a pointer to a const intint * const * const
- a const pointer to a const pointer to an int- ...
And to make sure we are clear on the meaning of const
:
int a = 5, b = 10, c = 15;
const int* foo; // pointer to constant int.
foo = &a; // assignment to where foo points to.
/* dummy statement*/
*foo = 6; // the value of a can´t get changed through the pointer.
foo = &b; // the pointer foo can be changed.
int *const bar = &c; // constant pointer to int
// note, you actually need to set the pointer
// here because you can't change it later ;)
*bar = 16; // the value of c can be changed through the pointer.
/* dummy statement*/
bar = &a; // not possible because bar is a constant pointer.
foo
is a variable pointer to a constant integer. This lets you change what you point to but not the value that you point to. Most often this is seen with C-style strings where you have a pointer to a const char
. You may change which string you point to but you can't change the content of these strings. This is important when the string itself is in the data segment of a program and shouldn't be changed.
bar
is a constant or fixed pointer to a value that can be changed. This is like a reference without the extra syntactic sugar. Because of this fact, usually you would use a reference where you would use a T* const
pointer unless you need to allow NULL
pointers.
What is the difference between static const and const?
The difference is the linkage.
// At file scope
static const int a=5; // internal linkage
const int i=5; // external linkage
If the i
object is not used outside the translation unit where it is defined, you should declare it with the static
specifier.
This enables the compiler to (potentially) perform further optimizations and informs the reader that the object is not used outside its translation unit.
What is the difference between #define and const?
The #define
directive is a preprocessor directive; the preprocessor replaces those macros by their body before the compiler even sees it. Think of it as an automatic search and replace of your source code.
A const variable declaration declares an actual variable in the language, which you can use... well, like a real variable: take its address, pass it around, use it, cast/convert it, etc.
Oh, performance: Perhaps you're thinking that avoiding the declaration of a variable saves time and space, but with any sensible compiler optimisation levels there will be no difference, as constant values are already substituted and folded at compile time. But you gain the huge advantage of type checking and making your code known to the debugger, so there's really no reason not to use const variables.
How does const differ in C and C++?
const
in C cannot be used to build constant expressions.
For example :
#include <stdio.h>
int main()
{
int i = 2;
const int C = 2;
switch(i)
{
case C : printf("Hello") ;
break;
default : printf("World");
}
}
doesn't work in C because case label does not reduce to an integer constant.
Defining constant in c with const keyword
Using the const
type qualifier doesn't make something a constant. A constant in C has its own definition.
See § 6.7.3 ¶ 6 of the C11 standard for a description of the const
keyword:
If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined. If an attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type, the behavior is undefined.
What you need there is a constant expression; see § 6.6 of C11 for details.
If what you're really wondering isn't "WTF is up with the const
qualifier?" but rather what the right solution for your code is, the answer is likely to simply not specify the size of the array:
float user_array[] = {5.1, 7.2, 5.1, 8.45, 23.0, 67.123, 5.1};
This is generally considered good practice as it actually makes your code a bit more robust.
static const vs #define vs enum
It depends on what you need the value for. You (and everyone else so far) omitted the third alternative:
static const int var = 5;
#define var 5
enum { var = 5 };
Ignoring issues about the choice of name, then:
- If you need to pass a pointer around, you must use (1).
- Since (2) is apparently an option, you don't need to pass pointers around.
- Both (1) and (3) have a symbol in the debugger's symbol table - that makes debugging easier. It is more likely that (2) will not have a symbol, leaving you wondering what it is.
- (1) cannot be used as a dimension for arrays at global scope; both (2) and (3) can.
- (1) cannot be used as a dimension for static arrays at function scope; both (2) and (3) can.
- Under C99, all of these can be used for local arrays. Technically, using (1) would imply the use of a VLA (variable-length array), though the dimension referenced by 'var' would of course be fixed at size 5.
- (1) cannot be used in places like switch statements; both (2) and (3) can.
- (1) cannot be used to initialize static variables; both (2) and (3) can.
- (2) can change code that you didn't want changed because it is used by the preprocessor; both (1) and (3) will not have unexpected side-effects like that.
- You can detect whether (2) has been set in the preprocessor; neither (1) nor (3) allows that.
So, in most contexts, prefer the 'enum' over the alternatives. Otherwise, the first and last bullet points are likely to be the controlling factors — and you have to think harder if you need to satisfy both at once.
If you were asking about C++, then you'd use option (1) — the static const — every time.
Constant pointer vs Pointer to constant
const int* ptr;
declares ptr
a pointer to const int
type. You can modify ptr
itself but the object pointed to by ptr
shall not be modified.
const int a = 10;
const int* ptr = &a;
*ptr = 5; // wrong
ptr++; // right
While
int * const ptr;
declares ptr
a const
pointer to int
type. You are not allowed to modify ptr
but the object pointed to by ptr
can be modified.
int a = 10;
int *const ptr = &a;
*ptr = 5; // right
ptr++; // wrong
Generally I would prefer the declaration like this which make it easy to read and understand (read from right to left):
int const *ptr; // ptr is a pointer to constant int
int *const ptr; // ptr is a constant pointer to int
Related Topics
Store Results of Std::Stack .Pop() Method into a Variable
Preventing Gcc from Automatically Using Avx and Fma Instructions When Compiled with -Mavx and -Mfma
Boost::Spirit How to Parse and Call C++ Function-Like Expressions
How to Properly Use System() to Execute a Command in C++
G++ -Wall Not Warning About Double-> Int Cast
What Is the Practical Use of Pointers to Member Functions
I++ Less Efficient Than ++I, How to Show This
How to Easily Indent Output to Ofstream
Immediate Exit of 'While' Loop in C++
Why Does Destructor Disable Generation of Implicit Move Methods
C++ Initializing Non-Static Member Array
Erase Element in Vector While Iterating the Same Vector
Why Does Long Long 2147483647 + 1 = -2147483648
Visual Studio 2015 Code Analysis C6386 Warns of Buffer Overrun