在C语言中,可变长度数组是如何编译的
我有一些给定的代码在C中的堆栈上使用可变长度数组。我无法轻松地将这些代码更改为在堆上使用在C语言中,可变长度数组是如何编译的,c,gcc,assembly,compilation,arm,C,Gcc,Assembly,Compilation,Arm,我有一些给定的代码在C中的堆栈上使用可变长度数组。我无法轻松地将这些代码更改为在堆上使用malloced缓冲区,因为我正在处理一些操作系统不支持动态内存的东西。然而,当我测试我的方法时,堆栈实际上被破坏了(即SPget被设置为一个完全虚假的地址)。为了弄清楚发生了什么,我看了一下程序集,被编译器的输出完全弄糊涂了 我正在Pandes(OMAP4460 ARM处理器)上运行代码 为了测试堆栈上的可变大小数组实际上是如何编译的,我制作了一个简单的工作测试程序。然而,代码还是太混乱了,我无法理解 我不
malloc
ed缓冲区,因为我正在处理一些操作系统不支持动态内存的东西。然而,当我测试我的方法时,堆栈实际上被破坏了(即SP
get被设置为一个完全虚假的地址)。为了弄清楚发生了什么,我看了一下程序集,被编译器的输出完全弄糊涂了
我正在Pandes(OMAP4460 ARM处理器)上运行代码
为了测试堆栈上的可变大小数组实际上是如何编译的,我制作了一个简单的工作测试程序。然而,代码还是太混乱了,我无法理解
我不明白的是:
当我编译一个简单函数时:
int test_method(size_t i)
{
unsigned char buffer[10];
return -1;
}
我得到了一些非常简单的汇编代码:
80003a68 <test_method>:
80003a68: b480 push {r7} ; store frame pointer
80003a6a: b087 sub sp, #28 ; make stack frame (size 28)
80003a6c: af00 add r7, sp, #0 ; set frame pointer
80003a6e: 6078 str r0, [r7, #4]; store parameter on stack
80003a70: f04f 0300 mov.w r3, #0 ; load return value
80003a74: 4618 mov r0, r3 ; put return value in return register
80003a76: f107 071c add.w r7, r7, #28 ; destroy stack frame
80003a7a: 46bd mov sp, r7 ; ...
80003a7c: bc80 pop {r7} ; pop stored frame pointer
80003a7e: 4770 bx lr ; return
我得到这个集合:
80003a68 <test_method>:
80003a68: e92d 03f0 stmdb sp!, {r4, r5, r6, r7, r8, r9}
80003a6c: b084 sub sp, #16
80003a6e: af00 add r7, sp, #0
80003a70: 6078 str r0, [r7, #4]
80003a72: 4669 mov r1, sp
80003a74: 460e mov r6, r1
80003a76: f8d7 c004 ldr.w ip, [r7, #4]
80003a7a: 4661 mov r1, ip
80003a7c: f101 31ff add.w r1, r1, #4294967295 ; 0xffffffff
80003a80: 60b9 str r1, [r7, #8]
80003a82: 4660 mov r0, ip
80003a84: f04f 0100 mov.w r1, #0
80003a88: f04f 38ff mov.w r8, #4294967295 ; 0xffffffff
80003a8c: f04f 090f mov.w r9, #15
80003a90: ea00 0008 and.w r0, r0, r8
80003a94: ea01 0109 and.w r1, r1, r9
80003a98: ea4f 7850 mov.w r8, r0, lsr #29
80003a9c: ea4f 05c1 mov.w r5, r1, lsl #3
80003aa0: ea48 0505 orr.w r5, r8, r5
80003aa4: ea4f 04c0 mov.w r4, r0, lsl #3
80003aa8: f04f 30ff mov.w r0, #4294967295 ; 0xffffffff
80003aac: f04f 010f mov.w r1, #15
80003ab0: ea04 0400 and.w r4, r4, r0
80003ab4: ea05 0501 and.w r5, r5, r1
80003ab8: 4660 mov r0, ip
80003aba: f04f 0100 mov.w r1, #0
80003abe: f04f 34ff mov.w r4, #4294967295 ; 0xffffffff
80003ac2: f04f 050f mov.w r5, #15
80003ac6: ea00 0004 and.w r0, r0, r4
80003aca: ea01 0105 and.w r1, r1, r5
80003ace: ea4f 7450 mov.w r4, r0, lsr #29
80003ad2: ea4f 03c1 mov.w r3, r1, lsl #3
80003ad6: ea44 0303 orr.w r3, r4, r3
80003ada: ea4f 02c0 mov.w r2, r0, lsl #3
80003ade: f04f 30ff mov.w r0, #4294967295 ; 0xffffffff
80003ae2: f04f 010f mov.w r1, #15
80003ae6: ea02 0200 and.w r2, r2, r0
80003aea: ea03 0301 and.w r3, r3, r1
80003aea: ea03 0301 and.w r3, r3, r1
80003aee: 4663 mov r3, ip
80003af0: f103 0307 add.w r3, r3, #7
80003af4: f103 0307 add.w r3, r3, #7
80003af8: ea4f 03d3 mov.w r3, r3, lsr #3
80003afc: ea4f 03c3 mov.w r3, r3, lsl #3
80003b00: ebad 0d03 sub.w sp, sp, r3
80003b04: 466b mov r3, sp
80003b06: f103 0307 add.w r3, r3, #7
80003b0a: ea4f 03d3 mov.w r3, r3, lsr #3
80003b0e: ea4f 03c3 mov.w r3, r3, lsl #3
80003b12: 60fb str r3, [r7, #12]
80003b14: f04f 0300 mov.w r3, #0
80003b18: 46b5 mov sp, r6
80003b1a: 4618 mov r0, r3
80003b1c: f107 0710 add.w r7, r7, #16
80003b20: 46bd mov sp, r7
80003b22: e8bd 03f0 ldmia.w sp!, {r4, r5, r6, r7, r8, r9}
80003b26: 4770 bx lr
80003a68:
80003a68:e92d 03f0 stmdb sp!,{r4,r5,r6,r7,r8,r9}
80003a6c:b084子sp,#16
80003a6e:af00添加r7,sp,#0
80003a70:6078街r0[r7,#4]
80003a72:4669 mov r1,sp
80003a74:460e mov r6,r1
80003a76:f8d7 c004 ldr.w ip[r7,#4]
80003a7a:4661 mov r1,ip
80003a7c:f101 31ff增补件w r1,r1,#4294967295;0xFFFFFF
80003a80:60b9街r1[r7,#8]
80003a82:4660 mov r0,ip
80003a84:f04f 0100移动带r1,#0
80003a88:f04f 38ff移动带r8,#4294967295;0xFFFFFF
80003a8c:f04f 090f移动带r9,#15
80003a90:ea00 0008和.w r0、r0、r8
80003a94:ea01 0109和.w r1、r1、r9
80003a98:ea4f 7850移动带r8、r0、lsr#29
80003a9c:ea4f 05c1移动带r5、r1、lsl#3
80003aa0:ea48 0505或w r5、r8、r5
80003aa4:ea4f 04c0移动带r4、r0、lsl#3
80003aa8:f04f 30ff移动带r0,#4294967295;0xFFFFFF
80003aac:f04f 010f移动带r1,#15
80003ab0:ea04 0400和.w r4、r4、r0
80003ab4:EA050501和.w r5、r5、r1
80003ab8:4660 mov r0,ip
80003aba:f04f 0100移动带r1,#0
80003abe:f04f 34ff移动带r4,#4294967295;0xFFFFFF
80003ac2:f04f 050f移动带r5,#15
80003ac6:ea00 0004和.w r0、r0、r4
80003aca:ea01 0105和.w r1、r1、r5
80003ace:ea4f 7450移动带r4、r0、lsr#29
80003ad2:ea4f 03c1移动带r3、r1、lsl#3
80003ad6:ea44 0303或w r3、r4、r3
80003ada:ea4f 02c0移动带r2、r0、lsl#3
80003ade:f04f 30ff移动带r0,#4294967295;0xFFFFFF
80003ae2:f04f 010f移动带r1,#15
80003ae6:ea02 0200和.w r2、r2、r0
80003aea:ea03 0301和.w r3、r3、r1
80003aea:ea03 0301和.w r3、r3、r1
80003aee:4663 mov r3,ip
80003af0:f103 0307增补w r3,r3,#7
80003af4:f103 0307增补w r3,r3,#7
80003af8:ea4f 03d3移动带r3、r3、lsr#3
80003afc:ea4f 03c3移动带r3、r3、lsl#3
80003b00:ebad 0d03附属带sp、sp、r3
80003b04:466b mov r3,sp
80003b06:f103 0307增补w r3,r3,#7
80003b0a:ea4f 03d3移动带r3、r3、lsr#3
80003b0e:ea4f 03c3移动带r3、r3、lsl#3
80003b12:60fb str r3[r7,#12]
80003b14:f04f 0300移动带r3,#0
80003b18:46b5 mov sp,r6
80003b1a:4618 mov r0,r3
80003b1c:f107 0710增补w r7,r7,#16
80003b20:46bd mov sp,r7
80003b22:e8bd 03f0 ldmia.w sp!,{r4,r5,r6,r7,r8,r9}
80003b26:4770 bx lr
所有这些额外的逻辑从何而来?我会想,只要将堆栈帧放大
I
(传入r0
)就足够了,可能还需要添加一些额外的代码来保持对齐。但是为什么所有这些附加寄存器都是用0xffffffff
和#15
编写的呢。我真的无法理解编译器给我的这个程序集。这不是一个真正的答案,只是收集了更多不适合注释的信息
typedef unsigned int size_t;
int test_method1(size_t i)
{
unsigned char buffer[10];
return -1;
}
int test_method2(size_t i)
{
unsigned char buffer[i];
return 0;
}
arm none eabi gcc-O2-c tm1.c-o tm1.o
arm none eabi objdump-D tm1.o
基本上优化了一切
00000000 <test_method1>:
0: e3e00000 mvn r0, #0
4: e12fff1e bx lr
00000008 <test_method2>:
8: e3a00000 mov r0, #0
c: e12fff1e bx lr
typedef unsigned int size_t;
unsigned char *test_method1(size_t i, size_t j)
{
unsigned char buffer[10];
return(&buffer[j]);
}
unsigned char *test_method2(size_t i, size_t j)
{
unsigned char buffer[i];
return(&buffer[j]);
}
00000000 <test_method1>:
0: e24dd010 sub sp, sp, #16
4: e28d3004 add r3, sp, #4
8: e0830001 add r0, r3, r1
c: e28dd010 add sp, sp, #16
10: e12fff1e bx lr
00000014 <test_method2>:
14: e92d0808 push {r3, fp}
18: e2800007 add r0, r0, #7
1c: e3c00007 bic r0, r0, #7
20: e28db004 add fp, sp, #4
24: e04dd000 sub sp, sp, r0
28: e08d0001 add r0, sp, r1
2c: e24bd004 sub sp, fp, #4
30: e8bd0808 pop {r3, fp}
34: e12fff1e bx lr
00000000:
0:e3e00000 mvn r0,#0
4:E12FF1E bx lr
00000008 :
8:e3a00000 mov r0,#0
c:E12FF1E bx lr
尽管代码很糟糕,但这会让编译器无法优化所有内容
00000000 <test_method1>:
0: e3e00000 mvn r0, #0
4: e12fff1e bx lr
00000008 <test_method2>:
8: e3a00000 mov r0, #0
c: e12fff1e bx lr
typedef unsigned int size_t;
unsigned char *test_method1(size_t i, size_t j)
{
unsigned char buffer[10];
return(&buffer[j]);
}
unsigned char *test_method2(size_t i, size_t j)
{
unsigned char buffer[i];
return(&buffer[j]);
}
00000000 <test_method1>:
0: e24dd010 sub sp, sp, #16
4: e28d3004 add r3, sp, #4
8: e0830001 add r0, r3, r1
c: e28dd010 add sp, sp, #16
10: e12fff1e bx lr
00000014 <test_method2>:
14: e92d0808 push {r3, fp}
18: e2800007 add r0, r0, #7
1c: e3c00007 bic r0, r0, #7
20: e28db004 add fp, sp, #4
24: e04dd000 sub sp, sp, r0
28: e08d0001 add r0, sp, r1
2c: e24bd004 sub sp, fp, #4
30: e8bd0808 pop {r3, fp}
34: e12fff1e bx lr
typedef无符号整数大小\u t;
无符号字符*测试方法1(大小i,大小j)
{
无符号字符缓冲区[10];
返回(&buffer[j]);
}
无符号字符*测试方法2(大小i,大小j)
{
无符号字符缓冲区[i];
返回(&buffer[j]);
}
00000000 :
0:e24dd010子sp,sp,#16
4:e28d3004添加r3,sp,#4
8:e0830001添加r0、r3、r1
c:e28dd010添加sp,sp,#16
10:E12FF1E bx lr
00000014 :
14:e92d0808推送{r3,fp}
18:E280007加上r0,r0,#7
1c:e3c00007 bic r0,r0,#7
20:e28db004添加fp,sp,#4
24:e04dd000子sp、sp、r0
28:e08d0001添加r0、sp、r1
2c:e24bd004子sp,fp,#4
30:e8bd0808 pop{r3,fp}
34:E12FF1E bx lr
确实如此,我想它也许回答了你的问题。编译器处理可变长度数组的方式是,在本例中,它计算了堆栈指针的数组大小,基本上按照预期在堆栈上分配动态数组。对于静态大小的数组