locked
conversion to void * in C and C++ RRS feed

  • Question

  • If I compile the following (with warning level 4) in a .cpp file,

        {
        const int ***pV1 = 0;
        void *pV2 = pV1;
        }

    the C++ compiler does not complain, but if I put the same code in a .C file, the C compiler issues the following warning:

        warning C4090: 'initializing': different 'const' qualifiers

    I don't understand why the presence of "const" at that depth should affect the conversion to void*. I think the behavior of the C++ compiler is correct, but the behavior of the C compiler is puzzling.

    Now, if I add a "const" immediately before the last asterisk, 

        {
        const int **const*pV1 = 0;
        void *pV2 = pV1;
        }

    I get an error from the C++ compiler:

        error C2440: 'initializing': cannot convert from 'const int **const *' to 'void *'

    which is exactly what I would expect.

    In other words, my understanding is that the only "const" that should matter in this case is the one that is (or is not) present immediately before the last asterisk. The "const" at the beginning of the first declaration (followed by three asterisks) should be irrelevant. My variable pV2 is a pointer to non-const void. I should be able to convert any pointer to a non-const "thing" to a pointer to non-const void.  My variable pV1 is a pointer to non-const pointer to non-const pointer to ... const int, so it *is* a pointer to a non-const "thing". 

    Is this a bug in the C compiler, or am I missing some rule that is specific to C?






    Saturday, September 7, 2019 10:59 PM

Answers

  • Given the description of C4090, this may be a bug. You should submit feedback to the compiler team.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    • Marked as answer by alessandrot60 Sunday, September 8, 2019 4:09 AM
    Sunday, September 8, 2019 12:48 AM
  • I would be surprised if any C++ compiler warns at [1] because that is technically not a problem, since you are implicitly casting a pointer to something into a pointer to void. There is no loss of cv in the first level of indirection. Also, it isn't as if you can do anything via the pointer to void since that is telling the compiler "this points to something, but I don't know what."

    None of the easily accessible main compilers, GCC, Clang and VC, actually warn at [1] in C++ or even emit an error. In C mode GCC and Clang don't warn at all but VC does.

    While [2] is invalid C++:

    int **pp = static_cast<int **>(pv);

    is valid C++ and compiles. What's more, it only requires a static cast.

    What's more, the documentation for C4090 itself states that it is the C equivalent of C++'s C2440. So I think that it is wrong to emit C4090 where C++ wouldn't emit C2440 if you compile the code as C++.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    • Edited by Darran Rowe Monday, September 9, 2019 12:27 AM
    • Marked as answer by alessandrot60 Monday, September 9, 2019 12:40 AM
    Monday, September 9, 2019 12:26 AM

