Duplicate Const Qualifier Allowed in C But Not in C++

Duplicate const qualifier allowed in C but not in C++?

C99 §6.7.3/4:

If the same qualifier appears more than once in the same specifier-qualifier-list, either directly or via one or more typedef s, the behavior is the same as if it appeared only once.

Yes, that is valid C99, and your discovery is correct.

What is the purpose of const qualifier if I can modify it through a pointer in C?

The reason you could modify the value is because you did a pointer typecast that stripped off the constness:

int *p = (int *)&a;

This typecasts a const int* (namely &a) to an int *, allowing you to freely modify the variable. Normally the compiler would warn you about this, but the explicit typecast suppressed the warning.

The main rationale behind const at all is to prevent you from accidentally modifying something that you promised not to. It's not sacrosanct, as you've seen, and you can cast away constness with impunity, much in the same way that you can do other unsafe things like converting pointers to integers or vice-versa. The idea is that you should try your best not to mess with const, and the compiler will warn you if you do. Of course, adding in a cast tells the compiler "I know what I'm doing," and so in your case the above doesn't generate any sort of warnings.

Is it legal to have multiple const qualifiers?

I think this is ill-formed. [dcl.type]/2

As a general rule, at most one defining-type-specifier is allowed in
the complete decl-specifier-seq of a declaration or in a
defining-type-specifier-seq, and at most one type-specifier is allowed
in a type-specifier-seq. The only exceptions to this rule are the
following:

  • const can be combined with any type specifier except itself.
  • ...

So it's not allowed to repeat const literally like const const int a = 5;. (PS redundant cv-qualifications introduced by typedefs are allowed and would be ignored).

Why can a const value not be changed in C++ but in C?

Given your error, the actual program must have been the following:

#include<stdio.h>
main()
{
const int i=5;
int *p;
p=&i;
*p=8;
printf("%d",i);
}

This produces a warning with gcc:

warning: assignment discards 'const' qualifier from pointer target type

and an error with g++:

error: invalid conversion from 'const int*' to 'int*'

So, let's change the title of your question to a better one:

Why does C allow conversion from const int * to int *, but C++ doesn't?

The reason why one gives a warning and another gives an error is not because one allows you to discard const qualifier and the other doesn't. It's merely because the C standard leaves such incorrect actions as undefined behavior, while the C++ standard specifically marks it as an error. Either way, doing this is wrong.

You can read this similar question asking why this is possible in C.

Is it possible to change value of variable assigned with const qualifier

That's simply how C works.

As far as I know variables declared with a const qualifier are stored in a read only mode

Declaring a variable as constant only prevents you from modifying it via that variable. There's nothing that prevents you from modifying it via a pointer to non-const, pointing at that variable.

Please suggest me a method in which I can't change value of a variable even by using pointers to it.

Sorry to say it, but the answer is choose another language. If you're going to code C, you have to deal with these kind of stuff often.

One thing to do is to instead declare a pointer to const. You can change int * ptr = &var to const int * ptr = &var. This would make impossible to modify the value in var via ptr, but that is about as far as protection in C goes. You can never write protect the data itself. You can only make sure that the access vectors (my own term in lack of a better one) are forbidden to change the data.

You can also use #define. It has it's pros and cons, but it does the job.

Another thing you can do to make it harder (but not impossible) to modify the data is to hide data structures. You can see an example of that in this question

A very quirky and certainly NOT recommended way is to use string literals. Here is an example:

const uint32_t const * ptr = (uint32_t*)"\x00\x01\x00\x00";

This will give you a "constant" that has the value 256. At least on my machine. I suspect that you will have to know if your machine is big or little endian. It has three different protections. You cannot make this pointer to point to anything else, you cannot use the pointer to change what it is pointing at, and the data is probably in read only memory. Compilers usually puts string literals in read only memory, but they are not required to do so. I suspect that this is undefined behavior. In short, don't do this. It's just a demonstration of what you might have to do in C to get the code relatively but not completely safe.

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++11 constexpr), but a const 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 write const 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.

