开发者

Array of pointers and more in C

Here is the piece of codes where I don't understand

#include "malloc.h"

/*some a type A and type for pointers to A*/
typedef struct a
{
    unsigned long x;
} A, *PA;

/*some a type B and type for pointers to B*/
typedef struct b
{
    unsigned char length;
    /*array of pointers of type A variables*/
    PA * x;
} B, *PB;

void test(unsigned char length, PB b)
{
    /*we can set length in B correctly*/

    b->length=length;

    /*we can also allocate memory for the array of pointers*/

    b->x=(PA *)malloc(length*sizeof(PA));

    /*but we can't set pointers in x*/

    while(length!=0)
        b->x[length--]=0; /*it just would not work*/
}

int main()
{
    B b;
    test开发者_开发技巧(4, &b);
    return 0;
}

Can anyone elaborate conceptually to me why we can't set pointers in array x in test()?


On the last line of test() you are initializing the location off the end of your array. If your length is 4, then your array is 4 pointers long. b->x[4] is the 5th element of the array, as the 1st is b->x[0]. You need to change your while loop to iterate over values from 0 to length - 1.


If you want to set to null every PA in b->x, then writing --length instead of length-- should do the job.
Obviously trying to figure out where the -- belongs is confusing. You better write:

unsigned i;
for (i = 0; i < length; i++)
   b->x[i] = 0;

But in fact, in this case, you could simply use:

memset(b->x, 0, length*sizeof(PA));


Your structure is more complicated by one level of dynamic memory allocation than is usually necessary. You have:

typedef struct a
{
    unsigned long x;
    ...and other members...
} A, *PA;

typedef struct b
{
    unsigned char length;
    PA * x;
} B, *PB;

The last member of B is a struct a **, which might be needed, but seldom is. You should probably simplify everything by using:

typedef struct a
{
    unsigned long x;
} A;

typedef struct b
{
    unsigned length;
    A       *array;
} B;

This rewrite reflects a personal prejudice against typedefs for pointers (so I eliminated PA and PB). I changed the type of length in B to unsigned from unsigned char; using unsigned char saves on space in the design shown, though it might conceivably save space if you kept track of the allocated length separately from the length in use (but even then, I'd probably use unsigned short rather than unsigned char).

And, most importantly, it changes the type of the array so you don't have a separate pointer for each element because the array contains the elements themselves. Now, occasionally, you really do need to handle arrays of pointers. But it is relatively unusual and it definitely complicates the memory management.

The code in your test() function simplifies:

void init_b(unsigned char length, B *b)
{
    b->length = length;
    b->x = (A *)malloc(length*sizeof(*b->x));
    for (int i = 0; i < length; i++)
        b->x[i] = 0;
}

int main()
{
    B b;
    init_b(4, &b);
    return 0;
}

Using an idiomatic for loop avoids stepping out of bounds (one of the problems in the original code). The initialization loop for the allocated memory could perhaps be replaced with a memset() operation, or you could use calloc() instead of malloc().

Your original code was setting the pointers in the array of pointers to null; you could not then access any data because there was no data; you had not allocated the space for the actual struct a values, just space for an array of pointers to such values.

Of course, the code should either check whether memory allocation failed or use a cover function for the memory allocator that guarantees never to return if memory allocation fails. It is not safe to assume memory allocation will succeed without a check somewhere. Cover functions for the allocators often go by names such as xmalloc() or emalloc().

Someone else pointed out that malloc.h is non-standard. If you are using the tuning facilities it provides, or the reporting facilities it provides, then malloc.h is fine (but it is not available everywhere so it does limit the portability of your code). However, most people most of the time should just forget about malloc.h and use #include <stdlib.h> instead; using malloc.h is a sign of thinking from the days before the C89 standard, when there was no header that declared malloc() et al, and that is a long time ago.


See also Freeing 2D array of stack; the code there was isomorphic with this code (are you in the same class?). And I recommended and illustrated the same simplification there.


I just added a printf in main to test b.length and b.x[] values and everything's work. Just added it like that printf("%d, %d %d %d %d", b.length, b.x[0], b.x[1], b.x[2], b.x[3]); before the return.

It gaves 4, 0, 0, 0, 0 which is I think what you expect no? Or it is an algorithmic error


I assume you are trying to zero all of the unsigned longs inside the array of A's pointed to within B.

Is there a precedence issue with the -> and [] operators here?

Try:

(b->x)[length--] = 0;

And maybe change

typedef struct a
{
    unsigned long x;
} A, *PA;

to

typedef struct a
{
    unsigned long x;
} A;
typedef A * PA;

etc

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