0

I have a simple struct:

typedef struct {
    void *things;
    int sizeOfThings;
} Demo;

things is intended to contain an array of individual "thing", like maybe strings or ints. I create a pointer to it:

Demo * Create(int value) {
    Demo *d = malloc(sizeof(Demo));
    if (d != NULL) {
        d->sizeOfThings = value;
        d->things = malloc(20 * value); // We'll have a max of 20 things
    }
}

value is sizeof(int) for an array of ints, for example.

If in another function I want to insert something into d->things (assuming at least for not that I'm just adding it to the first slot, position management done elsewhere):

char * thing = "Me!";
strncpy(d->things[0], &thing, d->sizeOfThings);

I get around the strncpy area

test.c:10: warning: pointer of type ‘void *’ used in arithmetic
test.c:10: warning: dereferencing ‘void *’ pointer
test.c:10: error: invalid use of void expression

I'm just trying to understand the use of void* as a way to generalize my functions. I suspect there's something wrong with d->things[0].

Rio
  • 14,182
  • 21
  • 67
  • 107

4 Answers4

4

According to the C standard, void has no size-- sizeof(void) is undefined. (Some implementations make it sizeof(int) but this is non-compliant.)

When you have an array of type foo, this expression:

array[3]

Adds 3*sizeof(foo) to the address stored in array and then deferences that. That's because the values are all packed together in memory. Since sizeof(void) is undefined, you can't do that for void arrays (in fact you can't even have void arrays, only void pointers.)

You must cast any void pointer to another pointer type before treating it as an array:

d->things = malloc(20 * sizeof(int));
(int *)(d->things)[0] = 12;

However, keep in mind that you don't even have to do that to use strncpy on it. Strncpy can accept a void pointer just fine. But you were using strncpy incorrectly. Your strncpy invocation should look like this:

strncpy(d->things, thing, d->sizeOfThings);

What your version would have done was try to treat the first array member of d->things as a pointer when it's not, and would have treated &thing, which is a char **, as if it were just a char *.

Max E.
  • 1,837
  • 13
  • 15
  • This is helpful. Would I use pointer arithmetic to strncpy future items? – Rio Apr 22 '11 at 01:45
  • You're right. But in this case, it was "sizeof(void *)", not "sizeof(void)". – shinkou Apr 22 '11 at 01:50
  • I'm not sure what you mean. Strncpy will copy a string (which is just an array of chars.) If you want to join multiple stings together in one big buffer, you could use strncpy on an offset pointer, but it's much easier to use strncat. – Max E. Apr 22 '11 at 01:52
  • Well ideally things is an array of void *? – Rio Apr 22 '11 at 01:56
  • In that case you should change the declaration in your struct to void **things. And then yes, you use d->things[0] (including when assigning the result of malloc!) – Max E. Apr 22 '11 at 02:05
1

Try to see if this fixes your problem:

char *thing = "Me!";
strncpy(&d->things[0], thing, d->sizeOfThings);

Then, cast the pointers to get rid of the warnings, but you have to make sure what you're going to do

char *thing = "Me!";
strncpy((char *) &d->things[0], (const char *) thing, d->sizeOfThings);
shinkou
  • 5,138
  • 1
  • 22
  • 32
0
Demo *d = malloc(sizeof(Demo));
if (d != NULL) {
    d->things = malloc(20 * sizeOfThings); // We'll have a max of 20 things
}

What is sizeOfThings initialized to ? Probably it might have garbage and is causing the error. Even if it is initialized to 0 by default, then malloc returns NULL( malloc( 20 * 0 ) ; ). And so, I suspect -

strncpy(d->things[0], &thing, d->sizeOfThings);
      // ^^^^^^^^^^ causing the error.
Mahesh
  • 34,573
  • 20
  • 89
  • 115
  • @Rio - The warning clearly says it - `test.c:10: warning: dereferencing ‘void *’ pointer` – Mahesh Apr 22 '11 at 01:39
  • sizeOfThings is set to sizeof(char *) for strings and sizeof(int) for ints. – Rio Apr 22 '11 at 01:39
  • @Rio - You are requesting for memory whose size is **20*sizeOfThings**. But at the point of execution of `malloc` statement, what is the `sizeOfThings` value ? Also as @shinkou suggested the second parameter should `thing`. – Mahesh Apr 22 '11 at 01:41
  • Right now I'm working with an array of ints. gdb tells me sizeOfThings is 4. – Rio Apr 22 '11 at 01:42
  • @Rio - Where are you initializing `sizeOfThings` to 4 which you have no where shown in the part of question you posted ? I suspect you are seeing `sizeof(sizeOfThings)` which is 4 bytes anyway. – Mahesh Apr 22 '11 at 01:45
0

Two things:

Firstly, there's definitely something wrong with using d->things[0]. d->things is actually a pointer and the convention is that pointers and arrays are basically interchangeable (with a few exceptions) and the array name will always point to the first element of the array.

Secondly, the functional signature of strncpy is char* strncpy(char* destination, const char* source, size_t num);. So to make this work, we have to cast d->thing from void* to char* and make sure that we pass thing as a char* (just thing) vs. a char** (which is thing&).

so we want this statement instead:

strncpy((char*)d->things, thing, d->sizeOfThings);

Once the changes are in place, the rest of the code compiles and runs as expected.

Marc
  • 4,546
  • 2
  • 29
  • 45