c - const pointer to const data (gcc - duplicate 'const' declaration specifier)

You seems have some miss understanding about const, for example.

#include <stdio.h>
int main()
{
int a=123, b=456;
const int *pa = &a;
pa = &b; //legal
*pa = 4; //illegal
printf("a=%d\n", a);
return 0;
}

gcc will give an error saying that

x.c: In function ‘main’:
x.c:8:9: error: assignment of read-only location ‘*pa’
*pa = 4;
^

for your purpose, if I understand correctly, you should define the function as

const struct userData * getEEPROMDataAtIndex(uint32_t uidIndex);
//this const declare the returned pointer point to something cannot be changed

then when you initiate your constant pointer by call this function

const struct userData * const myp = getEEPROMDataAtIndex(index);
// ^ this declare the pointer itself (myp) cannot be changed

Hope this helps.

Deep Analysis of Const Qualifier in C

The keyword const indicates a variable that is read-only (i.e., cannot be changed at run-time). It does not indicate a compile-time constant. Therefore, all of the usual attributes of variables apply; specifically, it is allocated addressable storage space.

Unlike with #define, your constant is not necessarily inlined by the compiler. Rather, the compiler will create a symbol corresponding to your const declaration in the object file so that it can be accessed from other code files—remember that const objects have external linkage by default in C (although some compilers will still inline the constant value within the file where it is defined).

The reason the code snippet that you posted "works" is because the unary operator & can be applied to any lvalue, which includes a const object. Though the behavior here is undefined, I suspect that your compiler is detecting this usage and ensuring that your const declaration is given address space, and therefore not inlining it, even within the file it is declared.

EDIT: Also see: http://publications.gbdirect.co.uk/c_book/chapter8/const_and_volatile.html

Let's look at what is meant when const
is used. It's really quite simple:
const means that something is not
modifiable, so a data object that is
declared with const as a part of its
type specification must not be
assigned to in any way during the run
of a program. It is very likely that
the definition of the object will
contain an initializer (otherwise,
since you can't assign to it, how
would it ever get a value?), but this
is not always the case. For example,
if you were accessing a hardware port
at a fixed memory address and promised
only to read from it, then it would be
declared to be const but not
initialized.

Taking the address of a
data object of a type which isn't
const and putting it into a pointer to
the const-qualified version of the
same type is both safe and explicitly
permitted; you will be able to use the
pointer to inspect the object, but not
modify it. Putting the address of a
const type into a pointer to the
unqualified type is much more
dangerous and consequently prohibited
(although you can get around this by
using a cast). For example...

Why we cannot create an array with a constant in c

In C:

20 is a constant.

unsigned int const size_of_list is not a constant.

Title: "Why we cannot create an array with a constant in c" does not apply to this code.

const char* list_of_words[size_of_list] = {"Some", "Array"}; // Bad

An issue here (and the error message) is why a VLA cannot be initialized. That is answered here.

With a constant, array initialization works fine.

const char* list_of_words[20] = {"Some", "Array"};  // Good

Another issue is mistaking that const makes an object a constant. It does not.


Alternative C code

int main(){
// unsigned int const size_of_list = 20;
#define size_of_list 20u

const char* list_of_words[size_of_list] = {"Some", "Array"};
for (unsigned int writing_index = 0; writing_index < size_of_list; writing_index ++)
;
return 0;
}

If you can specify the size in the array itself, then you can get it's size with the sizeof operator. This may be better suited for having the compiler count up the size instead manual counting. When not using C99 VLAs, sizeof also yields a compile-time constant.

#include <stddef.h> /* size_t */
int main(void) {
const char* list_of_words[] = {"Some", "Array"};
const char list_of_char[sizeof list_of_words
/ sizeof *list_of_words] = {'S','A'};
const size_t size_of_list
= sizeof list_of_words / sizeof *list_of_words;
for (size_t writing_index = 0;
writing_index < size_of_list; writing_index ++);
return 0;
}


Related Topics



Leave a reply



Submit