I found out that using a C compiler the code below works but not with a C++ compiler. I understand that casting to void** is the correct usage but I can't understand why it compiles with the C compiler even if I use the void* (commented out).
#include <stdio.h>
int fn(void **arg)
{
int *pvalue = *(int**)arg;
*pvalue = 200;
return 0;
}
int main()
{
int value = 99;
int *pvalue = &value;
// fn((void *)&pvalue); // works only in C
// error C2664: 'int fn(void **)': cannot convert argument 1 from 'void *' to 'void **'
fn((void **)&pvalue); // correct, works for both C/C++
printf("%d", value);
return 0;
}
Can someone explain why this is the case?
In C there is allowed to assign a pointer of the type void * to a pointer of other type. This takes place in this call
fn((void *)&pvalue)
where the argument has the type void * that is assigned to the function parameter that has the type void **.
int fn(void **arg)
{
int *pvalue = *(int**)arg;
*pvalue = 200;
return 0;
}
However such an assignment in general is unsafe. For example the value of a pointer of the type void * can not be properly aligned to be assigned to a pointer of other type.
So it was decided to not allow such an assignment in C++ to make programs more safer.
I can't understand why it compiles with the C compiler even if I use the void* (commented out).
It compiles because void* is implicitly convertible to other pointers in C.
fn((void **)&pvalue); // correct, works for both C/C++
This may be well-formed because of the cast, the standard doesn't technically give explicit guarantee that conversion to void** and back yields the same address.
While this may be likely to work in practice, there is no reason to not use void* as the function argument instead, which does have the guarantee. As a bonus, you won't need the cast in the call. Like this:
int fn(void *arg);
fn(&pvalue); // correct, works for both C/C++
Of course, this is assuming type erasure is needed in the first place. Avoid void* when it is not needed.
For avoidance of doubt, there is nothing correct in
fn((void **)&pvalue);
It is just as incorrect as
fn((void *)&pvalue);
The correct way to use the API is to do
int fn(void **arg)
{
int *pvalue = (int *)*arg;
*(int *)pvalue = 200;
return 0;
}
or
int fn(void **arg)
{
*(int *)*arg = 200;
return 0;
}
with
int main()
{
int value = 99;
void *pvalue = (void*)&value;
fn(&pvalue);
printf("%d", value);
return 0;
}
You're not allowed to access an object using any other pointer type, other than the declared type, compatible type, or a character type. Furthermore, while void * is used as a generic pointer type to all sorts of objects in C, there is no generic pointer to a pointer type in C - other than void *!
And this is the reason why the void ** is almost always a sign of a design error in APIs - most usages are just wrong.
Related
What does this error message mean?
error: call of overloaded ‘setval(int)’ is ambiguous
huge.cpp:18: note: candidates are: void huge::setval(unsigned int)
huge.cpp:28: note: void huge::setval(const char*)
My code looks like this:
#include <iostream>
#define BYTES 8
using namespace std ;
class huge {
private:
unsigned char data[BYTES];
public:
void setval(unsigned int);
void setval(const char *);
};
void huge::setval(unsigned int t) {
for(int i = 0; i< BYTES ; i++) {
data[i] = t;
t = t >> 1;
}
}
void huge::setval(const char *s) {
for(int i = 0; i< BYTES ; i++)
data[i] = s[i];
}
int main() {
huge p;
p.setval(0);
return 0;
}
The literal 0 has two meanings in C++.
On the one hand, it is an integer with the value 0.
On the other hand, it is a null-pointer constant.
As your setval function can accept either an int or a char*, the compiler can not decide which overload you meant.
The easiest solution is to just cast the 0 to the right type.
Another option is to ensure the int overload is preferred, for example by making the other one a template:
class huge
{
private:
unsigned char data[BYTES];
public:
void setval(unsigned int);
template <class T> void setval(const T *); // not implemented
template <> void setval(const char*);
};
The solution is very simple if we consider the type of the constant value, which should be "unsigned int" instead of "int".
Instead of:
setval(0)
Use:
setval(0u)
The suffix "u" tell the compiler this is a unsigned integer. Then, no conversion would be needed, and the call will be unambiguous.
replace p.setval(0); with the following.
const unsigned int param = 0;
p.setval(param);
That way it knows for sure which type the constant 0 is.
Use
p.setval(static_cast<const char *>(0));
or
p.setval(static_cast<unsigned int>(0));
As indicated by the error, the type of 0 is int. This can just as easily be cast to an unsigned int or a const char *. By making the cast manually, you are telling the compiler which overload you want.
Cast the value so the compiler knows which function to call:
p.setval(static_cast<const char *>( 0 ));
Note, that you have a segmentation fault in your code after you get it to compile (depending on which function you really wanted to call).
That is ambiguous because a pointer is just an address, so an int can also be treated as a pointer – 0 (an int) can be converted to unsigned int or char * equally easily.
The short answer is to call p.setval() with something that's unambiguously one of the types it's implemented for: unsigned int or char *. p.setval(0U), p.setval((unsigned int)0), and p.setval((char *)0) will all compile.
It's generally a good idea to stay out of this situation in the first place, though, by not defining overloaded functions with such similar types.
An API uses void* to store untyped pointer offsets. It's a bit hacky, but okay whatever.
To express my offset arithmetic, I tried doing something like this
int main ()
{
void * foo;
foo = static_cast <int *> (nullptr) + 100;
static_cast <int * &> (foo) += 100;
}
The last line fails to compile (gcc)
x.cpp:7:28: error: invalid static_cast from type ‘void*’ to type ‘int*&’
The fix is simple:
foo = static_cast <int *> (foo) + 100;
But why isn't the first one allowed?
Before you answer "because the standard says so", why does the standard say so? Is the first method somehow dangerous? Or is it just an oversight?
It's not allowed for the same reason that int i; static_cast<long &>(l) = 3L; isn't allowed.
Sure, on a lot of implementations (where int and long have the same size, representation and alignment), it could work. But the rules for which casts are valid are mostly the same for all implementations, and clearly this could never work on platforms where int and long have different sizes, meaning it'd be impossible to allow accessing one as the other on those platforms.
Historically, there have been implementations on which void * and int * have different representations.
Later, after the standard stating that accessing void * as if it were an int * is invalid, implementations also started optimising on the assumption that valid programs do not do that:
void *f (void **ppv, int **ppi) {
void *result = *ppv;
*ppi = nullptr;
return result;
}
The implementation is allowed to optimise this to
void *f (void **ppv, int **ppi) {
*ppi = nullptr;
return *ppv;
}
and such optimisations, when they reduce code size or increase efficiency, are commonplace nowadays. If f were allowed to be called as void *pv = &pv; f (pv, &static_cast<int*&>(pv));, this optimisation would be invalid. Because such optimisations have proved useful, the rules are unlikely to change.
Getting error:
error: invalid conversion from ‘int*’ to ‘int’ [-fpermissive]
on g++
on the following code:
void* func(void *s)
{
int i = 0;
int self = (int *)s;
printf("Thread Entered: %d\n", self);
sm.lock(self);
// Critical section (Only one thread
// can enter here at a time)
for (i=0; i<MAX; i++)
ans++;
sm.unlock(self);
}
You need to change int self = (int *)s; to int self = *((int *)s); or to int * self = (int *)s;.
You need to think of these as two different things. One is a pointer to memory where the value is stored (int*) and the other is an actual value (int).
Looking at your function declaration void* func(void *s), your s parameter is of type void, this will need to be int if you wish to convert it.
Your data types seem a bit mixed, which just doesn't fly do well in C/C++. Can you clean it? If you are using this function with pthread_create(), as per the documentation with example for this function, try..
// your pthread_create call..
int SOME_INT = 123;
s = pthread_create(&thread_id, &attr, &thread_start, &SOME_INT);
//...and your function
void* func(void *s)
{
int self = (int*) s;
Pointers can be confusing. See if that above code looks similar, in particular to passing the last parameter for pthread_create as a pointer reference. And then try your original code. It might just be that it wasn't passed as a reference.
See what that yields you, else try storing it as a pointer and then converting on use.
void* func(void *s)
{
int *self = s;
sm.lock(*self); // but can give a potential race condition.
In the second line of the function body, I suspect you were trying obtain the value at s (as an int pointer), but you actually cast it directly to an int. That would yield the address in s rather than the value stored there.
This code
#include "alloca.h"
String str = "abc";
unsigned int *i;
void setup() {
Serial.begin(9600);
i = alloca(StringLength() * sizeof(i));
unsigned int j[StringLength() * sizeof(i)];
}
int StringLength() {
return str.length();
}
void loop() {
}
gives me the following error messages:
sketch_dec11f.cpp: In function ‘void setup()’:
sketch_dec11f.cpp:14:7: error: invalid conversion from ‘void*’ to ‘unsigned int*’
What am I doing wrong?
(tried it with malloc() as well, also didn't work!)
You definitely don't want alloca(). That's an allocation that is on the stack of the function and only lasts for the duration of the call. It lets you have dynamic arrays that go away on function return (in C++ you could do this with RAII, but in C alloca was the only way).
You just need a cast in your allocation. Try i = (unsigned int *)malloc(StringLength() * sizeof(*i)). Note the sizeof(*i). That's the size of one member: sizeof(i) is the size of the pointer and is not likely to be related to what's inside.
Malloc and alloca return void * pointers. Just change
i = alloca(StringLength() * sizeof(i));
to
i = (unsigned int *)alloca(StringLength() * sizeof(i));
I am not sure of alloca, but malloc return's void * and C++ unlike C doesn't support implicit conversion from void * to any other pointer type. This is one of the many areas where C++ differs from C.
Cast the returning pointer from alloca:
i = (unsigned int *) alloca(StringLength() * sizeof(i));
In C++, void pointers are not implicitly convertible to other pointers, unlike in C. Therefore you need a cast:
i = static_cast<unsigned int *>(alloca(StringLength() * sizeof(i)));
The const modifier in C++ before star means that using this pointer the value pointed at cannot be changed, while the pointer itself can be made to point something else. In the below
void justloadme(const int **ptr)
{
*ptr = new int[5];
}
int main()
{
int *ptr = NULL;
justloadme(&ptr);
}
justloadme function should not be allowed to edit the integer values (if any) pointed by the passed param, while it can edit the int* value (since the const is not after the first star), but still why do I get a compiler error in both GCC and VC++?
GCC: error: invalid conversion from int** to const int**
VC++: error C2664: 'justloadme' : cannot convert parameter 1 from 'int **' to 'const int **'. Conversion loses qualifiers
Why does it say that the conversion loses qualifiers? Isn't it gaining the const qualifier? Moreover, isn't it similar to strlen(const char*) where we pass a non-const char*
As most times, the compiler is right and intuition wrong. The problem is that if that particular assignment was allowed you could break const-correctness in your program:
const int constant = 10;
int *modifier = 0;
const int ** const_breaker = &modifier; // [*] this is equivalent to your code
*const_breaker = & constant; // no problem, const_breaker points to
// pointer to a constant integer, but...
// we are actually doing: modifer = &constant!!!
*modifier = 5; // ouch!! we are modifying a constant!!!
The line marked with [*] is the culprit for that violation, and is disallowed for that particular reason. The language allows adding const to the last level but not the first:
int * const * correct = &modifier; // ok, this does not break correctness of the code