简体   繁体   中英

Accessing arrays in a pointer to a struct

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] .

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 *.

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.

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM