te')); return $arr; } /* 遍历用户所有主题 * @param $uid 用户ID * @param int $page 页数 * @param int $pagesize 每页记录条数 * @param bool $desc 排序方式 TRUE降序 FALSE升序 * @param string $key 返回的数组用那一列的值作为 key * @param array $col 查询哪些列 */ function thread_tid_find_by_uid($uid, $page = 1, $pagesize = 1000, $desc = TRUE, $key = 'tid', $col = array()) { if (empty($uid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('uid' => $uid), array('tid' => $orderby), $page, $pagesize, $key, $col); return $arr; } // 遍历栏目下tid 支持数组 $fid = array(1,2,3) function thread_tid_find_by_fid($fid, $page = 1, $pagesize = 1000, $desc = TRUE) { if (empty($fid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('fid' => $fid), array('tid' => $orderby), $page, $pagesize, 'tid', array('tid', 'verify_date')); return $arr; } function thread_tid_delete($tid) { if (empty($tid)) return FALSE; $r = thread_tid__delete(array('tid' => $tid)); return $r; } function thread_tid_count() { $n = thread_tid__count(); return $n; } // 统计用户主题数 大数量下严谨使用非主键统计 function thread_uid_count($uid) { $n = thread_tid__count(array('uid' => $uid)); return $n; } // 统计栏目主题数 大数量下严谨使用非主键统计 function thread_fid_count($fid) { $n = thread_tid__count(array('fid' => $fid)); return $n; } ?>c - Structure padding clarification for 32-bit and 64-bit architecture - Stack Overflow
最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c - Structure padding clarification for 32-bit and 64-bit architecture - Stack Overflow

programmeradmin4浏览0评论

Understanding Struct Alignment and Padding in 32-bit and 64-bit Architectures

Code:

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

struct struct_a {
    int x;
    float y;
    char a;
    double z;
};

int main(void) {
    printf("sizeof struct %zu\n", sizeof(struct struct_a));
    printf("Offset of x: %lu\n", offsetof(struct struct_a, x));
    printf("Offset of y: %lu\n", offsetof(struct struct_a, y));
    printf("Offset of a: %lu\n", offsetof(struct struct_a, a));
    printf("Offset of z: %lu\n", offsetof(struct struct_a, z));
    return 0;
}

Observed Output:

Compiled for 64-bit (default or -m64 option):

sizeof struct 24
Offset of x: 0
Offset of y: 4
Offset of a: 8
Offset of z: 16
  • 7 bytes of padding after a to align z at offset 16 (which is a multiple of 8).
  • The total struct size is 24 bytes, ensuring proper alignment for arrays.

Compiled for 32-bit (-m32 option):

sizeof struct 20
Offset of x: 0
Offset of y: 4
Offset of a: 8
Offset of z: 12
  • 3 bytes of padding after a to align z at offset 12.
  • The total struct size is 20 bytes, which is not a multiple of 8, and struct elements may not always be naturally aligned in arrays.

Questions:

  1. Why is double placed at offset 12 in the 32-bit version when it's an 8-byte type?

    • I expected double to be aligned to an 8-byte boundary, but in 32-bit mode, it's aligned at 12 instead. I understand that a 32-bit system processes data in 4-byte chunks per cycle—does this influence the alignment of double?
  2. How should I explain structure padding in a job interview?

    • Given that compiler behavior may differ across platforms, what is the best way to frame my answer when asked about struct alignment and padding? Should I focus on general rules (like aligning to the largest type) or explain that alignment can vary depending on architecture and compiler optimizations?

I have checked the following stack overflow discussions, but they seem it more complicated examples for me to understand since I'm trying to understand as a beginner.

  • Struct padding difference in 32 bit and 64 bit architecture with uint64_t
  • Confusion in structure member alignment in 32 bit and 64 bit architecture

Edit:

I compiled -m32 and -m64 output with some code changes on my ubuntu system.

