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; bobaFour pointing to bobaFour ? 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