3

In other words: is it guaranteed that if I have an array allocated this way:

void *arr = calloc(nmemb, sizeof(some_type))

Then elta, eltb, eltc will all point to the same location in memory, which will be the second element of type some_type of this array?

some_type *elta = &((some_type*)arr)[1];
some_type *eltb = ((some_type*)arr)+1;
some_type *eltc = (char*)arr+sizeof(some_type);

The reason I’m asking this is because I’m trying to do a “container” in C, and if this doesn’t hold then I’m out of ideas how to return a pointer to any other element than the first one.

  • 1
    why would there be some padding? when you `malloc(nmemb * sizeof(some_type))` you don't have padding. – Jean-François Fabre May 03 '17 at 17:10
  • 1
    Where is array here? – Sourav Ghosh May 03 '17 at 17:10
  • @Jean-FrançoisFabre I dunno. But I prefer to ask rather than run into UB. –  May 03 '17 at 17:11
  • Nope , my error. I was considering `some_type*` as the type and not `some_type`. No problem with this. – chux - Reinstate Monica May 03 '17 at 17:13
  • 2
    `((some_type*)arr)++` You can't post-increment an rvalue. – ephemient May 03 '17 at 17:15
  • @Jean-FrançoisFabre So I suppose I could as well ask if `malloc(nmemb * sizeof(some_type))` is equivalent to `calloc(nmemb, sizeof(some_type))` and thus an appropriate way to allocate spce for an array. –  May 03 '17 at 17:16
  • When you use pointer variables, arr[i] is the same as *(arr + i). – bruceg May 03 '17 at 17:17
  • @ephemient Sure thing, fixed, thank you. –  May 03 '17 at 17:17
  • Possible duplicate of [Can C arrays contain padding in between elements?](http://stackoverflow.com/questions/1066681/can-c-arrays-contain-padding-in-between-elements) – HolyBlackCat May 03 '17 at 17:19
  • Actually this is a good question, since the it's tricky to find a proper standard reference. – HolyBlackCat May 03 '17 at 17:21
  • 1
    Asking again, __where is the array here__? – Sourav Ghosh May 03 '17 at 17:24
  • @SouravGhosh I intend `arr` to be the array, which would normally be declared as `some_type *arr = calloc(nmemb, sizeof(some_type))`, but must be declared as `void*` instead, if the “container” is to be type-agnostic. But tbh when I saw your comment for the first time, I though you were trying to tell me I was simply doing some senseless shit… Sorry if I misinterpreted this. –  May 03 '17 at 17:28
  • 1
    @gaazkam a pointer is not an array, be aware. :) – Sourav Ghosh May 03 '17 at 17:29
  • @SouravGhosh Right, strictly speaking you’re of course right :) –  May 03 '17 at 17:30
  • Padding or not you can get the address of any element by doing `&(arr[i])`. While saying this I do not want to imply that there *can* be padding. There is **no** padding between array elements. – Ajay Brahmakshatriya May 03 '17 at 18:31
  • @AjayBrahmakshatriya `you can get the address of any element by doing &(arr[i])` AFAIK I can do this only if `arr` is of correct pointer type, if it’s `void*`, then I can’t get the addres of the third element of type `some_type` by writing `&(arr[2])`. –  May 03 '17 at 18:52
  • 1
    @gaazkam, assuming you know the type of the contained element (I read you are implementing a container), you can cast `void*` to `T*` and then do the above mentioned. Anyway you have to cast the `void*` to something (usually char*) before adding anything. Since pointer arithmetic on `void*` is UB. – Ajay Brahmakshatriya May 03 '17 at 18:55
  • @AjayBrahmakshatriya Thank you for your remark. The container code does not know the type, only the `sizeof()` of the type. Only the code using the container knows the type. –  May 03 '17 at 19:01
  • 1
    @gaazkam in that case, casting to `unsigned char*` and adding multiples of sizeof is your only option. – Ajay Brahmakshatriya May 03 '17 at 19:04
  • @AjayBrahmakshatriya Many thanks. However, why did you mention `unsigned char*` and not `char*`? Why does it matter? –  May 03 '17 at 19:05
  • 1
    @gaazkam No particular reason. Both are valid. – Ajay Brahmakshatriya May 03 '17 at 19:16

2 Answers2

6

Yes, it is guaranteed. If padding bytes are added, they are added within struct some_type, but not in between two array elements.

