Void pointer arithmetic and dereference - c

I was looking at a quick sort implementation for generic purpose and one of the parameters of the quick sort function was a void pointer, and I saw the ff arithmetics on void pointers, so I was wondering what that actually does and if it's even possible?
void _qsort(void *v, int size, int left, int right, int (*comp)(void *, void *));
void swap(void *v1, void *v2, int size) {
char buffer[size];
memcpy(buffer, v1, size);
memcpy(v1, v2, size);
memcpy(v2, buffer, size);
}
This part appears in the body of the quick sort defined above:
void *vl = (char *)(v + (left * size));
void *vr = (char *)(v + (mid * size));
swap(vl, vr, size);
so from above code,
How is it possible to do arithmetic on void pointer v, like v + (left * size)?
What does void *vl = (char *)(v + (left * size)); part mean? isn't already casted to char pointer, if so why are we assigning it to a void pointer?
In the swap part, what exactly is happening, like are the vl and vr changing their memory location, value or something else?

You assume right: arithmetics on void pointers is not defined by the C Standard.
The program you are looking at uses a common compiler extension supported by gcc, clang, tcc and many others, that implements arithmetics on void pointers as if they were byte pointers. With this extension, v + (left * size) behaves as
(void *)((char *)v + (left * size))
So the declaration void *vl = (char *)(v + (left * size)); is equivalent to:
void *vl = (char *)((void *)((char *)v + (left * size)));
Note that the whole expression is cast implicitly to (void *) in C.
This declaration can be simplified as:
void *vl = (char *)v + left * size;
This is probably what the programmer meant to write and their mistake went unreported because the compiler allows void * arithmetics with exactly the same effect.
Regarding your third question, the swap function exchanges the contents of the memory blocks pointed to by v1 and v2 using a local variable length array buffer of size bytes. qsort is usually called with a rather small element size, so this approach is OK, but calling qsort with an array of very long elements (more than a few megabytes) is allowed and could cause a stack overflow.
Here is a safer implementation:
void swap(void *v1, void *v2, size_t size) {
unsigned char *p1 = v1;
unsigned char *p2 = v2;
while (size >= 8) {
char buffer[8];
memcpy(buffer, p1, sizeof buffer);
memcpy(p1, p2, sizeof buffer);
memcpy(p2, buffer, sizeof buffer);
p1 += sizeof buffer;
p2 += sizeof buffer;
size -= sizeof buffer;
}
if (size > 0) {
if (size >= 4) {
char buffer[4];
memcpy(buffer, p1, sizeof buffer);
memcpy(p1, p2, sizeof buffer);
memcpy(p2, buffer, sizeof buffer);
p1 += sizeof buffer;
p2 += sizeof buffer;
size -= sizeof buffer;
}
while (size > 0) {
unsigned char temp = *p1;
*p1 = *p2;
*p2 = temp;
p1++;
p2++;
size--;
}
}
}
Also note these remarks:
The standard library function qsort has a different prototype:
void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
Using int for sizes and index values is not recommended. left * size might overflow for a large array on systems with 64-bit size_t and 32-bit int: The _qsort function assumes that left * size and right * size are within the range of type size_t and at most the size of the array pointed to by v, yet this size might exceed INT_MAX, causing the int multiplication to overflow with undefined behavior. The correct type is size_t and a sanity check at the beginning of the function can be used to detect invalid arguments.

Related

void * as a function parameter