// use the same struct_a form the above code,
a.x = 0xFFFFFF; // 2 nibble should show 0x00 and rest all 0xff
a.c = 'A';
*(uint32_t *)&a.y = 0xFFFFFFFF;  // Force 0xff bytes for float
*(uint64_t *)&a.z = 0xFFFFFFFFFFFFFFFF;  // Force 0xff bytes for double

Memory layout from GDB for -m32 compiled binary

$1 = {x = 16777215, y = -nan(0x7fffff), c = 65 'A', z = -nan(0xfffffffffffff)}
(gdb) x/20bx &a
0x56559008 <a>:         0xff    0xff    0xff    0x00    0xff    0xff    0xff    0xff
0x56559010 <a+8>:       0x41    0x00    0x00    0x00    0xff    0xff    0xff    0xff
0x56559018 <a+16>:      0xff    0xff    0xff    0xff
// 0x56559008 x, 0x56559008 y, 0x56559010 a, < 3 byte padding >, 0x56559010 z.
(gdb) p &a.x
$6 = (int *) 0x56559008 <a>
(gdb) p &a.y
$7 = (float *) 0x5655900c <a+4>
(gdb) p &a.c
$8 = 0x56559010 <a+8> "A"
(gdb) p &a.z
$9 = (double *) 0x56559014 <a+12>
  • It seems the struct field alignment is based on 4 byte and it does not matter if struct field are aligned according to multiple of their size.

I tried declaring a as an array of 2 elements and there is no strict alignment of data with their multiples.

(gdb) x/40bx &a
0x56559040 <a>: 0xff    0xff    0xff    0x00    0xff    0xff    0xff    0xff
0x56559048 <a+8>:       0x41    0x00    0x00    0x00    0xff    0xff    0xff    0xff
0x56559050 <a+16>:      0xff    0xff    0xff    0xff    0xff    0xff    0xff    0x00
0x56559058 <a+24>:      0xff    0xff    0xff    0xff    0x41    0x00    0x00    0x00
0x56559060 <a+32>:      0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
(gdb) p &a[0].z
$2 = (double *) 0x5655904c <a+12>
(gdb) p &a[1].x
$3 = (int *) 0x56559054 <a+20>

Memory layout from GDB for -m64 compiled binary

$1 = {x = 16777215, y = -nan(0x7fffff), c = 65 'A', z = -nan(0xfffffffffffff)}
(gdb) x/24bx &a
0x555555558010 <a>:     0xff    0xff    0xff    0x00    0xff    0xff    0xff    0xff
0x555555558018 <a+8>:   0x41    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x555555558020 <a+16>:  0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
// 0x555555558010 x, 0x555555558014 y, 0x555555558018 c, < 7 byte padding >, 0x555555558020 z  
(gdb) p &a.x
$2 = (int *) 0x555555558010 <a>
(gdb) p &a.y
$3 = (float *) 0x555555558014 <a+4>
(gdb) p &a.c
$4 = 0x555555558018 <a+8> "A"
(gdb) p &a.z
$5 = (double *) 0x555555558020 <a+16>
  • Alignment is based on 8 byte, and even though it's 64 bit it does not matter if the address is multiple of 8 or not.

So I tried executing the same for array, by declaring a[2] and assigning the same initialized values.

    a[0].x = 0xFFFFFF; a[1].x = 0xFFFFFF;
    a[0].c = 'A', a[1].c = 'A';
    *(uint32_t *)&a[0].y = 0xFFFFFFFF;  // Force 0xff bytes for float
    *(uint32_t *)&a[1].y = 0xFFFFFFFF;  // Force 0xff bytes for float
    *(uint64_t *)&a[0].z = 0xFFFFFFFFFFFFFFFF;  // Force 0xff bytes for double
    *(uint64_t *)&a[1].z = 0xFFFFFFFFFFFFFFFF;  // Force 0xff bytes for double
  • for a[0].z it begins at 0x555555558050 <a+16> end of array element 0
  • for a[1].x it begins at 0x555555558058 <a+24> start of array element 1, address begins at 0x555555558058 which is not a multiple of 8.
