2

In C, a non const object implicitly converts, without warning, to its const counterpart, so you can write a function, and call it as:

void foo( const char *p );
char bar[] = "bar";
foo( bar );

However, if the function is to be called via a pointer, it seems you can not be const correct without getting a warning:

void foo( void (*callback)(char *p) );
void bar( const char *p );
foo( bar );

bar does not need to modify the string pointed to, and so for proper const correctness, should be declared with const, yet passing it as a callback to a function that is willing to give the callback write access, generates a warning that the pointers are not compatible.

Is this an oversight/defect in the C standard?

psusi
  • 123
  • 5
  • 2
    You're talking about a programming language that will happily let you write past the end of an array. – Robert Harvey Apr 21 '16 at 01:58
  • Related: http://programmers.stackexchange.com/q/204500 – Robert Harvey Apr 21 '16 at 02:31
  • 1
    People have been working on the C standard for nearly 30 years. You can assume that they know about this and thus you can assume it is expected and intentional. – Gort the Robot Apr 21 '16 at 05:11
  • Curiously enough, MSVC doesn't give a warning in your case (and does when the `const` is moved from `bar` to `foo`). – ach Apr 21 '16 at 10:53
  • @AndreyChernyakhovskiy, that actually makes sense, a function with a const parameter can be used as an instance of a function prototype without a const parameter, but not the other way around. In English, if the prototype allows changes but the actual function doesn't, it's OK, but if the prototype doesn't allow changes but the actual function does, it's wrong. – acelent Apr 21 '16 at 11:09
  • @acelent, Yes, it makes sense, but is that technically legal? Sometimes we prefer legal purity. It is very annoying when code that compiles without a single warning on MSVC, doesn't compile at all on GCC (or produces a huge output of warnings) simply because MSVC didn't care to give you a warning. – ach Apr 21 '16 at 12:01
  • @AndreyChernyakhovskiy, did you set it for the highest warning level? I originally got the warning from a compiler for an embedded microcontroller, but I tested it with gcc and with -Wall, it also complains. I filed a bug report and they said the behavior is correct according to the standard. – psusi Apr 21 '16 at 22:52
  • 1
    @StevenBurnap, intentionally stupid is still a defect . I found some hints online that this is one of the silly things that the C standard committee has still not got right, but C++ has. – psusi Apr 21 '16 at 22:54
  • @psusi, Yeah, compiled with `/Wall`. – ach Apr 22 '16 at 09:50

1 Answers1

2

The fact that you can use a wrapper to avoid the warning should show that there isn't a deep technical reason:

void bar(const char *p) { /* ... */ }
void bar_w(char *p) { bar(p); }  /* wrapper */

foo(bar_w);  /* instead of foo(bar) */

This is based on then well known fact that you can use a pointer-to-T (for any type T) where a pointer-to-const-T is expected (as in your first example).

The warning is due to §6.7.5.3.15 - Function declarators (from ISO/IEC 9899:TC3):

For two function types to be compatible...

[cut]

Moreover, the parameter type lists, if both are present, shall agree in the number of parameters and in use of the ellipsis terminator; corresponding parameters shall have compatible types.

and const char *p is not compatible with char *p.

Anyway the compiler issues just a warning (not an error): maybe the programmer is using the wrong function (with a similar signature) and the warning can help to identify the situation.

If everything is ok an explicit cast / a wrapper function can rapidly resolve the "nuisance".


EDIT

it appears that char *p is compatible with const char *p, just not the other way around

char *p can be implicitly converted to const char *p (§6.3.2.3):

For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to the q-qualified version of the type; the values stored in the original and converted pointers shall compare equal

(in other words, const, volatile and restrict qualifiers can be added. The original pointer and the result compare equal. See also Implicit conversions).

E.g.

char n;
const char *p = &n;  /*   &n has type char *   */

This doesn't mean that const char * (a pointer to const-qualified char) is compatible with char * (pointer to char):

For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types

(§6.7.5.1)

The pointer are identically qualified (they aren't qualified!) but they aren't pointer to compatible types (const char is not compatible with a char).

manlio
  • 4,166
  • 3
  • 23
  • 35
  • Excellent citation, but *why* are they not "compatible"? As shown in my first example, it appears that `char *p` *is* compatible with `const char *p`, just not the other way around. – psusi Apr 21 '16 at 22:56
  • @psusi Please, take a look at the edit – manlio Apr 22 '16 at 09:13
  • It appears that const is a qualifier, so the const version is const qualified, but 6.7.5.1 requires that they be both compatible *and* identically qualified, so that seems to be the problematic rule. – psusi Apr 22 '16 at 20:35