0

A question I have on Problem 3.44 of the book Computer Systems: A Programmer's Perspective (3rd edition (6 October 2015)) on data alignment.

The problem:

For each of the following structure declarations, determine the offset of each feild, the total size of the structure, for 8-bit alignment:

struct P1 {short i; int c; int *j; short *d}
...
struct P4 {char w[16]; char *c[2]}
struct P5 {struct P4 a[2]; struct P1 t}

Answer given in the book:

  • struct P1:
i c j d total
0 2 6 14 16
  • struct P4:
w c total
0 16 32
  • struct P5:
a t total
0 24 40

What I don't get is why did struct P4 a[2] in P5 only takes 24 bytes?
Since P5.a is an array of P4 of size 2, shouldn't it occupy 2 * 32 (total size of P4) bytes?


To make sure I quote the book correctly, attaching the screenshots below: enter image description here enter image description here

waynewingorc
  • 169
  • 9
  • 2
    When was that book released? It was a very long time ago that alignment was typically only two bytes. The likely offsets for e.g. `P1` would be something like `4` and `8` for the `c` and `j` variables, then on a 64-bit system the offset for `d` would be `16` (or `12` for a 32-bit system). The size would then probably be `24` (with padding at the end for a 32-bit system). – Some programmer dude Sep 29 '22 at 07:52
  • 1
    And *very* related: [Why isn't sizeof for a struct equal to the sum of sizeof of each member?](https://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member) – Some programmer dude Sep 29 '22 at 07:54
  • 2
    Also the book seems to have some major problems: Besides that it says the pointer `j` is 8 bytes, it also tell that the pointer `d` is *two* bytes. That doesn't match. And of course the problem with `P4` which just can't be 12 bytes large, the `w` member in that structure is 16 bytes on its own. Have you looked online to see if there's some [errata](https://en.wikipedia.org/wiki/Erratum) for the book? – Some programmer dude Sep 29 '22 at 07:58
  • 2
    "What I don't get is why..." Does that mean, you understand why `P1` is only 16 bytes? I don't. – Gerhardh Sep 29 '22 at 07:58
  • 4
    The book is wrong. – Support Ukraine Sep 29 '22 at 08:21
  • 2
    It isn't unreasonable that a book which was released 20 years ago and is currently on the 3rd edition should have been proof-read better. Again, I think a SO black list / hall of shame of anti-recommendations would be helpful to mankind. – Lundin Sep 29 '22 at 08:26
  • OT: The posted code has several syntax errors but perhaps it's not 100% as it is in the book – Support Ukraine Sep 29 '22 at 08:29
  • In that `P1` struct it seems that `d` should actually be `short` instead of `short*`. – Gerhardh Sep 29 '22 at 09:13
  • A publication error in a STEM book! Stop presses!! This is NEWS!!! // sarcasm off – Fe2O3 Sep 29 '22 at 09:42
  • 1
    @Someprogrammerdude Indeed, here are the [errata for this book](https://csapp.cs.cmu.edu/3e/errata.html), but there is no mention of the errors in this problem solution. – Ian Abbott Sep 29 '22 at 10:24

3 Answers3

5

You have a copy of the Global Edition of CS:APP3e.

As we state in the errata web page:

Note on the Global Edition: Unfortunately, the publisher arranged for the generation of a different set of practice and homework problems in the global edition. The person doing this didn't do a very good job, and so these problems and their solutions have many errors. We have not created an errata for this edition.

This is one of the many problems that got messed up.

recreant
  • 51
  • 1
  • But the book is on the **third** edition and it's been out for **twenty** years. Why wasn't this corrected some decade ago? – Lundin Sep 30 '22 at 11:44
  • 1
    Anyway, "the book is wrong" doesn't answer the question. This is a technical Q&A site, answers are supposed to answer the question. – Lundin Sep 30 '22 at 11:46
2
  • P1 is wrong. In case int* j occupies 8 bytes, then on any known real-world computer with 64 bit addressing, short* d will also occupy 8 bytes. For a total of 22 bytes, assuming no padding was inserted after i, which is unlikely. In case padding is inserted, the total size would be 24 bytes.

    On a real-world 64 bit system, the struct layout would be:

    short  i  size: 2   offset:  0
    int    c  size: 4   offset:  4
    int*   j  size: 8   offset:  8
    short* d  size: 8   offset: 16
    
  • What I don't get is why did struct P4 a[2] in P5 only takes 24 bytes?

    It doesn't. P4 = 32 bytes, no padding. 2 * 32 = 64 bytes. P5 will be 64 + 24 = 88 bytes.

If you are quoting the book correctly, then evidently it has not been proof-read from the programmer's perspective...

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • I wrote this little test program yesterday when answering a similar question: https://godbolt.org/z/3YvYrExoY. Just edit the "X macro" list. In case of array members you might have to typedef them in advance. – Lundin Sep 29 '22 at 09:10
  • 1
    That same code modded for `P5`: https://godbolt.org/z/Krhvf488e – Lundin Sep 29 '22 at 09:13
0

A code to test: (Gives the occupation in structure, not the size).

#include <stdio.h>

struct P1
{
    short i;
    int c;
    int *j;
    short *d;
    long long end[];
};

struct P4
{
    char w[16];
    char *c[2];
    long long end[];
};

struct P5
{
    struct P4 a[2];
    struct P1 t;
    long long end[];
};

int main(void)
{
    struct P1 p1;
    printf("size of p1 : %zu\n", sizeof(p1));    
    printf("short i : %zu\n", (size_t)&p1.c - (size_t)&p1.i);
    printf("int c : %zu\n", (size_t)&p1.j - (size_t)&p1.c);
    printf("int *j : %zu\n", (size_t)&p1.d - (size_t)&p1.j);
    printf("short *d : %zu\n", (size_t)&p1.end - (size_t)&p1.d);

    struct P4 p4;
    printf("\nsize of p4 : %zu\n", sizeof(p4));
    printf("char w[16] : %zu\n", (size_t)&p4.c - (size_t)&p4.w);
    printf("char *c[2] : %zu\n", (size_t)&p4.end - (size_t)&p4.c);

    struct P5 p5;
    printf("\nsize of p5 : %zu\n", sizeof(p5));
    printf("struct P4 a[2] : %zu\n", (size_t)&p5.t - (size_t)&p5.a);
    printf("struct P1 t : %zu\n", (size_t)&p5.end - (size_t)&p5.t);

    return 0;
}
  • The flexible member array is just to mark the end.

Following Gerhardh's remark, code with offsetof.

#include <stdio.h>
#include <stddef.h>

struct P1
{
    short i;
    int c;
    int *j;
    short d;
    long long end[];
};

int main(void)
{
    struct P1 p1;
    printf("                   Offset        Size    Occupation\n");
    printf("short i  : %12zu %12zu %12zu\n", offsetof(struct P1, i), sizeof(p1.i), offsetof(struct P1, c));
    printf("int c    : %12zu %12zu %12zu\n", offsetof(struct P1, c), sizeof(p1.c), offsetof(struct P1, j)-offsetof(struct P1, c));
    printf("int *j   : %12zu %12zu %12zu\n", offsetof(struct P1, j), sizeof(p1.j), offsetof(struct P1, d)-offsetof(struct P1, j));
    printf("short *d : %12zu %12zu %12zu\n", offsetof(struct P1, d), sizeof(p1.d), offsetof(struct P1, end)-offsetof(struct P1, d));
    printf("end      : %12zu \n", offsetof(struct P1, end));
    return 0;
}
CGi03
  • 452
  • 1
  • 3
  • 8