// GDB output
(gdb) p a
$1 = {{x = 16777215, y = -nan(0x7fffff), c = 65 'A', z = -nan(0xfffffffffffff)}, {x = 16777215, y = -nan(0x7fffff), c = 65 'A', z = -nan(0xfffffffffffff)}}
(gdb) x/48bx &a
0x555555558040 <a>:     0xff    0xff    0xff    0x00    0xff    0xff    0xff    0xff
0x555555558048 <a+8>:   0x41    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x555555558050 <a+16>:  0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
0x555555558058 <a+24>:  0xff    0xff    0xff    0x00    0xff    0xff    0xff    0xff
0x555555558060 <a+32>:  0x41    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x555555558068 <a+40>:  0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff

Understanding Struct Alignment and Padding in 32-bit and 64-bit Architectures

Code:

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

struct struct_a {
    int x;
    float y;
    char a;
    double z;
};

int main(void) {
    printf("sizeof struct %zu\n", sizeof(struct struct_a));
    printf("Offset of x: %lu\n", offsetof(struct struct_a, x));
    printf("Offset of y: %lu\n", offsetof(struct struct_a, y));
    printf("Offset of a: %lu\n", offsetof(struct struct_a, a));
    printf("Offset of z: %lu\n", offsetof(struct struct_a, z));
    return 0;
}

Observed Output:

Compiled for 64-bit (default or -m64 option):

sizeof struct 24
Offset of x: 0
Offset of y: 4
Offset of a: 8
Offset of z: 16
  • 7 bytes of padding after a to align z at offset 16 (which is a multiple of 8).
  • The total struct size is 24 bytes, ensuring proper alignment for arrays.

Compiled for 32-bit (-m32 option):

sizeof struct 20
Offset of x: 0
Offset of y: 4
Offset of a: 8
Offset of z: 12
  • 3 bytes of padding after a to align z at offset 12.
  • The total struct size is 20 bytes, which is not a multiple of 8, and struct elements may not always be naturally aligned in arrays.

Questions:

  1. Why is double placed at offset 12 in the 32-bit version when it's an 8-byte type?

    • I expected double to be aligned to an 8-byte boundary, but in 32-bit mode, it's aligned at 12 instead. I understand that a 32-bit system processes data in 4-byte chunks per cycle—does this influence the alignment of double?
  2. How should I explain structure padding in a job interview?

    • Given that compiler behavior may differ across platforms, what is the best way to frame my answer when asked about struct alignment and padding? Should I focus on general rules (like aligning to the largest type) or explain that alignment can vary depending on architecture and compiler optimizations?

I have checked the following stack overflow discussions, but they seem it more complicated examples for me to understand since I'm trying to understand as a beginner.

  • Struct padding difference in 32 bit and 64 bit architecture with uint64_t
  • Confusion in structure member alignment in 32 bit and 64 bit architecture

Edit:

I compiled -m32 and -m64 output with some code changes on my ubuntu system.

// use the same struct_a form the above code,
a.x = 0xFFFFFF; // 2 nibble should show 0x00 and rest all 0xff
a.c = 'A';
*(uint32_t *)&a.y = 0xFFFFFFFF;  // Force 0xff bytes for float
*(uint64_t *)&a.z = 0xFFFFFFFFFFFFFFFF;  // Force 0xff bytes for double

Memory layout from GDB for -m32 compiled binary