All replies

  • Given the description of C4090, this may be a bug. You should submit feedback to the compiler team.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    • Marked as answer by alessandrot60 Sunday, September 8, 2019 4:09 AM
    Sunday, September 8, 2019 12:48 AM
  • Possibly because C++ compiler doesn't allow an implicit conversion from void* to int*** (so the construct you show doesn't open up a way to violate const correctness without a cast), while C compiler does.

    Igor Tandetnik

    Sunday, September 8, 2019 4:31 AM
  • Igor, the C++ compiler does exactly what I was expecting. It's the warning from the C compiler that surprises me (I was not expecting it). Note that the warning disappears if I remove the "const". I thought that a "const" in that particular position should not have any effect on the implicit conversion.


    Sunday, September 8, 2019 5:03 AM
  • With C, you could do this:

    const int n = 42;
    const int* p2const = &n;
    void* pv = p2const;  // [1]
    int* p = pv;  // [2] Valid C, not valid C++
    *p = 84;  // modifies n - oops
    

    This example violates const correctness with only implicit casts. [1] is the only spot where a warning about that could be reasonably issued.

    C++ won't allow [2], so [1] doesn't warrant a warning.


    Igor Tandetnik

    Sunday, September 8, 2019 3:48 PM
  • I understand what you are saying, but in my case the "const" is followed by more than one asterisk.  It's

    {

        const int ***pV1 = 0;
        void *pV2 = pV1;
    }

    vs.

    {
        int ***pV1 = 0;
        void *pV2 = pV1;
    }

    In the first case I get the warning, in the second case I don't get the warning.

    In both cases, the variable pointed to by pV1 is *not* const, therefore considerations about const-correctness should not apply, I think.

    I surely would expect a warning in the following case, where the "const" precedes the last asterisk:

    {

        int **const *pV1 = 0;
        void *pV2 = pV1;
    }

    but not in the other cases.


    Sunday, September 8, 2019 6:24 PM
  • Well, the output of Clang-cl for:

    int wmain()
    {
    	const int *** i;
    	void *v = i;
    	return 0;
    }

    is:

    1>------ Build started: Project: meh, Configuration: Debug x64 ------
    1>main.cpp(5,8): warning :  unused variable 'v' [-Wunused-variable]
    1>main.cpp(5,12): warning :  variable 'i' is uninitialized when used here [-Wuninitialized]
    1>main.cpp(4,17): message :  initialize the variable 'i' to silence this warning
    1>Done building project "meh.vcxproj".
    ========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

    So it doesn't warn.

    But compiling:

    int wmain()
    {
    	int ** const* i;
    	void *v = i;
    	return 0;
    }

    with Clang-cl does warn:

    1>------ Build started: Project: meh, Configuration: Debug x64 ------
    1>main.cpp(5,8): warning :  initializing 'void *' with an expression of type 'int **const *' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers]
    1>main.cpp(5,8): warning :  unused variable 'v' [-Wunused-variable]
    1>main.cpp(5,12): warning :  variable 'i' is uninitialized when used here [-Wuninitialized]
    1>main.cpp(4,23): message :  initialize the variable 'i' to silence this warning
    1>Done building project "meh.vcxproj".
    ========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

    So the Visual C++ compiler compiling in C mode and producing warning C4090 does seem to be a mistake.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    Sunday, September 8, 2019 6:58 PM
  • He expressed the question badly, but would you expect C++ to warn or even produce an error with:

    const int ** i;
    void * v;
    v = i;

    This is what the question was really about.

    In this case, you are assigning a pointer to a pointer to a const int to a pointer to void.

    Visual C++, while compiling a C source file produces a warning for this exact situation.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    • Edited by Darran Rowe Sunday, September 8, 2019 7:02 PM
    Sunday, September 8, 2019 7:00 PM
  • I understand what you are saying, but in my case the "const" is followed by more than one asterisk.

    Same thing. I was just too lazy to write out long chains of indirections. Let me add another layer:

    const int n = 42;
    const int* p2const = &n;
    const int** pp2const = &p2const;
    
    void* pv = pp2const;  // [1]
    
    int** pp = pv; // [2] Valid C, not valid C++
    **pp = 84;  // modifies n - oops
    

    Expanding to three or more levels of stars is left as an exercise for the reader.


    Igor Tandetnik

    Sunday, September 8, 2019 7:29 PM
  • I would be surprised if any C++ compiler warns at [1] because that is technically not a problem, since you are implicitly casting a pointer to something into a pointer to void. There is no loss of cv in the first level of indirection. Also, it isn't as if you can do anything via the pointer to void since that is telling the compiler "this points to something, but I don't know what."

    None of the easily accessible main compilers, GCC, Clang and VC, actually warn at [1] in C++ or even emit an error. In C mode GCC and Clang don't warn at all but VC does.

    While [2] is invalid C++:

    int **pp = static_cast<int **>(pv);

    is valid C++ and compiles. What's more, it only requires a static cast.

    What's more, the documentation for C4090 itself states that it is the C equivalent of C++'s C2440. So I think that it is wrong to emit C4090 where C++ wouldn't emit C2440 if you compile the code as C++.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    • Edited by Darran Rowe Monday, September 9, 2019 12:27 AM
    • Marked as answer by alessandrot60 Monday, September 9, 2019 12:40 AM
    Monday, September 9, 2019 12:26 AM