const
type qualifier. Despite the challenging title, perhaps much of what is described here will be known to you, but I hope there will be something new too. const
for C? Think again.const
rule in C. const uint32_t hello = 3;
const
before hello
means that during compilation there is a check that hello
never changes.hello
, the compiler will stop you: clang-700.1.81: error: read-only variable is not assignable hello++; ~~~~~^ error: read-only variable is not assignable hello = 92; ~~~~~ ^ gcc-5.3.0: error: increment of read-only variable 'hello' hello++; ^ error: assignment of read-only variable 'hello' hello = 92; ^
const
is located as long as it is in front of the identifier, so the const uint32_t
and uint32_t const
declarations are identical: const uint32_t hello = 3; uint32_t const hello = 3;
void printTwo(uint32_t a, uint64_t b); void printTwo(const uint32_t a, const uint64_t b) { printf("%" PRIu32 " %" PRIu64 "\n", a, b); }
const
qualifier are specified in the printTwo()
function implementation and without it in the prototype?const
qualifiers do not match in the prototype and function implementation.a
and b
outside its scope, so const
has no effect what you pass on to it. Your compiler is smart enough to understand that these are copies of a
and b
, so in this case the presence or absence of const
has no effect on the physical or mental models of your program.const
qualifier for any parameters that are not pointers or arrays, since they are copied into the function by value and the initial value of the passed variables always remains constant 1 .const
for parameters that are pointers or arrays, because in this case your function will be able to manipulate the data that the passed pointer refers to.const
for the entire array. const uint16_t things[] = {5, 6, 7, 8, 9};
const
can also be specified after the type declaration: uint16_t const things[] = {5, 6, 7, 8, 9};
things[]
, the compiler will stop you: clang-700.1.81: error: read-only variable is not assignable things[3] = 12; ~~~~~~~~~ ^ gcc-5.3.0: error: assignment of read-only location 'things[3]' things[3] = 12; ^
const
for the whole structure. struct aStruct { int32_t a; uint64_t b; }; const struct aStruct someStructA = {.a = 3, .b = 4};
struct const aStruct someStructA = {.a = 3, .b = 4};
someStructA
: someStructA.a = 9;
someStructA
declared as const
. We cannot change its members after definition. clang-700.1.81: error: read-only variable is not assignable someStructA.a = 9; ~~~~~~~~~~~~~ ^ gcc-5.3.0: error: assignment of member 'a' in read-only object someStructA.a = 9; ^
const
for individual members of the structure: struct anotherStruct { int32_t a; const uint64_t b; }; struct anotherStruct someOtherStructB = {.a = 3, .b = 4};
someOtherStructB
members: someOtherStructB.a = 9; someOtherStructB.b = 12;
b
changed, because b
declared as const
: clang-700.1.81: error: read-only variable is not assignable someOtherStructB.b = 12; ~~~~~~~~~~~~~~~~~~ ^ gcc-5.3.0: error: assignment of read-only member 'b' someOtherStructB.b = 12;
const
qualifier is equivalent to declaring a special copy of the structure in which all members are defined as const
. If you do not need a 100% const
structure, you can specify const
only for specific members when a structure is declared, only where it is needed.const
for pointers is where the fun begins. uint64_t bob = 42; uint64_t const *aFour = &bob;
bob
aFour
pointing to bob
aFour
? Let's try a few things. *aFour = 44;
clang-700.1.81: error: read-only variable is not assignable *aFour = 44; ~~~~~~ ^ gcc-5.3.0: error: assignment of read-only location '*aFour' *aFour = 44; ^
const
pointer without changing the value it points to? aFour = NULL;
uint64_t const *
, which means "pointer to immutable data", but the pointer itself is not immutable (note also: const uint64_t *
also has a meaning).const
.const
and see how things go. uint64_t bob = 42; uint64_t const *const anotherFour = &bob; *anotherFour = 45; anotherFour = NULL;
clang-700.1.81: error: read-only variable is not assignable *anotherFour = 45; ~~~~~~~~~~~~ ^ error: read-only variable is not assignable anotherFour = NULL; ~~~~~~~~~~~ ^ gcc-5.3.0: error: assignment of read-only location '*anotherFour' *anotherFour = 45; ^ error: assignment of read-only variable 'anotherFour' anotherFour = NULL; ^
const *const
mean? uint64_t const *const anotherFour = &bob;
anotherFour
is:*const
)uint64_t const
) uint64_t const *aFour = &bob;
aFour
is:*
means that the pointer itself is subject to change)uint64_t const
means that the data cannot be changed)const uint64_t *bob
as an “immutable pointer”, but this is not what is happening here. This is actually a “mutable pointer to immutable data”.const
qualifier. We can:const
and allow changing both the pointer itself and the data it points to. uint64_t *bob;
uint64_t const *bob;
uint64_t *const bob;
uintptr_t
), so here the const
has the same effect as in the case of ordinary integer values, i.e. it’s perfectly normal if your implementation uses const
to determine the parameters, but the prototype of your function does not have to include them , since this const
only protects the address, not the data. uint64_t const *const bob;
const
, but what if we add another pointer?const
to a double pointer? uint64_t const **moreFour = &aFour;
**moreFour = 46; *moreFour = NULL; moreFour = NULL;
clang-700.1.81: error: read-only variable is not assignable **moreFour = 46; ~~~~~~~~~~ ^ gcc-5.3.0: error: assignment of read-only location '**moreFour' **moreFour = 46; ^
uint64_t const **moreFour = &aFour;
moreFour
is:*
)*
)uint64_t const
)const
modifier to a level deeper? uint64_t const *const *evenMoreFour = &aFour;
const
3 What can we do now? **evenMoreFour = 46; *evenMoreFour = NULL; evenMoreFour = NULL;
clang-700.1.81: error: read-only variable is not assignable **evenMoreFour = 46; ~~~~~~~~~~~~~~ ^ error: read-only variable is not assignable *evenMoreFour = NULL; ~~~~~~~~~~~~~ ^ gcc-5.3.0: error: assignment of read-only location '**evenMoreFour' **evenMoreFour = 46; ^ error: assignment of read-only location '*evenMoreFour' *evenMoreFour = NULL; ^
uint64_t const *const *evenMoreFour = &aFour;
evenMoreFour
is:*
)*const
)uint64_t const
)const
. uint64_t const *const *const ultimateFour = &aFour;
**ultimateFour = 48; *ultimateFour = NULL; ultimateFour = NULL;
clang-700.1.81: error: read-only variable is not assignable **ultimateFour = 46; ~~~~~~~~~~~~~~ ^ error: read-only variable is not assignable *ultimateFour = NULL; ~~~~~~~~~~~~~ ^ error: read-only variable is not assignable ultimateFour = NULL; ~~~~~~~~~~~~ ^ gcc-5.3.0: error: assignment of read-only location '**ultimateFour' **ultimateFour = 46; ^ error: assignment of read-only location '*ultimateFour' *ultimateFour = NULL; ^ error: assignment of read-only variable 'ultimateFour' ultimateFour = NULL; ^
uint64_t const *const *const ultimateFour = &aFour;
ultimateFour
is:*const
)*const
)uint64_t const
)const
declarations are always safe (unless you need to change values):const
data can be assigned to a const
variable. uint32_t abc = 123; uint32_t *thatAbc = &abc; uint32_t const *const immutableAbc = thatAbc;
const
function parameters as you can. void trySomething(const storageStruct *const storage, const uint8_t *const ourData, const size_t len) { saveData(storage, ourData, len); }
const
checked only at compile time. The const
declaration does not change the behavior of the program.const
exists to help people cope with difficulties a little easier:const
can always be circumvented by explicitly casting or copying memory.const
you may encounter undefined behavior. const uint32_t hello = 3; uint32_t *getAroundHello = &hello; *getAroundHello = 92;
const
, but just giving a warning 4 which you can disable 5 . clang-700.1.81: warning: initializing 'uint32_t *' (aka 'unsigned int *') with an expression of type 'const uint32_t *' (aka 'const unsigned int *') discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers] uint32_t *getAroundHello = &hello; ^ ~~~~~~ gcc-5.3.0: warning: initialization discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers] uint32_t *getAroundHello = &hello; ^
const
initialization violation): uint32_t *getAroundHello = (uint32_t *)&hello;
&hello
type and use uint32_t *
instead.const
members, but will you change the data stored in it after the declaration? struct exampleA { int64_t a; uint64_t b; }; struct exampleB { int64_t a; const uint64_t b; }; const struct exampleA someStructA = {.a = 3, .b = 4}; struct exampleB someOtherStructB = {.a = 3, .b = 4};
someOtherStructB
into const someStructA
. memcpy(&someStructA, &someOtherStructB, sizeof(someStructA));
clang-700.1.81: warning: passing 'const struct aStruct *' to parameter of type 'void *' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers] memcpy(&someStructA, &someOtherStructB, sizeof(someStructA)); ^~~~~~~~~~~~ gcc-5.3.0: In file included from /usr/include/string.h:186:0: warning: passing argument 1 of '__builtin___memcpy_chk' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers] memcpy(&someStructA, &someOtherStructB, sizeof(someStructA)); ^ note: expected 'void *' but argument is of type 'const struct aStruct *'
memcpy
it looks like this: void *memcpy(void *restrict dst, const void *restrict src, size_t n);
memcpy
does not allow passing unchanged pointers to it as a dst
argument, since dst
changes during copying (and someStructA
immutable).const
parameters are checked only by the function prototype. Will the compiler complain if we use a partially immutable structure with separate const
fields as dst
?const someStructA
into a variable, but containing one const
member someOtherStructB
? memcpy(&someOtherStructB, &someStructA, sizeof(someOtherStructB));
memcpy
, even despite the fact that we have overwritten the immutable member of the not completely immutable structure. #include <stddef.h> /* NULL */ #include <stdint.h> /* */ int main(void) { uint64_t bob = 42; const uint64_t *aFour = &bob; /* uint64_t const *aFour = &bob; */ *aFour = 44; /* */ aFour = NULL; const uint64_t *const anotherFour = &bob; /* uint64_t const *const anotherFour = &bob; */ *anotherFour = 45; /* */ anotherFour = NULL; /* */ const uint64_t **moreFour = &aFour; /* uint64_t const **moreFour = &aFour; */ **moreFour = 46; /* */ *moreFour = NULL; moreFour = NULL; const uint64_t *const *evenMoreFour = &aFour; /* uint64_t const *const *evenMoreFour = &aFour; */ **evenMoreFour = 47; /* */ *evenMoreFour = NULL; /* */ evenMoreFour = NULL; const uint64_t *const *const ultimateFour = &aFour; /* uint64_t const *const *const ultimateFour = &aFour; */ **ultimateFour = 48; /* */ *ultimateFour = NULL; /* */ ultimateFour = NULL; /* */ return 0; }
const
scalars to a function that uses them as non- const
parameters, since it cannot change the original values ​​of scalar variables in any way. ^uint64_t const *
instead of const uint64_t *
, since both of these declarations lead to exactly the same result, but reading your ad from right to left becomes more convenient if the const
qualifier follows the type. ^type *name
, not type* name
and certainly not type * name
because when we add const
, the pointer is attached to the next qualifier, not the previous one. For example: uint64_t const* const* evenMoreFour; /* const */
uint64_t const *const *evenMoreFour; /* const . */
^const
checked only at compile time; it does not change the program’s behavior only if you don’t manage to break the restrictions imposed by const
(no more than changing any other value would change the behavior of your program), but it probably will not work as you expect. Also: your compiler can place immutable data in read-only code segments, and trying to bypass these const
blocks can lead to undefined behavior. ^restrict
keyword in the memcpy()
prototype. restrict
means “this pointer data does not overlap with other data in the current scope”, which determines how memcpy()
plans to process its parameters.memmove()
from, you need to use the memmove()
function, its prototype does not contain restrict
qualifiers. void *memmove(void *dst, const void *src, size_t len);
^Source: https://habr.com/ru/post/301332/
All Articles