2

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.

  • 4
    You have a pointer `var_data`, but *where does it point?* – Some programmer dude Oct 26 '20 at 13:20
  • 1
    to some struct's variable data. – Saransh Dixit Oct 26 '20 at 13:22
  • Also, you increase `var_data` after the `memcpy` call, which means that it no longer points to the data you copied to there it pointed. So "in another function" the pointer is not pointing to the data you think it does. – Some programmer dude Oct 26 '20 at 13:23
  • 2
    Please [edit] your question to include a proper [mcve]. Also please try to explain *why* you need to do it this way. What is the original problem you want to solve. – Some programmer dude Oct 26 '20 at 13:23
  • Usually you would just cast the bool to a pointer (which isn't a valid pointer and you can't dereference it) and then cast the pointer back to a bool. – user253751 Oct 26 '20 at 13:25
  • i have not added the completed code because it is too long code but i have parse it accordingly to the structure that i want every value is coming right but in case of boolean eveytime true value is coming. – Saransh Dixit Oct 26 '20 at 13:27
  • 3
    A [mcve] isn't supposed to be your "complete code", it's supposed to be enough for us to copy-paste and replicate the problem you ask about and nothing more. – Some programmer dude Oct 26 '20 at 13:30
  • You original code contained `var_data += sizeof(bool);` at the end, which changes quite a bit. Why did you remove it? – vgru Oct 26 '20 at 13:40
  • sorry @Someprogrammerdude Sir i have added the dummy code now. – Saransh Dixit Oct 26 '20 at 14:14

2 Answers2

3

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.

ndogac
  • 1,185
  • 6
  • 15
1

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++;

        ...
    }
}
vgru
  • 49,838
  • 16
  • 120
  • 201
  • Okay now i get it i did a blunder and will try to remember all the point you have stated sir. Thank you for the guidance. – Saransh Dixit Oct 27 '20 at 09:13