I've problem using void *. How should I do to use this clean_buffer function for int and float arrays.
void clean_buffer( void *ptr, int n)
{
for( int i = 0; i < n; i++)
ptr[i]=0;
}
int main(void)
{
float *pf;
int *pi;
pf = (float *) malloc(10*sizeof(float));
pi = (int *)malloc(10*sizeof(int));
clean_buffer( (float *)pf, 10);
clean_buffer( (int *)pi, 10);
return 0;
}
void is a non value you cannot use *((void*) x) = v; and to use a cast to use a pointer to an other type is dangerous because the size may not be the same
But, in your case you set to 0, so you can use memset or replace the malloc my calloc and it is useless to have clean_buffer :
int main(void)
{
float *pf;
int *pi;
pf = calloc(10, sizeof(float));
pi = calloc(10, sizeof(int));
return 0;
}
Type void has no size. Therefore you cannot use void * to clear an array.
You can not dereference that type for the same reason.
You must cast to a pointer with specific type:
void clean_buffer(void *ptr, size_t n)
{
unsigned char *my_ptr = ptr;
for (int i = 0; i < n; i++)
my_ptr[i]=0;
}
You need to take care that the size passed to your function cannot be the number or arrays because the compiler cannot do pointer arithmetics with void* pointers.
And for sizes you should use size_t
Instead you have to pass the size of the array in bytes:
int main(void)
{
float *pf;
int *pi;
pf = malloc(10*sizeof(float));
pi = malloc(10*sizeof(int));
clean_buffer( pf, 10*sizeof(float));
clean_buffer( pi, 10*sizeof(int));
return 0;
}
Or you need to pass any other information that can be used to determine type and/or size of the data.
Also:
Casting the return value of malloc is not needed in C.
Casting the parameters of clean_buffer to a type that the variable already has, is useless. The pointer type is converted to void * anyway as this is what the function expects.
Note:
Other answers and comments mention that you could simply pass the pointer to memset or use calloc etc.
This might be true for this very specific case but if you want to do anything else than simply zeroing the memory, the same aspects regarding void * pointers apply as I have shown here. And in those cases memset or calloc are of no help.
I've problem using void *. How should I do to use this clean_buffer function for int and float arrays (?)
Others have mentioned useful things like the need to sizeof to find the size, cast not needed and alternatives to use calloc() for a zero initialized memory allocation.
To add:
sizeof *object_pointer
Use sizeof *object_pointer to find the size. It is less error prone, easier to review and maintain than coding in the type.
// clean_buffer( (float *)pf, 10);
// clean_buffer( (int *)pi, 10);
// cast not needed
clean_buffer(pf, sizeof *pf * 10); // No need to mention type!
clean_buffer(pi, sizeof *pi * 10);
volatile
Scrubbing memory after its final use is prone to being optimized out and then a good reason to not use memset() when memory security is of concern. Instead use volatile to prevent clean_buffer() from itself being optimized out.
void clean_buffer(void *ptr, size_t n) {
volatile unsigned char *vuc = ptr;
for(size_t i = 0; i < n; i++)
vuc[i]=0;
}
}
You cast ptr to the appropriate type so it can dereferenced to clear what it points to; something you know points to a type that you want to clear i items of.
void clean_buffer( void *ptr, int n)
This function demands a void * as a parameter.
clean_buffer( (float *)pf, 10);
Here you are casting to a float pointer. So this a different type as demanded
Also void has no size, so you cant really use [] on ptr
malloc itself returns a void pointer as it doesn't really know for which type you are allocating memory. So, you need to use the same size which you pass to malloc for clearing the buffer also.
You can use memset and pass the size of the entire buffer to clear it without having to worry about it's type.
void clean_buffer( void *ptr, size_t n)
{
memset(ptr, 0, n)
}
int main(void)
{
float *pf;
int *pi;
pf = (float *) malloc(10*sizeof(float));
pi = (int *)malloc(10*sizeof(int));
clean_buffer(pf, 10*sizeof(float));
clean_buffer(pi, 10*sizeof(int));
return 0;
}
Additionally, as others have suggested, you can use calloc if it suits you.

Compiler showing "Expression must have a constant value" in VS2015 (C Programming)

So i wanted to create a swap function that will allocation size of character array dynamically. Is it possible? Is it recommended?
My code that shows errors.
void swap(void * vp1, void * vp2, int size)
{
char buffer[size]; //size must have a constant value is the error i am getting.
memcpy(buffer, vp1, size);
memcpy(vp1, vp2, size);
memcpy(vp2, buffer, size);
}
char buffer[size];
An array whose size is not known at compile time is known as a variable length array. These are C-only features. Since this is giving an error, you are using a C++ compiler. You must change size so that it will be known at compile time.
The following are valid C++ array definitions
#define NUM 8
int a[16];
int a[NUM];
This is ill-formed:
int n;
printf("What is your age?\n");
scanf("%d", &n);
int a[n];
because n depends on what the user types.
The solution is to cast the void pointer arguments into pointers to unsigned char, and then manipulate the unsigned char *
void swap(void *a, void *b, size_t n)
{
unsigned char *p = (unsigned char *) a,
*q = (unsigned char *) b,
tmp;
for (size_t i = 0; i < n; ++i) {
tmp = p[i];
p[i] = q[i];
q[i] = tmp;
}
}
But since you're using C++, it is a better idea to use templates.
Unfortunately MSVC is the only major C compiler that doesn't support your code. To support all compilers you can write:
void swap(void * vp1, void * vp2, int size)
{
char *buffer = malloc(size);
if ( buffer )
{
memcpy(buffer, vp1, size);
memcpy(vp1, vp2, size);
memcpy(vp2, buffer, size);
free(buffer);
}
}
Note: This way is simple in code but there may be more efficient options, especially if you are typically only swapping small buffers.

Pointer of substring from pointer of string

