zoukankan      html  css  js  c++  java
  • C内存对齐问题-bus error!总线错误!其实是 字符串字面量修改问题!

    最近写个小程序,出现bus error! 

    int main(void)
    {
        /**
         * char :1个字节
         * char*(即指针变量): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器)
         * char 和 char*占用字节不一样,所以 声明char *a = "hello,world! my name is cj", 后面会出现bus error, 即内存不对齐,
         * 其实在linux报段错误才对! 是因为声明为字符串字面量 后不能 对字符串修改!必须声明称字符数组才能后续修改
         */
        char a[] = "hello,world! my name is cj",  *cur, *end, *last;
        char *sep = "oa";
        printf("ori = %s 
    sep = %s 
    ", a, sep);
    
        //指针指向字符串
        cur = last = a;
        end = a + strlen(a) -1;
    
        while(cur <= end)
        {
            //如果不需要删除这个字符
            if(!strchr(sep, *cur))
            {
                *last++ = *cur; //char *a 时,bus error
            }
            
            cur++;
        }
    
        *last = '';
    
        printf("result = %s 
    ", a);
        return 1;
    }
    

    参考深入理解c/c++ 内存对齐

      内存对齐,memory alignment.为了提高程序的性能,数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问(我们将数据读入到寄存器中,并且假设我们的处理器以四个字节为偏移量访问内存(1-4)四个字节,未对齐的数据就会导致内存拼凑);然而,对齐的内存访问仅需要一次访问。

      内存对齐一般讲就是cpu access memory的效率(提高运行速度)和准确性(在一些条件下,如果没有对齐会导致数据不同步现象).依赖cpu,平台和编译器的不同.一些cpu要求较高(这句话说的不准确,但是确实依赖cpu的不同),而有些平台已经优化内存对齐问题,不同编译器的对齐模数不同.总的来说内存对齐属于编译器的问题.

      一般情况下不需要理会内存对齐问题,内存对齐是编译器的事情.但碰到一些问题上还是需要理解这个概念.毕竟c/c++值直接操作内存的语言.需要理解程序在内存中的分布和运行原理.

      总之一句话就是:不要让代码依赖内存对齐.

    1.原因:为什么需要内存对齐.

    1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

    2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐(该类型所占字节的整数倍)。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

    2.内存对齐的规则和范例

      讲述内存对齐之前先看下各种类型的大小,和机器字长及编译器有关系:

      所以,int,long int,short int的宽度都可能随编译器而异。但有几条铁定的原则(ANSI/ISO制订的):

      1 sizeof(short int)<=sizeof(int)

      2 sizeof(int)<=sizeof(long int)

      3 short int至少应为16位(2字节)

      4 long int至少应为32位。 unsigned 是无符号的意思。

      例如: 16位编译器

      char :1个字节

      char*(即指针变量): 2个字节

      short int : 2个字节

      int: 2个字节

      unsigned int : 2个字节

      float: 4个字节

      double: 8个字节

      long: 4个字节

      long long: 8个字节

      unsigned long: 4个字节

      32位编译器

      char :1个字节

      char*(即指针变量): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器)

      short int : 2个字节

      int: 4个字节

      unsigned int : 4个字节

      float: 4个字节

      double: 8个字节

      long: 4个字节

      long long: 8个字节

      unsigned long: 4个字节

      64位编译器

      char :1个字节

      char*(即指针变量): 8个字节

      short int : 2个字节

      int: 4个字节

      unsigned int : 4个字节

      float: 4个字节

      double: 8个字节

      long: 8个字节

      long long: 8个字节

      unsigned long: 8个字节

      成员的内存分配规律:从结构体的首地址开始向后依次为每个成员寻找第一个满足条件的首地址x,该条件是x % N = 0,并且整个结构的长度必须为各个成员所使用的对齐参数中最大的那个值的最小整数倍,不够就补空字节。

    结构体中所有成员的对齐参数N的最大值称为结构体的对齐参数。

    1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值(或默认值)和这个数据成员类型长度中,比较小的那个进行。在上一个对齐后的地方开始寻找能被当前对齐数值整除的地址.
    2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐.主要体现在,最后一个元素对齐后,后面是否填补空字节,如果填补,填补多少.对齐将按照#pragma pack指定的数值(或默认值)和结构(或联合)最大数据成员类型长度中,比较小的那个进行。
    3、结合1、2颗推断:当#pragma pack的n值等于或超过所有数据成员类型长度的时候,这个n值的大小将不产生任何效果。

    两点注意:数组,嵌套结构体.
    数组:
    对齐值为:min(数组元素类型,指定对齐长度).但数组中的元素是连续存放,存放时还是按照数组实际的长度.
    如char t[9],对齐长度为1,实际占用连续的9byte.然后根据下一个元素的对齐长度决定在下一个元素之前填补多少byte.
    嵌套的结构体:
    假设
    struct A
    {
    ......
    struct B b;
    ......
    };
    对于B结构体在A中的对齐长度为:min(B结构体的对齐长度,指定的对齐长度).
    B结构体的对齐长度为:上述2中结构整体对齐规则中的对齐长度.

    例子:
    VC++6.0中n 默认是8个字节,可以修改这个设定的对齐参数
    也可以采用指令:#pragma   pack(xx)控制.

    ......其他参考博客

    参考地址:http://www.jb51.net/article/45406.htm

  • 相关阅读:
    实习感悟——从用户中来,到用户中去
    FineUI PK DWZ
    Java入门到精通——工具篇之Maven概述
    信息论的熵
    菜鸟学习Hibernate——一对多关系映射
    StyleCop学习笔记——默认的规则
    StyleCop学习笔记——自定义规则
    StyleCop学习笔记——初识StyleCop
    好博客收藏
    菜鸟学习Hibernate——简单的增、删、改、查操作
  • 原文地址:https://www.cnblogs.com/thrillerz/p/4505316.html
Copyright © 2011-2022 走看看