Saransh Dixit
Saransh Dixit

Reputation: 43

memcpy boolean to void *

I was just creating a testing function in which i have to pass boolean in void * so that i can parse it in other function and use it.

but i am stuck and not able to know that how should i memcpy the boolean in void *.

but when i am parsing it in another fucntion i am always getting the value true.

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

typedef struct {
    int a;
    uint8_t var_data[];
} s;

void parse(s * dummy)
{
    void *var_data = dummy->var_data;
    
    uint8_t *len;
    char type[128];
    bool *leaf;
    for(int i = 0; i < dummy->a; i++)
    {
        len = (uint8_t *)var_data;
        var_data += 1;
        memcpy(type, var_data, *len);
        type[*len] = '\0';
        var_data += *len;
        leaf = (bool *)var_data;
        var_data += 1;
        
        printf("%s\n", type);
        printf("leaf: %s\n\n", leaf ? "true" : "false");
    }
}

int main() 
{
    // Write C code here
    char val[] = "dummy value";
    uint8_t len = strlen(val);
    bool v = false;
    int b = 2;
    int sz = sizeof(s) + b * (sizeof(bool) + len + 1);
    s * dummy = (s *) malloc(sz);
    dummy->a = b;
    void *var = dummy->var_data;
    for(int i = 0; i < dummy->a; i++){
        memcpy(var, &len, 1);
        var += 1;
        memcpy(var, val, len);
        var += len;
        memcpy(var, &v, sizeof(bool));
        var += sizeof(bool);
    }
    parse(dummy);
    return 0;
}

can body help me with this problem.

Upvotes: 2

Views: 535

Answers (2)

vgru
vgru

Reputation: 51224

You didn't dereference leaf in this line:

printf("leaf: %s\n\n", leaf ? "true" : "false");

Since leaf is a non-zero pointer, it will always evaluate to true in C. You want to print *leaf instead:

printf("leaf: %s\n\n", *leaf ? "true" : "false");

Some other misc remarks:

  1. void* arithmetic (i.e. var_data += 1) is illegal in C, although gcc will not complain. Use a char* because this is the type that's supposed to be used for serialization.

  2. As mentioned in other answers, using pointers like you are doing right now can lead to subtle errors. If your pointer is pointing to an address and you want to dereference it (read the value stored there), it's better to do this sooner than risk this location being changed by some other code in the meantime.

    So, just copy the data from the char* array into the target struct (or a primitive like uint8_t) and then advance the pointer.

  3. The only way you are technically allowed to cast pointers in C is to cast them from a specific pointer (like something*) to a char*, in order to inspect their contents. You can also implicitly cast from and to void*, but only if you are not aliasing the pointer (trying to modify the underlying type). Any casting in the other direction is a violation of strict aliasing, so you should try to use memcpy instead. It may look uglier, but compiler will optimize it anyway (see for yourself) and you'll be safe(r) from the horrors of aliasing.

  4. One nice habit to have it to try to utilize const-correctness wherever you can, it helps the compiler warn you if you're doing something wrong. If your function is parsing the array, the parameter should be const char*.

  5. Finally, if your goal is to serialize and deserialize structs, perhaps you should look into protocol buffers or some similar serialization framework. It is fast, efficient, portable and, best of all, already written.

So, something like:

typedef struct {
    int len;  
    char * var_data;
} example;

// note the const keyword - this means this function is
// not going to change the struct, only read it
void parse(const example * dummy)
{
    // again, pointer to const char
    const char * var_data = dummy->var_data;
    
    // move all variables to the innermost scope
    for (int i = 0; i < dummy->len; i++)
    {
        uint8_t len = 0;
        memcpy(&len, var_data, sizeof(len));
        var_data++;

        ...
    }
}

Upvotes: 1

ndogac
ndogac

Reputation: 1245

var_data is uninitialized. You should allocate var_data with malloc, and copy the data in leaf into it:

void *var_data = malloc(sizeof(bool));
bool leaf = false;
memcpy(var_data, &leaf, sizeof(bool));

And you can cast it to bool * like this:

bool *leaf;
leaf = (bool *) var_data;

In addition, you increment var_data pointer. So var_data points to a different memory location now.

Upvotes: 3

Related Questions