More about Struct&Union

关于进程调度的thread_union结构,有些趣味。

More about Struct&Union

这里做个小模拟:

int main(void)

{

    834c:	e24dd020 	sub	sp, sp, #32	; 0x20  //sp指针指向地址最低位,栈增长的最大界限位置

    union {

        struct thread_info thread_info;

        unsigned long stack[8];

    }uni_x;



    uni_x.thread_info.a = 1;

    8350:	e3a03001 	mov	r3, #1	; 0x1

    8354:	e58d3000 	str	r3, [sp]    //这里thread_info.a处在地址最低位

    uni_x.thread_info.b = 2;

    8358:	e3a03002 	mov	r3, #2	; 0x2

    835c:	e58d3004 	str	r3, [sp, #4]  //thead.b的位置

    

    

    uni_x.stack[7] = 7;  //将stack数组的index由高到底操作,实现向低地址方向的栈增长

    8360:	e3a03008 	mov	r3, #7	; 0x7

    8364:	e58d3020 	str	r3, [sp, #28]

    uni_x.stack[6] = 6;  //7,6...1,0,当然这里要考虑thread_info的大小,至少uni_x.stack[0]会覆盖thread_info

    8368:	e3a03007 	mov	r3, #6	; 0x6

    836c:	e58d301c 	str	r3, [sp, #24]



    return 0;

    8370:	e3a03000 	mov	r3, #0	; 0x0

}

“一个结构,两种用法”,重要的是思想,细细咀嚼,回味无穷。

再说下结构体,不得不提的是那个内核界早已闻名的巨星:宏contain_of (),先贴个代码:

#define mem_to_obj(ptr, type, member) \

    ( (char*)ptr - (char*)(&((type*)0)->member) )



int main(void)

{

    struct test_struct {

        int a;

        int b;

    };  



    struct test_struct test;

    test.a = 1;

    test.b = 2;

    int *bp = &test.b;



    struct test_struct *p = &test;

    struct test_struct *q = NULL;



    q = mem_to_obj(&test.b, struct test_struct, b);   //通过结构体中b地址,找到该结构体的地址

        

    return 0;

}

宏展开后如下,简单明了,无需多言。

  
    
q = mem_to_obj( & test.b, struct test_struct, b);  

q
= ( ( char * ) & test.b - ( char * )( & (( struct test_struct * ) 0 ) -> b) );
 瞧一下汇编:
int main(void)

{

    834c:	e24dd018 	sub	sp, sp, #24	; 0x18

        int a;

        int b;

    };



    struct test_struct test;

    test.a = 1;

    8350:	e3a03001 	mov	r3, #1	; 0x1

    8354:	e58d3004 	str	r3, [sp, #4]

    test.b = 2;

    8358:	e3a03002 	mov	r3, #2	; 0x2

    835c:	e58d3008 	str	r3, [sp, #8]

    int *bp = &test.b;

    8360:	e28d3004 	add	r3, sp, #4	; 0x4

    8364:	e2833004 	add	r3, r3, #4	; 0x4

    8368:	e58d300c 	str	r3, [sp, #12]



    struct test_struct *p = &test;

    836c:	e28d3004 	add	r3, sp, #4	; 0x4

    8370:	e58d3010 	str	r3, [sp, #16]

    struct test_struct *q = NULL;

    8374:	e3a03000 	mov	r3, #0	; 0x0

    8378:	e58d3014 	str	r3, [sp, #20]



    q = mem_to_obj(&test.b, struct test_struct, b);

    837c:	e28d3004 	add	r3, sp, #4	; 0x4  //r3已经是test_struct的地址,但之后的两条汇编,感觉无厘头

    8380:	e2833004 	add	r3, r3, #4	; 0x4  //在试着增加test_struct中的变量后发现,仍然是add x,再sub x

    8384:	e2433004 	sub	r3, r3, #4	; 0x4  //可能编译器不是万能的缘故,何况mem_to_obj也得浪费你1s的理解时间

    8388:	e58d3014 	str	r3, [sp, #20]

    

    return 0;

    838c:	e3a03000 	mov	r3, #0	; 0x0

}

最后再介绍个结构体中运用arr[0]的小技巧。

arr[0]是什么?到底有还是没有?

它确实没有,没有占内存空间,当你用它的时候,它却能发挥点作用。ho~ho~有点像鬼魂~

int main(void)

{

    8380:	e52de004 	push	{lr}		; (str lr, [sp, #-4]!)

    8384:	e24dd014 	sub	sp, sp, #20	; 0x14

    struct test_struct {

        int a;

        int b;

        int c[0];       // c是个指针,准确的说是半个指针,或者就是个int * const c,你懂de

    }test;

    int d = 8;      // 根据编译器对变量的分配规则,int d自然排在了test_struct结构体之后,而且是紧挨着

    8388:	e3a03008 	mov	r3, #8	; 0x8

    838c:	e58d300c 	str	r3, [sp, #12]

    printf("1: d = %d\n", d);  // 这里d当然等于8

    8390:	e59f003c 	ldr	r0, [pc, #60]	; 83d4 <main+0x54>

    8394:	e59d100c 	ldr	r1, [sp, #12]

    8398:	ebffffc8 	bl	82c0 <_init+0x48>

    test.a = 1;

    839c:	e3a03001 	mov	r3, #1	; 0x1

    83a0:	e58d3004 	str	r3, [sp, #4]

    test.b = 2;

    83a4:	e3a03002 	mov	r3, #2	; 0x2

    83a8:	e58d3008 	str	r3, [sp, #8]

    //test.c = NULL;  /*作为半个指针,当然也就是不能被赋值了,若赋值,就如下报错*/
    /*main.c:14:12: error: incompatible types when assigning to type ‘int[]’ from type ‘void *’*/
    test.c[0] = 10;      // 虽不能被赋值,但却能指引其他地儿赋值:给紧挨着test_struct的PP的地方赋值10

    83ac:	e3a0300a 	mov	r3, #10	; 0xa

    83b0:	e58d300c 	str	r3, [sp, #12]

    printf("2: d = %d\n", d);    // 很不幸,d就在test_struct的PP的地方,d被间接改变

    83b4:	e59f001c 	ldr	r0, [pc, #28]	; 83d8 <main+0x58>

    83b8:	e59d100c 	ldr	r1, [sp, #12]

    83bc:	ebffffbf 	bl	82c0 <_init+0x48>

    return 0;

    83c0:	e3a03000 	mov	r3, #0	; 0x0

}

结构体中运用arr[0]常用于指向struct's PP的功能。先可意会,实践中才能深入理解。

说到此,想起个事,被调函数中定义的"变量x"会随着被调函数的return而其资源一并销毁。若在销毁后正巧有一个指针能重新操作那个x的位置,是否会发生编译器编译报错呢? 实践的结果是不会。指针指向的x的位置,相对于整个程序来说,仍认为是合法,但最好不要这么做,毕竟不按规则出牌……要不见红涨停,要不套牢割肉……

你可能感兴趣的:(struct)