$1 = {x = 16777215, y = -nan(0x7fffff), c = 65 'A', z = -nan(0xfffffffffffff)}
(gdb) x/20bx &a
0x56559008 <a>:         0xff    0xff    0xff    0x00    0xff    0xff    0xff    0xff
0x56559010 <a+8>:       0x41    0x00    0x00    0x00    0xff    0xff    0xff    0xff
0x56559018 <a+16>:      0xff    0xff    0xff    0xff
// 0x56559008 x, 0x56559008 y, 0x56559010 a, < 3 byte padding >, 0x56559010 z.
(gdb) p &a.x
$6 = (int *) 0x56559008 <a>
(gdb) p &a.y
$7 = (float *) 0x5655900c <a+4>
(gdb) p &a.c
$8 = 0x56559010 <a+8> "A"
(gdb) p &a.z
$9 = (double *) 0x56559014 <a+12>
  • It seems the struct field alignment is based on 4 byte and it does not matter if struct field are aligned according to multiple of their size.

I tried declaring a as an array of 2 elements and there is no strict alignment of data with their multiples.

(gdb) x/40bx &a
0x56559040 <a>: 0xff    0xff    0xff    0x00    0xff    0xff    0xff    0xff
0x56559048 <a+8>:       0x41    0x00    0x00    0x00    0xff    0xff    0xff    0xff
0x56559050 <a+16>:      0xff    0xff    0xff    0xff    0xff    0xff    0xff    0x00
0x56559058 <a+24>:      0xff    0xff    0xff    0xff    0x41    0x00    0x00    0x00
0x56559060 <a+32>:      0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
(gdb) p &a[0].z
$2 = (double *) 0x5655904c <a+12>
(gdb) p &a[1].x
$3 = (int *) 0x56559054 <a+20>

Memory layout from GDB for -m64 compiled binary

$1 = {x = 16777215, y = -nan(0x7fffff), c = 65 'A', z = -nan(0xfffffffffffff)}
(gdb) x/24bx &a
0x555555558010 <a>:     0xff    0xff    0xff    0x00    0xff    0xff    0xff    0xff
0x555555558018 <a+8>:   0x41    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x555555558020 <a+16>:  0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
// 0x555555558010 x, 0x555555558014 y, 0x555555558018 c, < 7 byte padding >, 0x555555558020 z  
(gdb) p &a.x
$2 = (int *) 0x555555558010 <a>
(gdb) p &a.y
$3 = (float *) 0x555555558014 <a+4>
(gdb) p &a.c
$4 = 0x555555558018 <a+8> "A"
(gdb) p &a.z
$5 = (double *) 0x555555558020 <a+16>
  • Alignment is based on 8 byte, and even though it's 64 bit it does not matter if the address is multiple of 8 or not.

So I tried executing the same for array, by declaring a[2] and assigning the same initialized values.

    a[0].x = 0xFFFFFF; a[1].x = 0xFFFFFF;
    a[0].c = 'A', a[1].c = 'A';
    *(uint32_t *)&a[0].y = 0xFFFFFFFF;  // Force 0xff bytes for float
    *(uint32_t *)&a[1].y = 0xFFFFFFFF;  // Force 0xff bytes for float
    *(uint64_t *)&a[0].z = 0xFFFFFFFFFFFFFFFF;  // Force 0xff bytes for double
    *(uint64_t *)&a[1].z = 0xFFFFFFFFFFFFFFFF;  // Force 0xff bytes for double
  • for a[0].z it begins at 0x555555558050 <a+16> end of array element 0
  • for a[1].x it begins at 0x555555558058 <a+24> start of array element 1, address begins at 0x555555558058 which is not a multiple of 8.
