-1

When you intend to not change the value of a variable you declare it as a const in C language.

#include <stdio.h>
int c1()
{
  int *a;
  const int b;

  a=&b;

  printf("*a=%d, b=%d\n",*a, b);

  *a = *a + 1;
  //b = b + 1; 

  printf("*a=%d, b=%d\n", *a, b);

//return ;
}

main()
{       
        printf("C=%d\n", c1());
}

If you execute b = b + 1; then the compiler complains:

c1.c:12:5: error: cannot assign to variable 'b' with const-qualified type 'const int'
  b = b + 1; 
  ~ ^
c1.c:5:13: note: variable 'b' declared const here
  const int b;
  ~~~~~~~~~~^

That's what is expected. But if we declare a pointer variable and that points to the const variable, and you increment the "value" of what the pointer variable is pointing to, then the compiler does not complain. Why?

KjD
  • 5
  • 3
  • Because it doesn't point to const. – tkausl Dec 15 '17 at 17:41
  • "then the compiler does not complain"? Really? I find it hard to believe. – AnT stands with Russia Dec 15 '17 at 17:45
  • I tried editing the code example to correct the syntax but am not allowed to make edits under 6 characters....anyways the short answer is that's how C works. Powerful and potentially dangerous. Because you are referencing the value by reference and not the actual value, the compiler cannot reliably detected what the reference is to, there for you can change the values. – tatmanblue Dec 15 '17 at 17:46

3 Answers3

3

a = &b is not legal:

As others have pointed out, a = &b is not legal C. However, this is not because you are not allowed to convert a const int * (pointer to constant int) to an int * (pointer to int); it is because you are not allowed to do it implicitly. You can do it if you explicitly indicate you want to convert it, by using a cast. (If your compiler does not give you a warning message when you do this without a cast, then you should turn on more warning messages for your compiler. If it still does not give you a warning, it is a bad compiler.)

Specifically, clause 6.5.16.1, paragraph 1, of the C 2011 standard [draft N1570] lists constraints that must be satisfied for assignment statements. In regard to this situation, they essentially say that a const int * can only be assigned to a const int * and not to an int *.

a = (int *) &b is legal:

However, you can write a = (int *) & b. This satisfies the constraints for casts, listed in 6.5.4 1. And 6.3.2.3 says you may convert a pointer to an object type to a pointer to any other object type, as long as pointer is correctly aligned for the destination type.

However, you cannot then use the pointer; you cannot use *a = 3 because then you are trying to change an object that was defined with const. 6.7.3 6 says the behavior when doing this is undefined.

const is flexible due to C history:

Why then does C allow it? Well, C was developed and used for many years before const was added to the language. It had to be retrofitted in, and this required allowing existing code to work and not breaking other features of the language. One characteristic of const is that although one routine might be passed a const int *, the actual object it is pointing to was not necessarily defined with const. For example, I can make int x = 7 and then pass &x to a routine whose parameter is const int *p. This means that routine will not change the thing p points to, but it does not mean other routines will not change it.

One problem with that is shown by the standard strchr function. This is declared as char *strchr(const char *s, int c);. It is declared with the const char *s parameter because we want to be able to pass it pointers to const char without complaint. Since the parameter is const char *, we are allowed to pass either const char * or char *. And this makes sense because strchr only looks for things in strings; it does not change the strings.

Suppose I have the non-constant string char foo[] = "abcdef". I can use strchr to search for 'd' in it by using char *p = strchr(s, 'd'). But then the result I get back is char *, not const char *. strchr accepted a const char * but then changed it to a char *!

In this situation, that works. I passed a char *, it was automatically converted to const char * for the function call, and I got back a char * which is legal for my non-const string. But this funny business where const char * pointers have to be changed into char * is really a defect in the C language caused by its history.

Round-trips of adding const and removing it are legal:

Here is another situation:

  • I write a library for other programs to use.

  • One routine in the library prepares some data to be used later, say data in some struct foo. This routine allocates memory, calculates the data, and returns a const struct foo *. It returns a const pointer because the user of my library should not change the data.

  • The user calls this routine, uses the data for a while in their program, and eventually calls another routine in my library to free the data.

  • The routine to free the data must accept a const struct foo *, because that is what the user has. If it accepted a struct foo *, the user would get an error when passing a const struct foo * to a struct foo * parameter. But, inside the routine, I convert the const struct foo * to a non-constant struct foo * so that I can pass it to free, which requires a non-constant pointer.

This is legal in C because you are allowed to convert a pointer to any object to a pointer to any other kind of object (provided alignment requirements are satisfied) and back, and when you convert it back, 6.3.2.3 7 guarantees that you have the original pointer back. So, we are allowed to convert a struct foo * pointer to const struct foo *, let the user use it for a while, then accept the const struct foo * in our free-the-object routine, convert it back to the original pointer, which is struct foo *, and then pass it to free.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
1
int *a;
const int b=5;

a=&b;

This invokes Undefined Behaviour.

Quoting from http://c0x.coding-guidelines.com/6.7.3.html

6.7.3 Type qualifiers

1480 If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.

So it literally depends on what your compiler does to it.

My compiler gave

[Error] invalid conversion from 'const int*' to 'int*' [-fpermissive]

But yours gave different result because your compiler just tried to make sense of it. Since it's Undefined Behavior, there's no point trying to make sense of it

Community
  • 1
  • 1
Arun A S
  • 6,421
  • 4
  • 29
  • 43
  • gcc compiler for c++ version 4.2.1 with Apple LLVM version 9.0.0 (clang-900.0.39.2) Target: x86_64-apple-darwin16.7.0 Thread model: posix still compiles this, gives warnings, but no error. Further I have not asked the compiler to consider warns as error. – KjD Dec 15 '17 at 17:49
  • @Kongkon Jyoti Dutta: Until you learn to properly interpret compiler diagnostic messages yourself (i.e. realize which warnings are actually language "errors"), you should use `-pedantic-errors` flag. This flag is not "consider warns as error" flag. This flag is "consider errors as errors" flag. – AnT stands with Russia Dec 15 '17 at 17:55
  • @KongkonJyotiDutta read edited answer. It invokes undefined behavior – Arun A S Dec 15 '17 at 18:01
  • @Ant this is a very basic question, and I don't think the apple gcc compiler is dumb enough to miss this error. Irrespective of whether I use any special compiler flag, the gcc compiler should take care of these kind of warnings. – KjD Dec 15 '17 at 18:06
  • >> "It invokes undefined behavior", this is something I agree. But that's not the real reason. – KjD Dec 15 '17 at 18:08
  • @KongkonJyotiDutta what do you mean? Don't try to make reasons from undefined behavior. Your compiler gave different result because that's how it was implemented, others might give different. – Arun A S Dec 15 '17 at 18:13
  • @Kongkon Jyoti Dutta: It did not "miss" this error. It gave you a *diagnostic message*, exactly as required by the language standard. It is you who *missed* that error because you haven't learned yet to tell which diagnostic message are hard *errors* and which diagnostic message are mere innocent *warnings*. As I said before, this is reported as a "warning" just to support some ancient legacy C code. If you don't care to compile ancient code, then use `-pedantic-errors`. – AnT stands with Russia Dec 15 '17 at 18:14
  • BTW, this is not a discussion, it's the hard basics of the language. Everything I said about it is mandatory learning, if you care to become a competent C programmer. This all is well covered on SO. – AnT stands with Russia Dec 15 '17 at 18:15
  • "This invokes Undefined Behaviour" is misleading. The code contains a constraint violation, i.e. it is not valid C code. If some compiler agrees to compile it, it would indeed invoke UB, since C language does not define behavior of invalid code. But the same can be said about, say, a Pascal program or Tolstoy's "War and Peace" - if your C compiler somehow agrees to compile it, the behavior is undefined. – AnT stands with Russia Dec 15 '17 at 18:17
