struct a
{
struct b
{
int i;
float j;
}x;
struct c
{
int k;
float l;
}y;
}z;
Can anybody explain me how to find the offset of int k so that we can find the address of int i?
Use offsetof() to find the offset from the start of z or from the start of x.
offsetof() - offset of a structure member
SYNOPSIS
#include <stddef.h>
size_t offsetof(type, member);
offsetof() returns the offset of the field member from the
start of the structure type.
EXAMPLE
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
int
main(void)
{
struct s {
int i;
char c;
double d;
char a[];
};
/* Output is compiler dependent */
printf("offsets: i=%ld; c=%ld; d=%ld a=%ld\n",
(long) offsetof(struct s, i),
(long) offsetof(struct s, c),
(long) offsetof(struct s, d),
(long) offsetof(struct s, a));
printf("sizeof(struct s)=%ld\n", (long) sizeof(struct s));
exit(EXIT_SUCCESS);
}
You will get the following output on a Linux, if you compile with GCC:
offsets: i=0; c=4; d=8 a=16
sizeof(struct s)=16
It's been 3 years since the question has been asked, I'm adding my answer for the sake of completeness.
The hacky way of getting the offset of a struct member goes like this
printf("%p\n", (void*)(&((struct s *)NULL)->i));
It doesn't look pretty, I can't think of anything in pure C (which can get you the offset of the member, without knowing anything else about the structure. I believe the offsetof macro is defined in this fashion.
For reference, this technique is used in the linux kernel, check out the container_of macro :
http://lxr.free-electrons.com/source/scripts/kconfig/list.h#L18
A more elaborate explanation can be found in this article:
http://radek.io/2012/11/10/magical-container_of-macro/
struct a foo;
printf("offset of k is %d\n", (char *)&foo.y.k - (char *)&foo);
printf("offset of i is %d\n", (char *)&foo.x.i - (char *)&foo);
foo.x.i refers to the field i in the struct x in the struct foo.
&foo.x.i gives you the address of the field foo.x.i.
Similarly, &foo.y.k gives you the address of foo.y.k;
&foo gives you the address of the struct foo.
Subtracting the address of foo from the address of foo.x.i gives you the offset from foo to foo.x.i.
As Gangadhar says, you can use the offsetof() macro rather than the pointer arithmetic I gave. But it's good to understand the pointer arithmetic first.
As already suggested, you should use the offsetof() macro from <stddef.h>, which yields the offset as a size_t value.
For example:
#include <stddef.h>
#include <stdio.h>
#include "struct_a.h" /* Header defining the structure in the question */
int main(void)
{
size_t off_k_y = offsetof(struct c, k);
size_t off_k_z = offsetof(struct a, y.k);
size_t off_i_x = offsetof(struct b, i);
size_t off_i_z = offsetof(struct a, x.i);
printf("k = %zu %zu; i = %zu %zu\n", off_k_y, off_k_z, off_i_x, off_i_z);
return 0;
}
Example output:
k = 0 8; i = 0 0
To find the offset, this is one way we can go about it.
struct a{
struct b
{
int i;
float j;
}x;
struct c
{
int k;
float l;
}y;
}z;
int main(){
struct a* foo = &z;
printf("%d\n", foo); //address of z
printf("%d\n", &(foo->y)); //address of z.y
printf("%d\n", &( (&(foo->y))->k )); //address of z.y.k
int offset_k = (char*)&( (&(foo->y))->k ) - (char*)foo ;
printf("%d\n", offset_k);
return 0;
}
Output would be similar to this:
4225552 //address of z
4225560 //address of z.y
4225560 //address of z.y.k
8 //offset
In this particular case, since int i is the first member of the struct, the base address of the struct will be that of int i as well. Otherwise, you could compute the offset of int i in a similar manner.
int offset_i = (char*)&( (&(foo->x))->i ) - (char*)foo; //0 in this case
NOTE: The offset will be negative or positive depending on how you define it (if it's with respect to base address or member z.y.k). Here, it is defined to be with respect to base address of struct.
Here's a generic solution that works with both newer and older versions of GNU C:
<!-- language: C -->
#if defined(__GNUC__) && defined(__GNUC_MINOR__)
# define GNUC_PREREQ(minMajor, minMinor) \
((__GNUC__ << 16) + __GNUC_MINOR__ >= ((minMajor) << 16) + (minMinor))
#else
# define GNUC_PREREQ 0
#endif
#if GNUC_PREREQ(4, 0)
# define OFFSETOF(type, member) ((int)__builtin_offsetof(type, member))
#else
# define OFFSETOF(type, member) ((int)(intptr_t)&(((type *)(void*)0)->member) )
#endif
UPDATE: Someone asked for a usage example:
struct foo {
int bar;
char *baz;
}
printf("Offset of baz in struct foo = %d\n",
OFFSETOF(foo, baz));
Would print 4, if int compiles into 4 bytes on the architecture of the machine it runs on.
Related
I'm getting this error:
list.c list.h types.h
list.c: In function 'List_push':
list.c:11:23: error: invalid initializer
--- void *values[len] = ls->values);
EDIT:
Now, with my current code (I've done undos/redos that removed somethings), I get this error instead:
Why?
Basically I've a List structure which declares an variable-length array, containing void pointers (what I want is pointers to any data type). You can see it below at the list.h file.
I've tried a mix of changes in list.c (i.e., *values[pos++] = ..., etc...), but doing these changes it only results in worse gcc errors.
wscom.c
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
#include "types.h"
int main() {
List ls;
// TEST: Put a value pointer at index 0
uint8 value = 0x41;
List_push(&ls, 1, &value);
printf("%c",
*(char*) List_getindex(&ls, 0)
);
return 0;
}
types.h
#ifndef hydroTrackerTypesH
#define hydroTrackerTypesH
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned long long uint32;
#endif
list.h (Declarations)
#ifndef hydroTrackerListH
#define hydroTrackerListH
#include "types.h"
typedef struct {
uint32 length;
void *values[];
} List;
void List_push(List *ls, uint8 count, ...);
void *List_getindex(List *ls, uint32 i);
void List_setindex(List *ls, uint32 i, void *v);
#endif
list.c (Defns.)
#include "list.h"
#include "types.h"
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
static size_t PointerSize =
sizeof(void*);
void List_push(List *ls, uint8 count, ...) {
uint32 len = ls->length;
void *values[len] = ls->values;
uint32 sum = len + count;
realloc(&values, sum * PointerSize);
ls->length = sum;
va_list newVals;
va_start(newVals, count);
uint8 pos = len;
while(count--)
values[pos++] = va_arg(newVals, void*);
va_end(newVals);
}
void *List_getindex(List *ls, uint32 i) {
return (void *)(ls->values[i]);
}
//void List_setindex(List *ls, uint32 i, void *v);
This is a little bit long for a comment.
Thus, I make it an answer.
I try to show you how pointers and arrays are related to each other:
#include <stdlib.h>
#include <stdio.h>
int main()
{
/* The compiler allocates space for "Hello" and '\0' (5 + 1 chars)
* and stores the address in aString1.
*/
const char *aString1 = "Hello";
/* The compiler allocates 10 chars and initializes
* it with "World" (and the '\0' for terminator).
*/
const char aString2[10] = "World";
/* The compiler determines length of initializer "I'm here."
* (9 + 1) and allocates the array of appropriate size.
*/
const char aString3[] = "I'm here.";
/* allocate storage for array (3 const char*) */
#if 0 /* the usual way */
const char **array = malloc(3 * sizeof (const char*));
#else /* how Matheus wants to do it */
const char **array = NULL;
array = realloc(array, 3 * sizeof (const char*));
#endif /* 0 */
/* assign contents (using it like an array) */
array[0] = aString1;
array[1] = aString2;
array[2] = aString3;
/* apply array to another variable array2 */
const char **array2 = array; /* assigns the address only */
/* use it: */
printf("array2[0]: '%s', array2[1]: '%s', array2[2]: '%s'\n",
array2[0], array2[1], array2[2]);
/* throw away storage of array (and array2) */
free(array);
/* Attention! array, array2 become wild pointers at this point
* and may not be accessed (except new, valid addresses are assigned).
* However, aString1, aString2, aString3 are still intact.
*/
printf("aString1: '%s', aString2: '%s', aString3: '%s'\n",
aString1, aString2, aString3);
/* done */
return 0;
}
The sample can be tested on ideone.com.
The sample output is:
array2[0]: 'Hello', array2[1]: 'World', array2[2]: 'I'm here.'
aString1: 'Hello', aString2: 'World', aString3: 'I'm here.'
Update:
So, I finally looked again on to the question & answer of Matheus and tried to fix it according to his intention (or how I understood it). I based it on Matheus' implementation and remarked modified codes by comments:
list.h:
#ifndef LIST_H
#define LIST_H
#if 0 /* not necessary to define these types */
#include "types.h"
#else /* they are already available in a (better) portable manner: */
#include <stdint.h>
/* Btw. I had to change:
* uint8 -> uint8_t
* uint32 -> uint32_t
*/
#endif /* 0 */
typedef struct {
uint32_t length;
#if 0 /* gcc ERROR: */
/* list.c:17:3: error: invalid use of flexible array member
* ls->values = NULL;
*/
void *values[];
#else /* (not) 0 */
void **values;
#endif /* 0 */
} List;
void List_init(List *ls);
void List_push(List *ls, uint8_t count, ...);
void* List_getindex(List *ls, uint32_t i);
void List_setindex(List *ls, uint32_t i, void *v);
#endif /* LIST_H */
list.c:
#include "list.h"
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#if 0 /* no need for a variable (with storage */
static size_t PointerSize = sizeof(void*);
#else /* use enum instead (constant) */
enum { PointerSize = sizeof(void*) };
#endif /* 0 */
void List_init(List *ls)
{
ls->length = 0;
/* This is important: */
ls->values = NULL;
/* or 1st realloc() in List_push() may have Undefined Behavior.) */
}
void List_push(List *ls, uint8_t count, ...)
{
uint32_t len = ls->length;
uint32_t sum = len + count;
void **values = realloc(ls->values, sum * PointerSize);
if (!values) {
/* realloc() failed! Bail out before destroying the existing data. */
return;
}
ls->length = sum;
ls->values = values;
/* assign new contents */
va_list newVals;
va_start(newVals, count);
#if 1 /* the readable way: */
int pos = len;
while (count--) values[pos++] = va_arg(newVals, void*);
#else /* the hackish C style way: */
values += len;
while (count--) *values++ = va_arg(newVals, void*);
#endif /* 1 */
va_end(newVals);
}
void* List_getindex(List *ls, uint32_t i)
{
return ls->values[i];
}
wscom.c:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "list.h"
int main()
{
List ls;
/* Put a value pointers at indices 0, 1, 2 */
uint8_t value1 = 0x41, value2 = 0x42;
uint8_t value3[3] = { 0x43, 0x44, 0x45 };
List_init(&ls);
List_push(&ls, 3, &value1, &value2, value3);
/* Check whether list contents can be retrieved again */
if ((*(uint8_t*)List_getindex(&ls, 0)) == 0x41) {
printf("List entry 0 is correct.\n");
}
if ((*(uint8_t*)List_getindex(&ls, 1)) == 0x42) {
printf("List entry 1 is correct.\n");
}
{ uint8_t *values = List_getindex(&ls, 2);
if (values[0] == 0x43
&& values[1] == 0x44
&& values[2] == 0x45) {
printf("List entry 2 is correct.\n");
}
}
/* Done. */
return 0;
}
In one of my comments, I stated that void *values[]; in struct List might be OK. Ahem, I was wrong. gcc remarks this as error when I tried to use it in list.c. So, actually, it is OK but not for what I intend it to use.
Finally, my sample session (using gcc in cygwin on Windows 10):
$ gcc -std=c11 -o wscom wscom.c list.c
$ ./wscom
List entry 0 is correct.
List entry 1 is correct.
List entry 2 is correct.
$
2nd Update:
(I believe) I realized the missing piece of Matheus (considering his Javascript background):
There are no dynamic arrays in C (in opposition to Javascript).
Instead, there are arrays with variable size which may be used only in specific situations:
In C:
Definition of arrays with variable size in global variables is prohibited. (The compiler needs to know how many bytes to allocate for storage.) This does not exclude something like e.g.
int array[] = { 1, 2, 3 };
because the compiler determines the size from the initializer (on the right hand side of =).
Declaration of global arrays without explicit size is possible. (The definition with proper size might/must be done somewhere else. The linker will fail if no proper storage definition can be found.)
A local variable (inside a function, storage class auto but not static or extern) might be declared as array with size determined at runtime (from a variable). This feature was introduced in C99 but not (yet) in C++ (at least not until C++11 incl.)
A function parameter might be declared as array with unknown (or any) size. (This is equal to declaring it as a pointer.)
I found a nice answer about this in SO: Dynamic array allocation on stack in C (which I used to prove my own statements above).
The only supported way to have "dynamic arrays" in C is the usage of the standard library functions malloc()/realloc()/free(). However this is better called "dynamic memory" allocation because this applies to any C type (not only arrays).
Disclaimer:
I apologize if I wrote something rubbish about Javascript. I'm the total newbie in Javascript with very less practical experience...
I have a huge struct array and I created different function to get the struct pointer based on a member's value:
typedef struct {
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
} test_t;
test_t test[10]; // Initialized somewhere else
static test_t* __get_struct_by_a(uint_32_t a) {
for (uint32_t i = 0; i < 10; i++) {
if (test[i].a == a)
return &test[i];
}
return NULL;
}
static test_t* __get_struct_by_b(uint_32_t b) {
...
}
Is there an easy way to tackle this in C instead of create a lookup function for every member?
Following is one way to write a general function to get (find) your first matching struct member.
I tried to keep consistent with your sample code, adding a few #include directives that may be necessary, and a typical fixed-array size #define for TEST_SIZE so that you don't use hard-coded values for the loop indices.
#include <stddef.h>
#include <stdio.h>
#include <string.h>
typedef struct {
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
} test_t;
test_t test[10]; // Initialized somewhere else
#define TEST_SIZE (sizeof(test)/sizeof(test[0]))
static test_t* __get_struct_by_offset(const void* value_ptr, const size_t offset, const size_t field_size)
{
for (uint32_t i = 0; i < TEST_SIZE; i++)
{
if (0 == memcmp(value_ptr, ((unsigned char*)&test[i])+offset, field_size))
{
return &test[i];
}
}
return NULL;
}
You would use it like this:
uint32_t a_value_to_find = 5; /* example field "a" value to find */
uint32_t b_value_to_find = 10; /* example field "b" value to find */
test_t* test_ptr;
/* find specified value for field "a" */
test_ptr = __get_struct_by_offset(&a_value_to_find, offsetof(test_t, a), sizeof(a_value_to_find));
/* find specified value for field "b" */
test_ptr = __get_struct_by_offset(&b_value_to_find, offsetof(test_t, b), sizeof(b_value_to_find));
It's your responsibility to ensure that the data types for *value_ptr and the field at the specified offset are identical, and therefore of the same size (field_size).
To simplify the usage, you could write some macros as shorthand for these calls. For example:
#define GET_A(value) __get_struct_by_offset(&value, offsetof(test_t, a), sizeof(value))
#define GET_B(value) __get_struct_by_offset(&value, offsetof(test_t, b), sizeof(value))
The queries for "a" and "b" are then simplified to:
/* find specified value for field "a" */
test_ptr = GET_A(a_value_to_find);
/* find specified value for field "b" */
test_ptr = GET_B(b_value_to_find);
I am trying to do packed struct in tcc C compiller.
Code is as follows and __attribute __ tag should be supported:
#include <stdio.h>
#include <stdint.h>
typedef struct _test_t{
char c;
uint16_t i;
char d;
} __attribute__((__packed__)) test_t;
int main(){
test_t x;
x.c = 0xCC;
x.i = 0xAABB;
x.d = 0xDD;
const char *s = (const char *) & x;
unsigned i;
for(i = 0; i < sizeof(x); i++)
printf("%3u %x\n", i, 0xFF & s[i]);
return 0;
}
It works in gcc, but not work on tcc.
I also tried __attribute __((packed)) and few other tests - none works.
As you already found the __attribute__ extension applies only to struct's members, so each of them should have it applied individually. Here is your code with minor adaptations, that compiles with tcc 0.9.26 and then runs with correct output:
typedef struct {
char c __attribute__((packed));
unsigned short i __attribute__((packed));
char d __attribute__((packed));
} test_t;
int main(void)
{
test_t x;
printf("%zu\n", sizeof(test_t));
x.c = 0xCC;
x.i = 0xAABB;
x.d = 0xDD;
const char *s = (const char *) &x;
unsigned i;
for (i = 0; i < sizeof(x); i++)
printf("%3u %x\n", i, 0xFF & s[i]);
return 0;
}
Result:
4
0 cc
1 bb
2 aa
3 dd
There is one catch here. As you may already spotted there are no headers. The correctly written code should have:
#include <stdio.h>
#include <stdint.h> // then replace unsigned short with uint16_t
However, with headers the __attribute__ is no longer working. I am not sure if that always happen, but on my system (CentOS 6) it does exactly in that way.
As I found the explanation lies in internal sys/cdefs.h header, that contains:
/* GCC has various useful declarations that can be made with the
`__attribute__' syntax. All of the ways we use this do fine if
they are omitted for compilers that don't understand it. */
#if !defined __GNUC__ || __GNUC__ < 2
# define __attribute__(xyz) /* Ignore */
#endif
so the __attribute__ function-like macro is "washed-up" for tcc, as it does not define __GNUC__ macro. It seems to be some incoherence between tcc developers and standard library (here glibc) writers.
I can confirm that at least with tcc 0.9.26 attribute((packed)) on
struct members is not working. Using Windows-style packing pragmas works just fine:
#if defined(__TINYC__)
#pragma pack(1)
#endif
typedef struct {
uint16_t ..
} interrupt_gate_descriptor_t;
#if defined(__TINYC__)
#pragma pack(1)
#endif
Seems to be error with TCC.
according many sources , including this one, http://wiki.osdev.org/TCC
this should work:
struct some_struct {
unsigned char a __attribute__((packed));
unsigned char b __attribute__((packed));
} __attribute__((packed));
... but it does not work.
Below is a kernel module which can be compiled and run nicely. I have written a method show which gets the pointer to a hashtable and prints it. But if I use the methods argument, b, I get a compilation error. I can only use the global variable a.
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/hashtable.h>
MODULE_LICENSE("GPL");
typedef struct {
int age;
struct hlist_node my_next;
} my_type;
DEFINE_HASHTABLE(a, 3);
my_type *node1, *node2, *node3;
void show(struct hlist_head b[]){
int i;
my_type *temp;
printk(KERN_INFO "a: %p \tb: %p \n", a, b); // Always the same address.
hash_for_each(a, i, temp, my_next){ // Change the a to b
printk(KERN_INFO "%d\n", temp->age);
}
}
int init_module(void){
printk(KERN_INFO "Hi.\n");
node1 = kmalloc(sizeof(my_type), GFP_KERNEL);
node1->age = 28;
node2 = kmalloc(sizeof(my_type), GFP_KERNEL);
node2->age = 27;
node3 = kmalloc(sizeof(my_type), GFP_KERNEL);
node3->age = 20;
show(a);
hash_add(a, &node1->my_next, 0);
hash_add(a, &node2->my_next, 1);
hash_add(a, &node3->my_next, 1);
show(a);
return 0;
}
void cleanup_module(void){
kfree(node1);
kfree(node2);
kfree(node);
printk(KERN_INFO "Bye.\n");
}
The error I get is:
In file included from include/linux/thread_info.h:11:0,
from /usr/src/kernels/3.15.8-200.fc20.x86_64/arch/x86/include/asm/preempt.h:6,
from include/linux/preempt.h:18,
from include/linux/spinlock.h:50,
from include/linux/seqlock.h:35,
from include/linux/time.h:5,
from include/linux/stat.h:18,
from include/linux/module.h:10,
from /home/k/development/hash2/main.c:1:
/home/k/development/hash2/main.c: In function ‘show’:
include/linux/bug.h:33:45: error: negative width in bit-field ‘<anonymous>’
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
^
I have tried playing with the definitions like using struct hlist_head * b[] and using in the hash_for_each macro the *b but non worked.
I also tried defining DEFINE_HASHTABLE(c, 3); inside the method show() as shown below works, but it's quite useless.
void show(struct hlist_head b[]){
int i;
my_type *temp;
DEFINE_HASHTABLE(c, 3);
printk(KERN_INFO "a: %p \tb: %p \n", a, b);
hash_for_each(c, i, temp, my_next){
printk(KERN_INFO "%d\n", temp->age);
}
}
So, why do I get this error? How do I solve it?
hash_for_each is a macro which in turn uses another macro (ARRAY_SIZE) to determine the size of the array passed to hash_for_each as first parameter.
You pass a pointer to hash_for_each as 1st parameter, this makes ARRAY_SIZE choke, as "pointers are not arrays".
To get aorund this, you might like to take the approach proposed by Paul R in his comment to your question, that is implementing show() as a macro as well.
The compilation error you observe is by intention, to keep you from using code not doing what it was intended to do. The error message itself
error: negative width in bit-field ...
however is heavily missleading.
I can use pragma pack on various compilers to force structs to have fields which are not on their natural alignments.
Is this recursive - so suppose struct typedef A contains a field of typedef struct B. If use a pragma to pack A will this force struct B to be packed?
You have to hope not! Lets say you have a function that takes a parameter struct A:
void fn( struct A x ) ;
and then a packed struct B that has struct A as a member:
#pragma pack(1)
struct B
{
struct A a ;
}
If you pass the member a of B to fn(), the function has no knowledge of the packing in this instance, and would fail.
Other answerer's results notwithstanding, I performed the following test on VC++ 2010:
struct A
{
int a;
char b;
int c ;
} ;
struct B
{
struct A d ;
} ;
#pragma pack(1)
struct C
{
struct A d ;
} ;
#pragma pack(1)
struct D
{
int a;
char b;
int c ;
} ;
#pragma pack(1)
struct E
{
struct D ;
} ;
int main()
{
int a = sizeof(struct A) ;
int b = sizeof(struct B) ;
int c = sizeof(struct C) ;
int d = sizeof(struct D) ;
int e = sizeof(struct E) ;
}
Inspection of a, b, c, d and e in main() in the debugger yield:
a = 12
b = 12
c = 12
d = 9
e = 9
The packing of struct C has no effect on the size of its struct A member.
Just don't. The exact behavior of an ugly nonstandard extension is not something you can even ask questions about without specifying an exact platform/compiler you're working with. If you find yourself needing packed structs, you'd be better to figure out why your code is broken and fix it. Packed structs are a band-aid for broken code that do nothing to fix the root cause of the brokenness.
Folowing Nathon's post, I tried about the same thing on my computer:
#include <stdio.h>
#if defined PACK_FIRST || defined PACK_BOTH
#pragma pack(1)
#endif
struct inner {
char a;
double b;
};
#if defined PACK_SECOND || defined PACK_BOTH
#pragma pack(1)
#endif
struct outer {
char a;
struct inner b;
char c;
double d;
};
int main(void) {
printf("sizeof (char): %d (1, of course)\n", (int)sizeof (char));
printf("sizeof (double): %d\n", (int)sizeof (double));
printf("sizeof (inner): %d\n", (int)sizeof (struct inner));
printf("sizeof (outer): %d\n", (int)sizeof (struct outer));
return 0;
}
$ gcc 4128061.c
$ ./a.out
sizeof (char): 1 (1, of course)
sizeof (double): 8
sizeof (inner): 16
sizeof (outer): 40
$ gcc -DPACK_FIRST 4128061.c
$ ./a.out
sizeof (char): 1 (1, of course)
sizeof (double): 8
sizeof (inner): 9
sizeof (outer): 19
$ gcc -DPACK_SECOND 4128061.c
$ ./a.out
sizeof (char): 1 (1, of course)
sizeof (double): 8
sizeof (inner): 16
sizeof (outer): 26
$ gcc -DPACK_BOTH 4128061.c
$ ./a.out
sizeof (char): 1 (1, of course)
sizeof (double): 8
sizeof (inner): 9
sizeof (outer): 19
Apparently my gcc packs from the point where the #pragma line appears.
For the version of GCC I have handy, it looks like yes:
#include <stdio.h>
typedef struct
{
char a;
int b;
}inner_t;
#pragma pack(1)
typedef struct
{
char a;
inner_t inner;
} outer_t;
int main()
{
outer_t outer;
outer.inner.a = 'a';
outer.inner.b = 0xABCDEF01;
printf ("outer.inner.a: %c\n", outer.inner.a);
return 0;
}
And gdb breaking on the printf gives me...
(gdb) x/5xw &outer
0x7fffffffe4b0: 0xffff61a0 0xcdef01ff 0x000000ab 0x00000000
0x7fffffffe4c0: 0x00000000
inner.b is not word aligned. So under GCC 4.4.5 on a little endian 64-bit machine, nested structures are packed if the outer structure is packed. Pardon my typedefs, those of you who don't like them.