43

I use sizeof to get size of a struct in C, but the result I got is unexpected.

struct sdshdr {
    int len;
    int free;
    char buf[];
};


int main(){
    printf("struct len:%d\n",(sizeof(struct sdshdr)));
    return 0;
} //struct len:8, with or without buf

my question is why does buf not occupy any space and why is the size of the int type still 4 on a 64-bit CPU?

here is the output from gcc -v:

Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)
Target: x86_64-apple-darwin14.4.0
Thread model: posix  
dandan78
  • 13,328
  • 13
  • 64
  • 78
ssj
  • 1,737
  • 2
  • 16
  • 28
  • 2
    The `int` will remain 4 bytes, `long` will become the 8 bytes large datatype. To force it, use `int32_t` and `int64_t`, imho it is in `stdint.h`. – Koshinae Jul 09 '15 at 13:28
  • 3
    @Koshinae `long` is not necessarily 8 bytes. It can be the same size as `int`, and it often is. `long long`, on the other hand, is guaranteed to be at least 8 bytes. – Filipe Gonçalves Jul 09 '15 at 13:29
  • 2
    OT: To `printf()` a `size_t` use the conversion specifier `zu` not `d`. – alk Jul 09 '15 at 13:32
  • Also see: [Flexible array member in C-structure](http://stackoverflow.com/questions/3047530/flexible-array-member-in-c-structure) – P.P Jul 09 '15 at 13:35
  • 1
    Related: http://stackoverflow.com/q/20221012/694576 – alk Jul 09 '15 at 13:37
  • @FilipeGonçalves Yes, not neccessarily, it depends on a lot of things. – Koshinae Jul 09 '15 at 13:44
  • 3
    There is no guarantee `int` will be 32 bits. It depends on the ABI (application binary interface) of your platform. Also for the other types. Even `char` can be larger. This is the reason why to use `stdint.h` types if you need a certain size for an object. – too honest for this site Jul 09 '15 at 13:54
  • How much space do you expect `buf` to occupy? – usr Jul 09 '15 at 14:05
  • @whatout: Why would you expect an array declared as `[]` to occupy any space? How much space did you expect it to occupy? – AnT stands with Russia Jul 09 '15 at 14:19
  • @Olaf You're right about `int`, but `sizeof(char)` is *always* 1, by definition - http://stackoverflow.com/questions/2215445/are-there-machines-where-sizeofchar-1. A "C-byte" is defined as the size of `char`, which may be any number of bits >= 8. It's just one of those confusing decisions in design of C. – Luaan Jul 09 '15 at 16:08
  • @Luaan: Were did I state any `sizeof()` unit? (which is the size of a `char` by definition. I know that very well. Read my other answers and comments, I always point that out. Note the number of bits per `char`/`byte` is given by `CHAR_BIT`. I actually do not find that confusing and that make C so very portable actually. There were and are CPUs/DSPs which do not have 8 bit operands. – too honest for this site Jul 09 '15 at 17:22

4 Answers4

53

The [] is a flexible array member. They do not count towards the total size of the struct, because the C standard explicitly says so:

6.7.2.1/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.

This is intentional by design, because the purpose of a flexible array member is to allow you to allocate trailing data dynamically after the struct. (When the struct is a file header, protocol header etc.)

Example including discussion about non-standard gcc extensions and the old pre-C99 "struct hack".

Community
  • 1
  • 1
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 1
    Huh! If you create two of these elements in succession would they be 8 or 16 bytes apart? Where is the pointer stored? Or isn't `buf` a pointer? Then what if I tried to `malloc` space to it?... – Floris Jul 09 '15 at 13:59
  • 3
    @Floris `buf` isn't a pointer; it's simply the end of the struct. By default, `buf[0]` is beyond the struct's space; it's your job to `malloc` data beyond the struct to store whatever you want to put in `buf`. – Colonel Thirty Two Jul 09 '15 at 14:04
  • 1
    @ColonelThirtyTwo - thanks. Do that mean that `&buf[0]==&(struct)+sizeof(struct)`? If so, how would you `malloc` that space? It basically means you can't simply create a variable with the type of this `struct` - you actually have to malloc and cast the pointer returned to the type of the struct, right? Seems messy - but that's what I see in the linked example. – Floris Jul 09 '15 at 14:09
  • 7
    @Floris `malloc(sizeof(mystruct) + sizeof(mystruct.flexiblearray[0]) * numelements)` Yea, you'd have to use `malloc` or `alloca`; a regular stack-allocated struct wouldn't have any space reserved for the array. – Colonel Thirty Two Jul 09 '15 at 14:09
  • @Floris Yes, the _flexible array member_ cannot be used unless you allocate memory for it (e.g., with `malloc`). However, other fields of the struct could still be used, e.g., a regular variable of the struct's type could be used to fill in the head of the struct and once the required size becomes known the final struct with the array can be allocated and the fields copied over from the stack variable. – Arkku Jul 09 '15 at 15:43
  • But won't accessing trailing data invoke undefined behavior? – Ruslan Jul 10 '15 at 04:16
  • 2
    @Ruslan Not if you allocate the struct dynamically as shown in the example I linked. – Lundin Jul 10 '15 at 06:08
12

From C99 onwards the size of an array at the end of a struct may be omitted. For purposes of sizeof(struct) this array will appear to have zero size (although its presence may add some padding to the struct), but the intent is for its length to be flexible, i.e., when allocating space for the struct one must allocate the desired amount of extra space for the array at the end. (To avoid going out of bounds, the actual allocated length of the array should be stored somewhere.)

Before C99 it was a fairly common hack to have an array of size 1 (or 0 where allowed by the compiler) at the end of a struct and then allocate more space for it, so C99 made this practice explicitly allowed by introducing the flexible array member with no size given.

Arkku
  • 41,011
  • 10
  • 62
  • 84
2

As a GNU c extension, you have zero-length arrays:

As a GNU extension, the number of elements can be as small as zero. Zero-length arrays are useful as the last element of a structure which is really a header for a variable-length object:

for example, consider this code from The gnu c manual

 struct line
 {
   int length;
   char contents[0];
 };

 {
   struct line *this_line = (struct line *)
     malloc (sizeof (struct line) + this_length);
   this_line -> length = this_length;
 }

In ISO C99, you would use a flexible array member, which is slightly different in syntax and semantics:

  • Flexible array members are written as contents[] without the 0.

  • Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero.

  • Flexible array members may only appear as the last member of a struct that is otherwise non-empty.

  • A structure containing a flexible array member, or a union containing such a structure (possibly recursively), may not be a member of a structure or an element of an array. (However, these uses are permitted by GCC as extensions.)

Raman
  • 2,735
  • 1
  • 26
  • 46
  • 3
    This is incorrect. This is not a zero sized array, it is a flexible array member. Zero sized arrays are _not_ allowed in C (there is a gcc non-standard extension allowing them, but that's a remain from the time before C99). – Lundin Jul 09 '15 at 13:27
  • @Lundin - Must be implementation wiggle room in there. I get an "undefined size for "incomplete array of char buf". It is a compile error, not warning. – ryyker Jul 09 '15 at 13:29
  • @ryyker: It became optional with C11. Try C99 mode (although I do not believe a compiler will kick it off if it supports C99). – too honest for this site Jul 09 '15 at 13:33
  • yes, sorry my mistake, forgot that it is allowed only as a gnu extension.Editing the answer. – Raman Jul 09 '15 at 13:34
  • 2
    @ARBY: You are confusing gcc _zero sized arrays_ and _flexible array member_. As Lundin wrote, these are two similar solutions for the same problem (they actually differ in to reported size). Read the gcc [manual](https://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Zero-Length.html#Zero-Length). zero-sized array should be seen as legacy. Do not use them anymore for new code. – too honest for this site Jul 09 '15 at 13:37
  • @Lundin - I am using C99 extensions. I get same error with either `char buf[0]; or char buf[];`. More importantly at this point, ARBY has demonstrated (with an example) his point, that in his environment, what he says is valid. – ryyker Jul 09 '15 at 13:38
  • 3
    Be that as it may, `buf[0]` is not relevant to the question since the OP has `buf[]`… – Arkku Jul 09 '15 at 13:42
  • @Olaf :Ah! I was very much confused. Thank you very much. – Raman Jul 09 '15 at 13:45
  • 2
    I already removed the downvote.However, I think is is a bad idea to bring this gcc extension into play, as OP explicitly uses a _flexible array member_ already which is the standard's way and the extension does not add any benefit. – too honest for this site Jul 09 '15 at 14:34
  • @ryyker Because it doesn't make sense to use sizeof on any incomplete type, flexible array members are no exception. As for whatever this answer is about, I have no idea. The question is not about gcc extensions. – Lundin Jul 09 '15 at 14:45
  • @ARBY re. your edited answer, it's now factually correct regarding GCC, but it has essentially no connection to the OP's question. I get that you wish to correct your own initial wrong answer, but for anyone not knowing the history of what you originally posted, this looks like a completely off-topic answer that suddenly starts talking about GNU C extensions and zero-length arrays when the question has none of these… – Arkku Jul 09 '15 at 15:34
  • @Arkku But this now explains the reason, why sizeof evaluates to zero. – Raman Jul 09 '15 at 15:47
  • 2
    @ARBY No, it doesn't, the OP has `sizeof(struct)` which is not zero but the size of the struct without the flexible element. This is specified in the C99 standard and has nothing to do with the GNU extension. Meanwhile the highlighted part in your answer says that `sizeof(struct.buf)` evaluates to zero due to the legacy implementation using zero-length arrays, but (as it says right before that in the text you quoted) `sizeof(struct.buf)` is in fact invalid due to the flexible member having an incomplete type (and, e.g., clang gives a compile-time error if you try it). – Arkku Jul 09 '15 at 15:58
  • @Arkku Ya, you were right. It gives an error `invalid application of ‘sizeof’ to incomplete type`. Thanks for your help. Now, should I remove the answer? – Raman Jul 10 '15 at 03:29
0

buf here is a flexible array member Flexible array members have incomplete type, and so the sizeof operator may not be applied whereas original implementation of zero-length arrays, sizeof evaluates to zero.

ameyCU
  • 16,489
  • 2
  • 26
  • 41
  • 3
    That is wrong. It reports a size as if the array does not exist or has zero size (which is has never been allowed by the standard, but is a legacy pre-C99 gcc-extension). – too honest for this site Jul 09 '15 at 13:41
  • Sorry, I had the gcc behaviour in mind. You are right, the standard does not allow to take their size. Howeve,r it _does_ allow to take the size of the containing struct, which will be as if the array had zero size. (so, why not allow to take their size? - strange). Please notice that _zero length arrays_ are not standard. – too honest for this site Jul 09 '15 at 13:50
  • Yes _zero length arrays_ would be used in C99. – ameyCU Jul 09 '15 at 13:59
  • 1
    Please point at where the standard defines them. Actually, the standard explicitly **forbids** a zero for a _constant expression_ as `0` is! – too honest for this site Jul 09 '15 at 14:30
  • http://stackoverflow.com/questions/9722632/what-happens-if-i-define-a-0-size-array-in-c-c/9723093#9723093 – Lundin Jul 09 '15 at 14:37