// GDB output
(gdb) p a
$1 = {{x = 16777215, y = -nan(0x7fffff), c = 65 'A', z = -nan(0xfffffffffffff)}, {x = 16777215, y = -nan(0x7fffff), c = 65 'A', z = -nan(0xfffffffffffff)}}
(gdb) x/48bx &a
0x555555558040 <a>:     0xff    0xff    0xff    0x00    0xff    0xff    0xff    0xff
0x555555558048 <a+8>:   0x41    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x555555558050 <a+16>:  0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
0x555555558058 <a+24>:  0xff    0xff    0xff    0x00    0xff    0xff    0xff    0xff
0x555555558060 <a+32>:  0x41    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x555555558068 <a+40>:  0xff    0xff    0xff    0xff    0xff    0xff    0xff    0xff
Share Improve this question edited Feb 18 at 2:11 Anton asked Feb 17 at 14:34 AntonAnton 618 bronze badges 14
  • 3 TL;DR it is not as simple as 32 or 64 bits, it is target-specific. On some targets like x86-32 it is more optimal to not risk placing a double across the end of a cache line. On some other target like ARM Cortex M3, also 32 bit, it doesn't matter because there's no cache and no double precision FPU. – Lundin Commented Feb 17 at 14:43
  • 1 @Jabberwock, When I load your demo and add -m32 to the executor, it gives 20. I think you ran your test incorrectly – ikegami Commented Feb 17 at 15:34
  • 3 @Anton, Re "If the interviewer asks you to draw the memory layout", Then you tell them it depends on the compiler, the target machine, and directives to the compiler. Then suggest they might add padding to align the fields based on size, and that they may add padding to end to ensure alignment is preserved in arrays. You can even provide an example layout based on field size, but whether the compiler will use that that layout or not depends on its engineers, the target machine and directives to the compiler. – ikegami Commented Feb 17 at 15:41
  • 1 @KamilCuk, it's x86_64 & gcc: x86_64-linux-gnu – Anton Commented Feb 17 at 17:16
  • 1 Hi, How should I explain structure padding in a job interview? is for me a broad question that more depends on interpersonal skills then actual "padding skillz". I usually try to talk as much as possible on interviews. Bottom line the interviewer wants to know you know basics of C and machine architecture, so you should show that you know them. Relevant reading is catb./esr/structure-packing . Should I focus on general rules For me that depends on the position you are interviewing on. Embedded programmer will be different vs high performance. – KamilCuk Commented Feb 17 at 17:27
 |  Show 9 more comments

2 Answers 2

Reset to default 3

Why is double placed at offset 12 in the 32-bit version when it's an 8-byte type?

Because it is specified that way.

Because document System V i386 ABI specification says alignment of double is 4 in this https://www.uclibc./docs/psABI-i386.pdf document in table2.1 on page 8. So next from 8 is 12.

But document System V AMD64 ABI specifies alignment of double to be 8 according to this https://refspecs.linuxbase./elf/x86_64-abi-0.99.pdf document table 3.1 on page 12.

The compiler creates structures according to an specification. All compilers use the same specifications, so the generated code can talk to each other.

does this influence the alignment of double?

Maybe there is i386 rationale but I wasn't able to find one. Reading Alignment of a struct with two doubles is 4 even though double is aligned to 8 (32bit) it would be better to align doubles on 8 on i386. But i386 ABI is what it is and is very very old and compilers want to produce portable output.

2. How should I explain structure padding in a job interview?

Given that compiler behavior may differ across platforms, what is the best way to frame my answer when asked about struct alignment and padding? Should I focus on general rules (like aligning to the largest type) or explain that alignment can vary depending on architecture and compiler optimizations?

You should show that you understand the alignment requirements for specific types larger than char that may lead to padding between structure members and/or after the last member, but never before the first member.

The alignment requirements are target specific, more precisely ABI specific to ensure interoperability among different compilers that implement structures according to the C source definition. You can use as an example the compiler that produces an executable and the kernel that implements a structure in kernel space that is copied to user space via a system call (stat or ioctl calls). These can use different programming languages and compilers but must adhere to the same ABI.

Padding cause structures to use extra space in memory, which may result in early memory exhaustion or other performance reduction. When designing your own structures you might be able to reduce padding by reordering members, grouping small types together. Optimizing the structure layout for 32 and 64 bit ABIs is not very difficult, but explaining why and how will show your interviewer a good skill level in the C language, which can be seen from your detailed question. You did not know about the specifics of double alignment on 32-bit intel targets, that's no big deal and now you know. Keep learning, that's a very valuable skill.

发布评论

评论列表(0)

  1. 暂无评论