Solution for "dereferencing" void * 'pointer "warning in a struct in C?

I was trying to create a pseudo superstructure to print an array of structures. My main structures are as follows.

/* Type 10 Count */
typedef struct _T10CNT
{
    int _cnt[20];
} T10CNT;

...

/* Type 20 Count */
typedef struct _T20CNT
{
    long _cnt[20];
} T20CNT;
...

      

I created the structure below to print an array of the above structures. I got a dereference of a void pointer error while compiling the below code snippet.

typedef struct _CMNCNT
{
    long  _cnt[3];
} CMNCNT;

static int printCommonStatistics(void *cmncntin, int cmncnt_nelem, int cmncnt_elmsize)
{
    int ii;
    for(ii=0; ii<cmncnt_nelem; ii++)
    {
        CMNCNT *cmncnt = (CMNCNT *)&cmncntin[ii*cmncnt_elmsize];
        fprintf(stout,"STATISTICS_INP: %d\n",cmncnt->_cnt[0]);
        fprintf(stout,"STATISTICS_OUT: %d\n",cmncnt->_cnt[1]); 
        fprintf(stout,"STATISTICS_ERR: %d\n",cmncnt->_cnt[2]);
    }
    return SUCCESS;
}

T10CNT struct_array[10];
...
printCommonStatistics(struct_array, NELEM(struct_array), sizeof(struct_array[0]);
...

      

My intention is to have a common function to print all arrays. Please let me know the correct way to use it.

Appreciate help in advance.

Edit: the parameter name changed to cmncntin from cmncnt. Sorry, that was a typo error.

Thanks, Matthew Liju

+1


source to share


10 replies


I think your design will fail, but I'm also not convinced that the other answers I see are entirely about deeper reasons.

It looks like you are trying to use C to deal with generic types, which always gets hairy. You can do this if you're careful, but it's not easy, in which case I doubt it would be helpful.

Deeper mind . Suppose we have overcome syntactic (or hardly syntactic) problems. Your code shows T10CNT contains 20 int

and T20CNT contains 20 long

. On modern 64-bit machines - except Win64 - sizeof(long) != sizeof(int)

. Therefore, the code inside your print function must distinguish between dereferencing int

arrays and long

arrays. There is a rule in C ++ that you shouldn't try to handle arrays polymorphically, and that's why. CMNCNT type contains 3 long

values; unlike the structures T10CNT and T20CNT, although the underlying array type is T20CNT.

Style guide: I highly recommend avoiding underscores on names. In general, names beginning with an underscore are reserved for implementation use and for use as macros. Macros have nothing to do with scope; if the implementation defines a _cnt macro, it will destroy your code. There are nuances in what names are reserved; I am not going to get into these nuances. It is much easier to think that "names starting with underscores are reserved" and that will save you the trouble.

Style tip: Your print function returns success unconditionally. This is unreasonable; your function shouldn't return anything, so the caller doesn't need to check for success or failure (since he can never fail). A careful coder who notices that a function is returning a status will always check the return status and have error handling code. This code will never get executed, so it's dead, but it's hard for anyone (or the compiler) to figure it out.

Surface correction: temporary, we can assume that you can think of int

and long

as synonyms; but you have to get in the habit of thinking that they are synonymous. An argument void *

is the correct way to say "this function takes an undefined pointer". However, inside the function, you need to convert from void *

to a specific type before indexing.

typedef struct _CMNCNT
{
    long    count[3];
} CMNCNT;

static void printCommonStatistics(const void *data, size_t nelem, size_t elemsize)
{
    int i;
    for (i = 0; i < nelem; i++)
    {
        const CMNCNT *cmncnt = (const CMNCNT *)((const char *)data + (i * elemsize));
        fprintf(stdout,"STATISTICS_INP: %ld\n", cmncnt->count[0]);
        fprintf(stdout,"STATISTICS_OUT: %ld\n", cmncnt->count[1]); 
        fprintf(stdout,"STATISTICS_ERR: %ld\n", cmncnt->count[2]);
    }
}

      



(I like the idea of ​​a file stream called stout

). Suggestion: Use cut'n'paste for real source code - it's safer! I usually use " sed 's/^/ /' file.c

" to prepare the code to cut the "response" into the "SO" response.)

What does this casting line do? I'm glad you asked ...

  • The first operation is to convert const void *

    to const char *

    ; this allows operations on the size of the bytes by address. In the days before the standard, C was instead void *

    used char *

    as a universal addressing mechanism.
  • The following operation adds the correct number of bytes to jump to the beginning of the i

    th element of the size object array elemsize

    .
  • The second cast tells the compiler "trust me - I know what I'm doing" and "treat this address as the address of the CMNCNT structure".

From there, the code is simple enough. Please note, since the CMNCNT structure contains a value long

, I used %ld

to tell the truth fprintf()

.

Since you are not going to change the data in this function, it would be nice to use a qualifier const

like I did.

Note that if you are correct sizeof(long) != sizeof(int)

, you will need two separate blocks of code (I would suggest separate functions) to work with array int

and 'array long

'.

+5


source


The type of void is intentionally left unfinished. It follows that you cannot dereference void pointers, and you cannot take its size. This means that you cannot use the index operator using it as an array.

The moment you assign something to a pointer to void, any original type information pointing to the type is lost, so you can only look up if you first revert it back to the original pointer type.

First and most importantly, you are passing functions to a T10CNT*

function, but you are trying to type (and dereference) in CMNCNT*

in your function. This is not valid and the behavior is undefined.



You need the printCommonStatistics function for each type of array elements. So printCommonStatisticsInt

, printCommonStatisticsLong

, printCommonStatisticsChar

that are distinguished by their first argument (one takes int*

the other takes long*

, etc.). You can create them using macros to avoid redundant code.

Passing the structure itself is not a good idea, since then you need to define a new function for each different size of the contained array inside the structure (since they are all different). So it's better to pass the contained array directly ( struct_array[0]._cnt

, call the function for each index)