I'm quite new to C and struggle with the following.
Consider a string (pointer to an array of characters), containing an arbitrary amount of unsigned bytes, in our case, let it be programming. Write a function, which returns a pointer to a new location in memory (but not allocated with malloc), containing the substring of arbitrary but fixed size, at an arbitrary offset - in our case, let it be gramm.
I tried with the following:
unsigned char* newptr(unsigned char* p, int offset, int size) {
unsigned char r[size];
memcpy(r, p + offset, size + 1);
return r;
}
but it errors with Segmentation fault. Otherwise
unsigned char* newptr = oldptr + offset;
works but it doesn't solve the problem with the fixed size - I don't want the rest of chars belonging to the pointer.
Thanks in advance and sorry if the question was asked before, I just couldn't find anything of help.
Some possible solutions, all them are non-optimal:
Using global buffer (using global variable is bad practice and if you call the function again it rewrites the buffer, buffer has fixed size and it must be larger then your size):
unsigned char r[100000];
unsigned char* newptr(unsigned char* p, int offset, int size) {
memcpy(r, p + offset, size);
r[size] = '\0';
return r;
}
Using static local buffer (has mostly the same problems as solution with global buffer):
unsigned char* newptr(unsigned char* p, int offset, int size) {
static unsigned char r[100000];
memcpy(r, p + offset, size);
r[size] = '\0';
return r;
}
Using memory of input variable as buffer (the problem is then *p changes after the function call)
unsigned char* newptr(unsigned char* p, int offset, int size) {
p[offset + size] = '\0';
return p + offset;
}
Passing allocated buffer into function:
unsigned char* newptr(unsigned char* p, int offset, int size, unsigned int* r) {
memcpy(r, p + offset, size);
r[size] = '\0';
return r;
}

qsort and strcmp problems when dealing with empty strings and whitespace

I am having a problem getting qsort to work for an array of strings (char * to be exact). I create a compare function that I thought should work for the qsort requirement, but it does not seem to work at all. I also need it to work with whitespace characters and blank strings (ex. ""). Any direction or note on what I am doing wrong would be greatly appreciated.
My relevant source code:
int compareAlphabetically(const void * a,const void * b);
void sortStringArray(char * * arrString, int len){
int size = sizeof(arrString) / sizeof(char *);
if(*arrString != NULL && len > 1)
qsort(arrString, size, sizeof(char *), compareAlphabetically);
}
int compareAlphabetically(const void * a, const void * b)
{
const char *a_p = *(const char **) a;
const char *b_p = *(const char **) b;
return strcmp(a_p,b_p);
}
The function definition is (and it should remain unchanged):
/**
* Takes an array of C-strings, and sorts them alphabetically, ascending.
*
* arrString: an array of strings
* len: length of the array 'arrString'
*
* For example,
* int len;
* char * * strArr = explode("lady beatle brew", " ", &len);
* sortStringArray(strArr, len);
* char * str = implode(strArr, len, " ");
* printf("%s\n", str); // beatle brew lady
*
* Hint: use the <stdlib.h> function "qsort"
* Hint: you must _clearly_ understand the typecasts.
*/
void sortStringArray(char * * arrString, int len);
Wrong size calculation.
size = sizeof(arrString) / sizeof(char *); is likely always 1:
the size of a pointer (char **) divided by the size of a pointer (char *).
Code likely needs to use len:
void sortStringArray(char * * arrString, int len){
if(*arrString != NULL && len > 1) {
// qsort(arrString, size, sizeof(char *), compareAlphabetically);
qsort(arrString, len, sizeof(char *), compareAlphabetically);
}
}
[Edit]
Note: The len > 1 is not functionally needed.
For a value of 0, len > 1 is not needed. But as len may be less than 0 and size_t is some unsigned type (and changing a negative int to some unsigned tpye is disater), using len > 1 is prudent.

C converting void** to char** on 64 bit system

I am getting a warning as follows on 64 bit system:
warning: cast to pointer from integer of different size when I make the following call:
char** str;
str = (char **)Calloc2D(nObj + 1, 20, sizeof(char))
Here is the actual function to allocate 2D array:
void **Calloc2D(size_t nobj1, size_t nobj2, size_t size)
{
void **p1;
void *p2;
size_t iobj1;
char *c2;
/* Allocate memory for one big array */
p2 = calloc((nobj1 * nobj2), size);
if (p2 == NULL) return NULL;
/* Allocate memory for the first dimension */
p1 = (void **) calloc(nobj1, sizeof(void *));
if (p1 == NULL) {
free(p2);
return NULL;
}
/* Set up the pointers for the first dimesion */
c2 = (char *) p2;
for (iobj1 = 0; iobj1 < nobj1; iobj1++) {
p1[iobj1] = (void *) c2;
c2 += (nobj2 * size);
}
/* Return a pointer to the 2-dimensional array */
return p1;
}
What I do not understand is why I am getting the above warning thought my function has been fully declared to return void**. My understanding is that in 64 bit systems void** and char** should have the same size (8 bytes). Then why this warning and how to fix it.

Resources