E. g.:

struct S
{
    int n;
    short s;

// this is just for illustration WHERE byte padding (typically) would occur!!!
#if BYTE_ALIGNMENT >= 4
    unsigned char : 0;
    unsigned char : 0;
#endif
};
struct S s[2];
size_t d = (char*)(s + 1) - (char*)s;

With byte alignment adjusted to 4 or 8 (or even larger powers of 2), this struct will have size of 8 and d will be equally 8, with byte alignment set to 1 or 2, the struct will have size of 6 just as will be d...

Note: This is not the only place where padding bytes can occur: If you switched members n and s, padding bytes would be needed in between s and n to get n correctly aligned. On the other hand, no padding bytes would be necessary after n any more as the structure size would assure correct alignment already.

Referring to the standard: C11, 6.2.5.20:

An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type. 36) Array types are characterized by their element type and by the number of elements in the array. [...]

(Highlighting by me!).

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • 1
    What is this syntax: `unsigned char : 8;` ? I’ve never seen such a syntax before. –  May 03 '17 at 17:33
  • What is `BYTE_ALIGNMENT`? Bytes are mever aligned to anything else than bytes, i.e. `1`. And there is not necessarily padding between a `short` and an unsigned char` - typically there is none. – too honest for this site May 03 '17 at 17:36
  • @gaazkam This is a [bit-field](http://www.tutorialspoint.com/cprogramming/c_bit_fields.htm). I used it to be able to add anonymous bytes... – Aconcagua May 03 '17 at 17:41
  • @Olaf Well, see my comment: "this is just for illustration WHERE byte padding would occur" -- the macro does not exist, of course, it shall represent the compiler settings, and the unsigned char bit fields were thought to *represent* the padding bytes. Wasn't that obvious? – Aconcagua May 03 '17 at 17:45
  • @Aconcagua: Padding bytes can occur after **any** field in a structure. There is no guarantee for a specific layout. It depends on the implementation and ABI of the target platform. Also note that C99 is **not the C standard**. It has been canceled 6 years ago with the release of C11. – too honest for this site May 03 '17 at 18:28
  • @Olaf Above was just one single example, and the important point was only that padding occurs *within* the struct. Question was actually not about how alignment is realised in struct layout anyway. -- Standard is easy, though, just exchanging the ciphers (paragraph and text exactly the same in both...). – Aconcagua May 03 '17 at 21:38
  • @Olaf Admitted - correct alignment *could* be realised, too, by inserting the padding bytes in between n and s members (in this case). Never saw a compiler doing this, though... – Aconcagua May 03 '17 at 21:41
  • @Aconcagua: Because it does not make sense and would complicate the compiler without any benefit (and because ABIs state different). But that's not the point of the question. – too honest for this site May 03 '17 at 21:45
0

Data alignment is a complex matter, as illustrated by the following example (that you can even draw to make your own experiments):

#include <stdio.h>
struct A {  /* no padding */
    char a[3];
};
struct B {
    int  a;
    char b[3];
    /* one byte of padding (in 32bit arch) added here */
};
struct C {  /* no padding again */
    char a[4];
    char b[3];
};

struct D {
    char a[3];
    /* one byte of padding to ensure alignment of next field */
    int  b;
    char c[3];
    /* one byte of padding to ensure alignment of whole struct in arrays */
}

#define P(X) printf("sizeof struct %s == %ld\n", #X, sizeof (struct X))
int main()
{
    P(A);
    P(B);
    P(C);
    P(D);
} /* main */

As you see, the required alignment (1 byte) of the struct A type is allowing to place it anywhere in memory and determines that no padding bytes are necessary to determine the sizeof value.

In the second example, we introduced an int value, which forces the whole struct to be word aligned (so an int is accessible in a properly aligned memory address) so this time, the compiler has padded (at the end of the structure) for extra bytes, so any array type of the specified type gets correctly aligned, and its int field is accessed at a valid address.

In the third example, I illustrate that the effect of the padding in the second example is due to the int field present in the struct, as the third example has a same size field, but this time a non requiring alignment field, so no padding has been inserted, as all the individual data types inside need alignment of 1.

The output (on my system, a MAC OSX system) is:

sizeof struct A == 3
sizeof struct B == 8
sizeof struct C == 7
sizeof struct D == 12
Luis Colorado
  • 10,974
  • 1
  • 16
  • 31