+2


source


Change the function declaration to char * as follows:

static int printCommonStatistics(char *cmncnt, int cmncnt_nelem, int cmncnt_elmsize)

      

void does not take any particular size, whereas char will take byte size.

+1


source


You cannot do this:

cmncnt->_cnt[0]

      

if cmnct is a pointer to void.

You must specify the type. You may need to rethink your implementation.

+1


source


Function

static int printCommonStatistics(void *cmncntin, int cmncnt_nelem, int cmncnt_elmsize)
{
    char *cmncntinBytes;
    int ii;

    cmncntinBytes = (char *) cmncntin;
    for(ii=0; ii<cmncnt_nelem; ii++)
    {
        CMNCNT *cmncnt = (CMNCNT *)(cmncntinBytes + ii*cmncnt_elmsize);  /* Ptr Line */
        fprintf(stdout,"STATISTICS_INP: %d\n",cmncnt->_cnt[0]);
        fprintf(stdout,"STATISTICS_OUT: %d\n",cmncnt->_cnt[1]); 
        fprintf(stdout,"STATISTICS_ERR: %d\n",cmncnt->_cnt[2]);
    }
    return SUCCESS;
}

      

Works for me.

The problem is that on the line commented out "Ptr Line" the code adds a pointer to an integer. Since our pointer is char *, we move forward in memory size (char) * ii * cmncnt_elemsize, which is what we want since char is one byte. Your code was trying to do an equivalent thing by forwarding sizeof (void) * ii * cmncnt_elemsize, but void is not sized, so the compiler gave you an error.

I would change T10CNT and T20CNT to use int or long instead of one with each. You depend on sizeof (int) == sizeof (long)

+1


source


In this line:

CMNCNT *cmncnt = (CMNCNT *)&cmncnt[ii*cmncnt_elmsize];

      

You are trying to declare a new variable cmncnt, but a variable with that name already exists as a function parameter. You can use a different variable name to solve this problem.

