David542
David542

Reputation: 110382

const param vs non-const param

Let's take the following two function:

#include<stdio.h>

void my_print1(char* str) {
    // str = "OK!";
    printf("%s\n", str);
}

void my_print2(char* const str) {
    // str = "OK!";
    printf("%s\n", str);
}

They both produce the same assembly:

enter image description here

How then is the const-ness enforced here? For example, if I un-comment str = "OK!; it will of course work in the first function call but not the second (error: assignment of read-only parameter ‘str’).

Is the const-ness of a local variable just a compiler construct, and it is responsible for checking that, or how does it work if the assembly for the two functions is the same? Note: this is C only, not C++ (as I think they treat const different).

Upvotes: 1

Views: 448

Answers (3)

jwdonahue
jwdonahue

Reputation: 6669

The compiler enforces const by refusing to generate non-compliant code. If the my_print2 source had attempted to modify str, the compiler would have issued an error message.

With respect to your code however:

void my_print2(char* const str)...

It's kind of pointless as it can only limit what the function can do with the value of the pointer itself, not what it can do to memory it points to, so:

void my_print2(char* const str)
{
    str++; // Not allowed, compile time error.
    *str = 'A'; // Okay.
}

Functions that do not need to modify the content of what is pointed to, should be declared as const <type> * to give callers confidence that your function, print in this case, won't change their data:

void my_print3(const char* str)
{
    str++; // Okay
    *str = 'A'; // Not allowed, compile time error.
}

void my_print4(char const* str)
{
    str++; // Okay
    *str = 'A'; // Not allowed, compile time error.
}

Upvotes: 0

Nate Eldredge
Nate Eldredge

Reputation: 58518

Correct, on most implementations it's just a compiler construct.

On a typical mainstream OS implementation, there is a way to place const objects having static storage duration in memory that is actually write-protected by the CPU's memory-management unit (MMU), e.g. a .text or .rodata section. Then attempts to write it, if not prevented at compile time, will cause a trap at runtime. But hardware write protection applies to large blocks of memory (e.g. whole pages). There is no good way to do this with auto objects, such as local variables or function parameters, which live in stack memory or in registers. On the stack, since they are mixed in with non-const variables, hardware write protection is not fine-grained enough to apply to them, and in any case would be very expensive to be continually changing (it needs a call to the operating system). And registers on most machines cannot be made read-only at all.

Since there is no good way to protect them at runtime, compilers often generate the exact same code for const auto objects as for non-const.

You might see differences in some cases, since const informs the compiler that the object's value is not supposed to change, and therefore the compiler can assume that it does not. For instance, if you pass a pointer to a non-const object to another function, the compiler has to assume that the value of the object may have been changed, and will reload it from memory after each function call. But a const object may have its value cached in a register across the functionn call, or just optimized into an immediate constant if possible.

Upvotes: 4

Barmar
Barmar

Reputation: 781907

The const qualifier on function parameters and other local variables normally has no effect on the generated code. It just tells the compiler to prevent assigning to the variable.

Theoretically, it could generate code that prevents modifying the variable through other means. E.g. if you had

void my_print2(char* const str) {
    *(char *)&str = "OK!";
    printf("%s\n", str);
}

The assignment causes undefined behavior, but won't cause an error (although the compiler might warn about casting away constness). But the compiler could theoretically store str in memory that's marked read-only; in that case, the assignment would cause a segmentation fault. This would not normally be done for function parameters because it's difficult to reconcile that with using the stack for automatic data. (Nate Eldredge's answer explains this better.)

Upvotes: 3

Related Questions