简单分析针对ARM平台的C语言程序的编译问题

作者:守夜者 时间:2021-06-19 21:59:13 

  我们知道在C语言编译时,有那么几个常用的优化编译选项,分别是-O0,-O1,-O2,-O3以及-Os。之前一直觉得既然是优化选项,顶多是优化一下逻辑,提高一些效率或者减少一下程序大小而已。很少会觉得它们会影响程序的最终结果。直到最近在ARM平台上发现一个程序里的一个bug,才觉得这些优化选项有时候也没那么智能。或者说针对ARM平台,还没有那么智能。
      首先看这么一段程序,此程序是我将问题简单化的程序:


#include<stdio.h>
#include<string.h>

int main()
{
char buffer[1024] = {0,1,2,3,4,5,6,7};
int iTest = 0x12345678;
int *p = (int *)(buffer + 7);
memcpy(p, &iTest, sizeof(iTest));
printf("%x\n", buffer[6]);
printf("%x\n", buffer[9]);
return 0;
}

乍看之下,觉得这个程序没啥问题。然后我们将此程序文件名称叫point.c。然后分别用交叉编译链进行如下编译:


arm-xxx-linux-gcc point.c -o point0 -O0
arm-xxx-linux-gcc point.c -o point1 -O1
arm-xxx-linux-gcc point.c -o point2 -O2

    最终再分别执行三个程序,结果却有点出人意料:


./point0
6
34
./point1
34
0
./point2
6
0

    只有在-O0,也就是没有优化的情况下,结果才和假想的一致。但是同样的问题在x86平台上却没有问题。
    于是我通过用以下命令,分别来生成不同优化选项下的汇编代码,来确定在ARM平台上编译到底出了什么问题。


arm-xxx-linux-gcc point.c -o point0.s -O0 -S
arm-xxx-linux-gcc point.c -o point1.s -O1 -S
arm-xxx-linux-gcc point.c -o point2.s -O2 -S

    然后对比三个汇编的代码,发现问题出在memcpy这句话上。
    在point0.s中,程序是老老实实的调用的memcpy,然后就将0x12345678老老实实按照字节一个个的放到了buffer+7的位置。
    而在point1.s中程序则是没有调用memcpy,而是用的语句:
    str        r3, [sp, #7]
    而此时r3中存储的就是0x12345678;而由于我采用的ARM平台是32位的,此语句执行时,地址线应该不会发生变化,所以最终的结果是buffer+4到buffer+7的数据被覆盖了,而不是buffer+7到buffer+10的数据被修改。
    而在point2.s中,貌似又针对流水线进行了优化,程序执行顺序会有所变化,在对buffer部分位置赋初值的顺序是在str  r3, [sp, #7]之后,所以buffer+6处的数据反而是正确的6。
    分析到这儿,也许有人会说写个简单的程序,都会因为编译的优化选项不同导致结果不同,那这memcpy是不是就不敢用了?
    其实一般只要有较好的编程习惯的话,都不会遇到此类问题,比如下面的程序:


#include<stdio.h>
#include<string.h>

int main()
{
char buffer[1024] = {0,1,2,3,4,5,6,7};
int iTest = 0x12345678;
char *p = buffer + 7;
memcpy(p, &iTest, sizeof(iTest));
printf("%x\n", buffer[6]);
printf("%x\n", buffer[9]);
return 0;
}

    这段程序其实只是简单的改变了p的类型,就能保证在各种优化下,结果都一样。可见好的编程习惯是有多么的重要。

标签:C语言,编译
0
投稿

猜你喜欢

  • Java中字符串String的+和+=及循环操作String原理详解

    2023-05-13 15:10:35
  • Idea如何导入一个SpringBoot项目的方法(图文教程)

    2022-08-10 22:40:49
  • 微信公众平台开发教程(三) 基础框架搭建

    2022-08-12 00:33:50
  • 使用Spring Cloud Feign远程调用的方法示例

    2021-12-06 10:30:09
  • C#实现简单俄罗斯方块

    2023-06-18 07:18:36
  • SpringBoot实现Excel文件批量上传导入数据库

    2023-11-29 11:13:44
  • C# 动态调用WebService的示例

    2023-07-04 05:40:20
  • 解决RedisTemplate存储至缓存数据出现乱码的情况

    2023-04-20 15:30:58
  • java线程之join方法的使用介绍

    2023-01-15 12:18:40
  • C#递归算法之归并排序

    2023-01-01 14:49:36
  • C#采用OpenXml给Word文档添加表格

    2023-03-10 14:07:13
  • C#模式画刷HatchBrush用法实例

    2021-10-01 08:59:45
  • Unity 制作一个分数统计系统

    2021-11-30 03:01:52
  • C#实现汉字转换为拼音缩写的代码

    2021-11-21 04:04:51
  • springboot如何重定向外部网页

    2022-11-12 05:19:19
  • java线程同步操作实例详解

    2023-11-13 14:19:08
  • C#获取汉字字符串拼音首字母的方法

    2022-09-06 14:01:12
  • SpringMVC+Mybatis实现的Mysql分页数据查询的示例

    2023-11-24 20:53:33
  • C#怎样实现文件下载断点续传

    2023-02-23 19:11:41
  • Java中Lombok常用注解分享

    2023-06-13 01:32:48
  • asp之家 软件编程 m.aspxhome.com