Also you can pass a pointer to CMNCNT to the function instead of a void pointer, because then the compiler will do the pointer arithmetic for you and you don't need to use it. I see no point in passing a void pointer when everything you do with it is passed to CMNCNT. (It's not a very descriptive name for a data type, by the way.)

0


source


Your expression

(CMNCNT *)&cmncntin[ii*cmncnt_elmsize]

      

tries to take the address cmncntin [ii * cmncnt_elmsize] and then apply that pointer to the type (CMNCNT *). It cannot get the address of cmncntin [ii * cmncnt_elmsize] because cmncntin is void *.

C-related indicia, and insert parentheses if necessary.

0


source


Point of information: The inner gasket can really mess it up.

Consider struct {char c [6]; }; - It has sizeof () = 6. But if you had an array of them, each element could be padded with an 8-byte alignment!

Some build operations do not handle misaligned data. (For example, if int contains two words of memory.) (YES, I was already bitten by this.)

...

Second: I used to use variable size arrays. (I was dumb then ...) It works if you don't change the type. (Or, if you have a union of types.)

eg:.

struct T { int sizeOfArray;  int data[1]; };

      

Highlighted as

T * t = (T *) malloc( sizeof(T) + sizeof(int)*(NUMBER-1) );
                      t->sizeOfArray = NUMBER;

      

(Although padding / aligning can still hurt you.)

...

Third: Consider:

   struct T {
     int sizeOfArray;
     enum FOO arrayType;
     union U { short s; int i; long l; float f; double d; } data [1];
    };

      

He solves problems by knowing how to print data.

...

Fourth: you can just pass an int / long array to your function, not a struct. For example:

void printCommonStatistics( int * data, int count )
{
  for( int i=0;  i<count;  i++ )
    cout << "FOO: " << data[i] << endl;
}

      

Called via:

_T10CNT  foo;
printCommonStatistics( foo._cnt, 20 );

      

Or:

 int a[10], b[20], c[30];
printCommonStatistics( a, 10 );
printCommonStatistics( b, 20 );
printCommonStatistics( c, 30 );

      

This works much better than hiding data in structures. When you add members to one of your structures, the layout may change between your structure and not be consistent. (The value of the _cnt address relative to the start of the structure may change for _T10CNT and not for _T20CNT. There is debugging time. A single structure with a pooled _cnt payload will avoid this.)

eg:.

struct FOO {
  union {
         int     bar  [10];
          long biff [20];
   } u;
}

      

...

Fifth: If you have to use structs ... C ++, iostreams and templating will be much cleaner to implement.

eg:.

template<class TYPE> void printCommonStatistics( TYPE & mystruct, int count )
{
  for( int i=0;  i<count;  i++ )
    cout << "FOO: " << mystruct._cnt[i] << endl;
}      /* Assumes all mystruct have a "_cnt" member. */

      

But this is probably not what you are looking for ...

0


source


C is not my cup o'java, but I think your problem is that "void * cmncnt" should be CMNCNT * cmncnt.

Feel free to correct me now, C programmers, and tell me why Java programmers can't have nice things.

-1


source


This line is kind of tortured, doesn't it?

CMNCNT *cmncnt = (CMNCNT *)&cmncntin[ii*cmncnt_elmsize];

      

How about something more similar

CMNCNT *cmncnt = ((CMNCNT *)(cmncntin + (ii * cmncnt_elmsize));

      

Or even better if cmncnt_elmsize = sizeof (CMNCNT)

CMNCNT *cmncnt = ((CMNCNT *)cmncntin) + ii;

      

This should also get rid of the warning, as you are no longer looking for the void *.

BTW: I'm not entirely sure why you are doing this, but if cmncnt_elmsize is sometimes not sizeof (CMNCNT), and may actually differ from call to call, I would suggest rethinking this design. I suppose there might be a good reason for this, but it looks very shaky to me. I almost guarantee there is a better way to design things.

-1


source







All Articles