0

This

a = &b;

is not allowed in C. The code contains a constraint violation, i.e. it is not valid C code, an error. Any compliant C compiler will immediately issue a diagnostic message for this problem. If your compiler does not complain, your compiler is broken.

6.5.16.1 Simple assignment
Constraints
1 One of the following shall hold:
[...]
— the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
[...]

In your case the pointed type on the LHS does not have all the qualifiers of the pointed type on the RHS. Your code contains a constraint violation, i.e. a hard language error.

P.S. Your main() is invalid too. Should be int main(), or better int main(void).

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Both of them generates warnings, not error. So, I'm still legal. – KjD Dec 15 '17 at 17:46
  • @Kongkon Jyoti Dutta: That is false. There's no differentiation between "warnings" and "errors" in C language. There are only *diagnostic messages*. Your compiler simply decided to generate a "warning" for what is really an "error". It is your responsibility to figure that out. In many cases, your can configure your compiler to generate "errors" for "errors". In GCC That would be `-pedantic-errors` flag. – AnT stands with Russia Dec 15 '17 at 17:47
  • you (@AnT) said there's no differentiation between "warnings" and "errors" in C. Can't agree to this. In case of warning, the compiler goes ahead and produces the executable, but on errors it does not proceeds further, hard stop. – KjD Dec 15 '17 at 17:53
  • @KongkonJyotiDutta It is a warning only because the C compiler cannot totally discern from the context whether the cited statement is going to be a problem. The C compiler is drawing your attention to it so that you can apply some of your own thought to see if it's really a problem. So a "warning" could actually be an "error" as far as whether the code will function properly or not. If it doesn't function properly, then it really is an error. If a warning really was just simply "legal" and you interpret that as "no problem" then there would be no purpose behind warnings. – lurker Dec 15 '17 at 17:57
  • @Kongkon Jyoti Dutta: I'm talking about *C language*, not about your *compiler*. Your *compiler* is not C language, it is just a mere attempt to implement it to some degree. In *C language* there's no such concept as "warning" or "error", there's only *diagnostic message*. Once you get a language-mandated diagnostic message from your compiler, all bets are off. After that nobody cares whether your compiler "produces the executable" or not. Your program is not C, period. What your compiler produced in response to it has nothing to do with C language. – AnT stands with Russia Dec 15 '17 at 17:58
  • 1
    In C language your `a = &b` is not allowed. If your compiler produces a mere "warning" in response to that violation - it is quirk of your compiler. Learn to recognize it and live with it. If you find it difficult to recognize, then use `-pedantic-errors` which was implemented in GCC specifically to help people like you. It is not perfect, but it works sufficiently well. – AnT stands with Russia Dec 15 '17 at 18:01
  • Easy way to solve the warning/error problem - compile with the -Werror option, then everything is an error – Chris Turner Dec 15 '17 at 18:01
  • @Chris Turner: People don't normally want *everything* to be an error. People just want language *errors* to be errors. This is what `-pedantic-errors` (as opposed to `-Werror`) is for. – AnT stands with Russia Dec 15 '17 at 18:03
  • @lurker: No, this is an immediate hard error in C. No need to discern any "contexts". The only reason GCC in its default configuration issues a mere "warning" is legacy code. There are lots of pre-standard ancient legacy code that ignores const-correctness rules. That's the only reason this language error is reported as mere "warning". You will also get a mere "warning" for errors like `int *a = 42;`. Same reason. – AnT stands with Russia Dec 15 '17 at 18:06
  • @AnT I agree it's a C error according to the C standard. I was addressing the specific case of it being flagged as a warning in a specific compiler. My point would be, even in that case, the user needs to heed the warning. – lurker Dec 15 '17 at 18:21