rpbear
rpbear

Reputation: 640

The difference of the pointer and a array

I have a piece of code bellow,and what's the difference of them? The first one,the address of buf element of the struct is 4 bigger than that of the struct while the second one is not.

First

#include <stdio.h>

typedef struct A
{
    int i;
    char buf[];  //Here
}A;

int main()
{
    A *pa = malloc(sizeof(A));
    char *p = malloc(13);
    memcpy(p, "helloworld", 10);
    memcpy(pa->buf, p, 13);

    printf("%x %x %d %s\n", pa->buf, pa, (char *)pa->buf - (char *)pa, pa->buf);
}

Second

typedef struct A
{
    int i;
    char *buf; //Here
}A;

Upvotes: 0

Views: 192

Answers (2)

Jonathan Leffler
Jonathan Leffler

Reputation: 753785

The first is a C99 'flexible array member'. The second is the reliable fallback for when you don't have C99 or later.

With a flexible array member, you allocate the space you need for the array along with the main structure:

A *pa = malloc(sizeof(A) + strlen(string) + 1);

pa->i = index;
strcpy(pa->buf, string);

...use pa...

free(pa);

As far as the memory allocation goes, the buf member has no size (so sizeof(A) == sizeof(int) unless there are padding issues because of array alignment — eg if you had a flexible array of double).

The alternative requires either two allocations (and two releases), or some care in the setup:

typedef struct A2
{
    int   i;
    char *buf;
} A2;

A2 *pa2 = malloc(sizeof(A2));
pa2->buff = strdup(string);

...use pa2...

free(pa2->buff);
free(pa2);

Or:

A2 *pa2 = malloc(sizeof(A2) + strlen(string) + 1);
pa2->buff = (char *)pa2 + sizeof(A2);

...use pa2...

free(pa2);

Note that using A2 requires more memory, either by the size of the pointer (single allocation), or by the size of the pointer and the overhead for the second memory allocation (double allocation).

You will sometimes see something known as the 'struct hack' in use; this predates the C99 standard and is obsoleted by flexible array members. The code for this looks like:

typedef struct A3
{
    int  i;
    char buf[1];
} A3;

A3 *pa3 = malloc(sizeof(A3) + strlen(string) + 1);
strcpy(pa3->buf, string);

This is almost the same as a flexible array member, but the structure is bigger. In the example, on most machines, the structure A3 would be 8 bytes long (instead of 4 bytes for A).

GCC has some support for zero length arrays; you might see the struct hack with an array dimension of 0. That is not portable to any compiler that is not mimicking GCC.

It's called the 'struct hack' because it is not guaranteed to be portable by the language standard (because you are accessing outside the bounds of the declared array). However, empirically, it has 'always worked' and probably will continue to do so. Nevertheless, you should use flexible array members in preference to the struct hack.


ISO/IEC 9899:2011 §6.7.2.1 Structure and union specifiers

¶3 A structure or union shall not contain a member with incomplete or function type (hence, a structure shall not contain an instance of itself, but may contain a pointer to an instance of itself), except that the last member of a structure with more than one named member may have incomplete array type; such a structure (and any union containing, possibly recursively, a member that is such a structure) shall not be a member of a structure or an element of an array.

¶18 As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member. In most situations, the flexible array member is ignored. In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply. However, when a . (or ->) operator has a left operand that is (a pointer to) a structure with a flexible array member and the right operand names that member, it behaves as if that member were replaced with the longest array (with the same element type) that would not make the structure larger than the object being accessed; the offset of the array shall remain that of the flexible array member, even if this would differ from that of the replacement array. If this array would have no elements, it behaves as if it had one element but the behavior is undefined if any attempt is made to access that element or to generate a pointer one past it.

Upvotes: 6

Useless
Useless

Reputation: 67733

struct A {
    int i;
    char buf[];
};

does not reserve any space for the array, or for a pointer to an array. What this says is that an array can directly follow the body of A and be accessed via buf, like so:

struct A *a = malloc(sizeof(*a) + 6);
strcpy(a->buf, "hello");
assert(a->buf[0] == 'h');
assert(a->buf[5] == '\0';

Note I reserved 6 bytes following a for "hello" and the nul terminator.

The pointer form uses an indirection (the memory could be contiguous, but this is neither depended on nor required)

struct B {
    int i;
    char *buf;
};

/* requiring two allocations: */
struct B *b1 = malloc(sizeof(*b1));
b1->buf = strdup("hello");

/* or some pointer arithmetic */
struct B *b2 = malloc(sizeof(*b2) + 6);
b2->buf = (char *)((&b2->buf)+1);

The second is now laid out the same as a above, except with a pointer between the integer and the char array.

Upvotes: 1

Related Questions