Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When to use const void*?

I have this very simple test function that I'm using to figure out what's going on with the const qualifier.

int test(const int* dummy)
{
   *dummy = 1;
   return 0;
}

This one throws me an error with GCC 4.8.3. Yet this one compiles:

int test(const int* dummy)
{
   *(char*)dummy = 1;
   return 0;
}

So it seems like the const qualifier works only if I use the argument without casting to another type.

Recently I've seen codes that used

test(const void* vpointer, ...)

At least for me, when I used void *, I tend to cast it to char for pointer arithmetic in stacks or for tracing. How can const void prevent subroutine functions from modifying the data at which vpointer is pointing?

like image 454
MoneyBall Avatar asked Jan 17 '16 18:01

MoneyBall


People also ask

What does const void * mean?

That is it is a constant pointer that points to a const object of type GLvoid . This parameter declaration const GLvoid * const * indices. means that using pointer indices you may not change the pointer (or pointers if this pointer points to the first element of an array of pointers) it points to.

Should void functions be const?

void* is normally used as a way to pass non-specific pointers around (e.g. with memcpy() ). If you want to pass a const char* to such a function then you cannot use void* or you lose the fact that the thing it points to is constant and cannot be altered.

What does void * func () mean?

Void functions, also called nonvalue-returning functions, are used just like value-returning functions except void return types do not return a value when the function is executed. The void function accomplishes its task and then returns control to the caller. The void function call is a stand-alone statement.

What is void * C++?

void (C++) If a pointer's type is void* , the pointer can point to any variable that's not declared with the const or volatile keyword. A void* pointer can't be dereferenced unless it's cast to another type. A void* pointer can be converted into any other type of data pointer.

What is a const void in C++?

A const void* means a pointer to some data that cannot be changed. In order to read it, yes, you have to cast it to concrete types such as char. But I said reading, not writing, which, again, is UB.

Is const void return type invalid?

This answer states that const void return type would be invalid (however compiles on VC++ 2015) If by standard, const void is invalid (VC being wrong) - then what is const void? Show activity on this post. const void is a type which you can form a pointer to. It's similar to a normal void pointer, but conversions work differently.

When not to use const in a function?

Never use const in a function prototypefor a parameter passed by value. It has no meaning and is hence just 'noise'. // don't add const to 'value' or 'size' int find(const int *data, size_t size, int value); Where appropriate, use const volatileon locations that cannot be changed by the program but might still change.

Is a pointer to a void * const or void?

The thing it points to (a pointer to void) is not const, nor is the thing that points to ( void ). The thing to remember about * in a declaration is that they tend to read right-to-left; that is, the rightmost * applies to the variable being declared, while the next one applies to what’s pointed to, etc. With that in mind, how about this one?


3 Answers

const int *var;

const is a contract. By receiving a const int * parameter, you "tell" the caller that you (the called function) will not modify the objects the pointer points to.

Your second example explicitly breaks that contract by casting away the const qualifier and then modifying the object pointed by the received pointer. Never ever do this.

This "contract" is enforced by the compiler. *dummy = 1 won't compile. The cast is a way to bypass that, by telling the compiler that you really know what you are doing and to let you do it. Unfortunately the "I really know what I am doing" is usually not the case.

const can also be used by compiler to perform optimization it couldn't otherwise.


Undefined Behavior note:

Please note that while the cast itself is technically legal, modifying a value declared as const is Undefined Behavior. So technically, the original function is ok, as long as the pointer passed to it points to data declared mutable. Else it is Undefined Behavior.

more about this at the end of the post


As for motivation and use lets take the arguments of strcpy and memcpy functions:

char* strcpy( char* dest, const char* src );
void* memcpy( void* dest, const void* src, std::size_t count );

strcpy operates on char strings, memcpy operates on generic data. While I use strcpy as example, the following discussion is exactly the same for both, but with char * and const char * for strcpy and void * and const void * for memcpy:

dest is char * because in the buffer dest the function will put the copy. The function will modify the contents of this buffer, thus it is not const.

src is const char * because the function only reads the contents of the buffer src. It doesn't modify it.

Only by looking at the declaration of the function, a caller can assert all the above. By contract strcpy will not modify the content of the second buffer passed as argument.


const and void are orthogonal. That is all the discussion above about const applies to any type (int, char, void, ...)

void * is used in C for "generic" data.


Even more on Undefined Behavior:

Case 1:

int a = 24;
const int *cp_a = &a; // mutabale to const is perfectly legal. This is in effect
                      // a constant view (reference) into a mutable object

*(int *)cp_a = 10;    // Legal, because the object referenced (a)
                      // is declared as mutable

Case 2:

const int cb = 42;
const int *cp_cb = &cb;
*(int *)cp_cb = 10;    // Undefined Behavior.
                       // the write into a const object (cb here) is illegal.

I began with these examples because they are easier to understand. From here there is only one step to function arguments:

void foo(const int *cp) {
    *(int *)cp = 10;      // Legal in case 1. Undefined Behavior in case 2
}

Case 1:

int a = 0;
foo(&a);     // the write inside foo is legal

Case 2:

int const b = 0;
foo(&b);     // the write inside foo causes Undefined Behavior

Again I must emphasize: unless you really know what you are doing, and all the people working in the present and in the future on the code are experts and understand this, and you have a good motivation, unless all the above are met, never cast away the constness!!

like image 117
bolov Avatar answered Oct 12 '22 17:10

bolov


int test(const int* dummy)
{
   *(char*)dummy = 1;
   return 0;
}

No, this does not work. Casting away constness (with truly const data) is undefined behavior and your program will likely crash if, for example, the implementation put const data in ROM. The fact that "it works" doesn't change the fact that your code is ill-formed.

At least for me, when I used void*, I tend to cast it to char* for pointer arithmetic in stacks or for tracing. How can const void* prevent subroutine functions from modifying the data at which vpointer is pointing?

A const void* means a pointer to some data that cannot be changed. In order to read it, yes, you have to cast it to concrete types such as char. But I said reading, not writing, which, again, is UB.

This is covered more in depth here. C allows you to entirely bypass type-safety: it's your job to prevent that.

like image 44
edmz Avatar answered Oct 12 '22 16:10

edmz


It’s possible that a given compiler on a given OS could put some of its const data in read-only memory pages. If so, attempting to write to that location would fail in hardware, such as causing a general protection fault.

The const qualifier just means that writing there is undefined behavior. This means the language standard allows the program to crash if you do (or anything else). Despite that, C lets you shoot yourself in the foot if you think you know what you’re doing.

You can’t stop a subroutine from reinterpreting the bits you give it however it wants and running any machine instruction on them it wants. The library function you’re calling might even be written in assembler. But doing that to a const pointer is undefined behavior, and you really don’t want to invoke undefined behavior.

Off the top of my head, one rare example where it might make sense: suppose you’ve got a library that passes around handle parameters. How does it generate and use them? Internally, they might be pointers to data structures. So that’s an application where you might typedef const void* my_handle; so the compiler will throw an error if your clients try to dereference it or do arithmetic on it by mistake, then cast it back to a pointer to your data structure inside your library functions. It’s not the safest implementation, and you want to be careful about attackers who can pass arbitrary values to your library, but it’s very low-overhead.

like image 4
Davislor Avatar answered Oct 12 '22 16:10

Davislor