简体   繁体   中英

changing const value in C

I find that in the following code snippet

const int i = 2;  
const int* ptr1= &i;  
int* ptr2 = (int*)ptr1;  
*ptr2 =3;

i 's value changes to 3. What I could like to know is why is this allowed. What are the situations in which this could become helpful?

It's allowed because you have overruled the constness of ptr1 by casting it to a non-const pointer. This is why casts can be very dangerous.

Note that some compilers, such as GCC, will not allow you to cast away const status like this.

You have broken the constantness guarantee by playing pointer tricks. This is not guaranteed to work all the time and could invoke almost any behavior depending on the system/OS/compiler that you throw it at.

Don't do that.

Or at least don't do that unless you really know what you are doing and even then understand that it is not in the least portable.

"Allowed" is the opposed of "prevented", but it's also the opposite of "forbidden". You've seen that modifying your const object isn't prevented, but that doesn't exactly mean it's allowed.

Modifying a const object isn't "allowed" in the sense of being "permitted". The behaviour of your program is not defined by the standard (see 6.7.3/5). It just so happens that on your implementation, on that run, you saw the value 3. On another implementation or on another day, you might see a different result.

However, it's not "prevented", because with the way C casts work, detecting it at compile time is a halting problem. Detecting it at runtime requires extra checks at all memory accesses. The standard is designed not to impose a lot of overhead on implementations.

The reason casting away const is supported at all, is because if you have a const pointer to a non-const object, the language allows you (in both senses) to modify that object. To do so you need to get rid of the const qualifier. The consequence of this is that programmers can also discard const qualifiers from pointers to objects which actually are const.

Here's a (slightly silly) example of code which discards a const qualifier for that reason:

typedef struct {
    const char *stringdata;
    int refcount;
} atom;

// returns const, because clients aren't allowed to directly modify atoms,
// just read them
const atom *getAtom(const char *s) {
    atom *a = lookup_in_global_collection_of_atoms(s);
    if (a == 0) {
        // error-handling omitted
        atom *a = malloc(sizeof(atom));
        a->stringdata = strdup(s);
        a->refcount = 1;
        insert_in_global_collection_of_atoms(a);
    } else {
        a->refcount++;
    }
    return a;
}

// takes const, because that's what the client has
void derefAtom(const atom *a) {
    atom *tmp = (atom*)a;
    --(tmp->refcount);
    if (tmp->refcount == 0) {
        remove_from_global_collection_of_atoms(a);
        free(atom->stringdata);
        free(atom);
    }
}
void refAtom(const atom *a) {
    ++(((atom*) a)->refcount);
}

It's silly because a better design would be to forward-declare atom , to make pointers to it completely opaque, and provide a function to access the stringdata. But C doesn't require that you encapsulate everything, it allows you to return pointers to fully-defined types, and it wants to support this kind of const usage to present a read-only view of an object that's "really" modifiable.

const really means "readonly".

As you've found out, the value of const objects can change, but you have to use devious methods to do it. And while using these devious methods you invoke Undefined Behaviour .

It works because you have explicitly cast the const ness of the pointee away. While ptr1 is a pointer to a const int, ptr2 is a pointer to an int, so it's pointee is changeable.

There are very few good reasons to do this, but you can just about find a case where it avoids code duplication. For instance:

const char* letter_at(char* input, int position)
{
    ... stuff ...
    return &found_char;
}

char* editable_letter_at(char* input, int position)
{
    return (char*)(letter_at(input, position));
}

(Example somewhat mangled from the C++ example in Item 3 of Effective C++ 3rd)

If you're going to cast away constness in a C++ program, please use a more C++ style of casting:

int *ptr2 = const_cast<int*>(ptr1);

If you do run into problems related to this type of casting (you will, you always do) then you can find where it happened much rapidly by searching for "const_cast" rather than trying every combination under the sun. Besides, it'll help others our who may or may not come after you.

There are only a few situations where I could see this being helpful. The majority of them are corner cases. I'd avoid this at all costs if you're developing in C++.

C casts tell the compiler that you know what you're doing, and that you'll make sure it all works in the end. If you use them without understanding precisely what you're doing, you can get into trouble.

In this case, the compiler is perfectly within its rights to put i in read-only memory, so that this code will crash when run. Alternately, it might work as you saw. The Standard specifies this as undefined behavior, so literally anything might happen.

C was originally designed to write Unix in, and deliberately gives the programmer a great deal of freedom in manipulating data, since in OS writing it's often very useful to write highly implementation-specific code that does things that would be unsafe in any other context. In regular application code, casting should be done with caution.

And don't use C-style casts in C++. C++ has its own family of casts that are easy to search for in code and which often specify more what the cast is actually doing. In this particular case, you'd use const_cast , which shows exactly what you're doing and is easy to find.

Because C knows that programmers always know what they are doing and always do the right thing. You can even cast it like:

void* ptr2 = (int(*)(void(*)(char*), int[])) ptr1;
(*(int*)ptr2) = 3;

and it will not even complain at all.

The statement const int i = 2; means that the symbol/variable i holds value 2 , and the value cannot be changed using i ; but there is no guarantee that the value cannot be changed by some other means (as in OP's example).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM