Why do some code for eg in gstreamer etc use code which has code like

struct xxx
{
int foo;
...
..
char arrURI[1];
char data[0];
}

Recommended Answers

All 14 Replies

Array indexes start at 0.
char arrURI[1]; -- creates an array of size one
-- so there are two indexes 0 and 1
char data[0]; -- creates an initially empty array
Hope that is what you were looking for.

If the array is the last one in the struct, you are probably seeing the struct hack. Prior to C99 it is an unportable trick for allocating arrays in a struct:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct
{
    char s[1];
} String;

int main()
{
    /* allocate 15 extra bytes for the string */
    String *s = malloc(sizeof *s + 15);

    strcpy(s->s, "test string");
    puts(s->s);
    free(s);

    return 0;
}

The idea is that by tacking on extra memory and accessing it by overflowing the last array member, you can save an extra call to malloc:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct
{
    char *s;
} String;

int main()
{
    String *s = malloc(sizeof *s);

    s->s = malloc(15);
    strcpy(s->s, "test string");
    puts(s->s);
    free(s->s);
    free(s);

    return 0;
}

C99 makes the struct hack a legal feature of the language, but I do not think it is a good idea even then because it makes the code less clear. ;)

commented: Thanks for the reply . Now I got the answer. +0

Array indexes start at 0.
char arrURI[1]; -- creates an array of size one
-- so there are two indexes 0 and 1
char data[0]; -- creates an initially empty array
Hope that is what you were looking for.

Thank you very much for responding .

As I understand.
from the example I have given
char arrURI[1] ;
is same to declaring
char arrURI;
BOTH allocates only one byte.

But in real usage arrURI is assigned some string which is more than one byte when it is declared as char arrURI[1].

In char data[0];
data is a constant pointer which cannot be assigned another address and data has no size . So how is it equivalent as creating an empty array.

Hope my question is clear.

char arrURI[1]; -- creates an array of size one
-- so there are two indexes 0 and 1

Actually there is only 1 index, which is 0 in char A[1];
A[0] = 'a'; //ok
A[1] = 'b'; //bug

In the case of zero sized array, it was generally used as an easy way to index into a variable sized buffer ...

struct Header {
    DWORD dwMagic;
    DWORD dwSize;
    BYTE data[];
};

void MyFunction()
{
    Header *pHdr = (Header *)buffer;
 
    Read(buffer, MAX_SIZE);

    switch(pHdr->dwMagic)
   {
        case M_TAG('R','I','F','F') :
            HandleRIFF(pHdr->data,pHdr->dwSize);
            break;
        // ... etc
   }

}

It lets the compiler calculate the offset so you don't have to.

commented: Thanks for the reply +0

Actually there is only 1 index, which is 0 in char A[1];
A[0] = 'a'; //ok
A[1] = 'b'; //bug

Wow, yes you are right. I mistyped there. Thanks for the correction.
With only one element there is only one index, which is 0.

Why can't we just use a pointer. It will do the same
eg : BYTE *byte;

Why can't we just use a pointer.

In C99 because a pointer might not work while an empty array will. Prior to C99, I cannot think of any reason why an array is better than a pointer except for keeping the array size out of the struct size when using sizeof . I think the original struct hack uses an array size of 0, and it evolved to use 1 for compilers that do not allow an array size of 0.

Why can't we just use a pointer. It will do the same
eg : BYTE *byte;

Let's try it with my previous example.

struct Header {
    DWORD dwMagic;
    DWORD dwSize;
    BYTE *data;
};

void MyFunction()
{
    Header *pHdr = (Header *)buffer;
 
    Read(buffer, MAX_SIZE);

    switch(pHdr->dwMagic)
   {
        case M_TAG('R','I','F','F') :
            HandleRIFF(pHdr->data,pHdr->dwSize);
            break;
        // ... etc
   }
}

Now this will crash. Why ?

Now this will crash.

An example that actually compiles would help your case. ;)

It's purely theoretical. You shouldn't have to compile it to see that it will crash.

The buffer will be filled with unknown data after the first two known values so the four bytes at BYTE *data will be some random value which surely will not be the desired pointer value.
The zero sized array version ->data member will always point to the next byte after the known values in the buffer.

You shouldn't have to compile it to see that it will crash.

All I see is undefined behavior from using a bogus pointer. I have debugged many cases where the same problem does not crash, so your theoretical result does not mean anything unless you can back it up with empirical data. The problem is that saying things like this will crash gives beginners a false sense of security that the program will fail spectacularly if they do something wrong. It is just as probable that the program will fail silently and continue running until the damage done is very costly.

All I see is undefined behavior from using a bogus pointer. I have debugged many cases where the same problem does not crash, so your theoretical result does not mean anything unless you can back it up with empirical data. The problem is that saying things like this will crash gives beginners a false sense of security that the program will fail spectacularly if they do something wrong. It is just as probable that the program will fail silently and continue running until the damage done is very costly.

Agreed. Saying it will crash is highly subjective.

I will try to avoid that in the future. I forget that what's 'obvious' to me is not to every one :)

I forget that what's 'obvious' to me is not to every one

We all do. :) For me, obvious now is not always obvious later too. ;)

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.