• C语言深入学习系列


      用C语言写程序时需要知道是大端模式还是小端模式。

             所谓的大端模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;所谓的端模,是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中

     

             为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit。但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如果将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。例如一个16bit的short型x,在内存中的地址为0x0010,x的值为0x1122,那么0x11为高字节,0x22为低字节。对于大端模式,就将0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x2211中小端模式,刚好相反,还是ox1122。我们常用的X86结构是小端模式,而KEIL C51则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式。

     

            Java、.NET大行其道的今天,C语言作为一门经典的高级语言,存在的唯一理由就是其高效、精练。随着PC硬件升级和降价,C语言由于其自身的复杂度,在开发PC应用软件时,已经很少使用。但是在开发嵌入式系统软件和操作系统时,由于普遍强调微内核,少占用空间和高效,因此,在系统开发舞台上,C语言依旧是主角

          在实际的程序开发中,为了提高数据的读取效率,在内存资源足够的情况下,一般在定义数据结构时,应该考虑四字节对齐,其原因很简单,现在的计算机多数是32位,也就是四字节。在每次读取数据时,一般都是直接读取32位的数据。有些情况下,字节对齐的数据结构,要比非对齐的数据结构占用空间少。以下分别就这两方面举例阐述。
          1、充分考虑四字节对齐,可以节省存储空间
             typedef struct tagAAA{
                   char name[10];
                   long sno;
                   char sex;
                   float score[4];
             }AAA;
             typedef struct tagBBB{
                   char name[10];
                   char sex;
                   long sno;
                   float score[4];
            }BBB;
            在VC下,调试,可以很容易看出来,AAA占的存储空间为36,BBB占的存储空间为32。原因很简单,在四字节对齐的情况下,按四个字节为单位分配存储空间,如果不足,会自动补充,本次分配不足以存放下面的变量时,会重新分配空间。
            AAA:
                  |name[0]|name[1]|name[2]|name[3]|
                  ------------------------------------
                  |name[4]|name[5]|name[6]|name[7]|
                  ------------------------------------
                  |name[8]|name[9]|             |                   |
                            ----------由于剩下的两个字节不足以存放sno(long占四个字节),所以重新分配
                  ------------------------------------
                  |                        sno                                       |
                              ----------long变量占四个字节,32bits
                  ------------------------------------
                  |sex        |    自动填充                                |
                              ----------剩余三个字节的空间,不足以重放一个float变量,因此重新分配
                  ------------------------------------
                  |                  score[0]                                     |
                  ------------------------------------
                  |                  ..........                                        |
                  ------------------------------------
                  |                  score[3]                                     |
                  ------------------------------------
                   由此可以轻易的计算出,AAA占36个字节,同理,很容易计算出BBB占32个字节空间。
               
             2、字节对其的情况下,可以更高效的访问
                   假设一个结构体的数据如下存储:
                  -----------------------------------------------------
                  |        12        |       34       |        56        |        78         |   -----------(A)
                  -----------------------------------------------------  
                  -----------------------------------------------------
                  |        XX        |       YY       |        12       |         34        |   -----------(B)
                  -----------------------------------------------------  
                  |        56        |        78       |       XX       |         YY        |
                  在A情况下,一次性读取数据成功,但是,在B情况下,需要读取数据两次,由此,可看出效率的差异。
              一般情况下,字节对齐遵从系统字节数与要求的对齐字节数相比,最小原则,即:假设要求按八字节对齐,但是系统为32位系统,则按照4字节对齐。在四字节对齐时,局部会按照2字节对齐,如:
                 struct tagAAA
                 {
                         char a;
                         short b;
                         char c;
                 }AAA;
    结构体占据的空间为8字节而不是4字节,原因就是:
                        -----------------------------------
                        |    a     |          |             b              |
                        -----------------------------------
                        |   c      |                                      |
    而不是:
                        ------------------------------------
                        |    a     |          b          |        c         |
                        ------------------------------------
    其原因就是局部会以2字节对齐
     
    ***********************************************
     
           众所周知,C语言程序设计中,内存的分配和管理完全交由程序员来控制,因此,内存管理是每个C程序员必须熟练掌握的一般而言,分配给进程的内存有四个概念上不同的区域,分别为:代码段、数据段、堆和栈,其中数据段又可以细分为初始化为非零的数据和初始化为零的数据。如下图所示:
               
                -------------------
                |       程序栈          |----------高地址--〉低地址向下增长
                -------------------
                |          堆                |----------向上增长
                -------------------
                |          BSS              |----------数据段
                | 全局和静态变量 |
                -----------------------------低地址
                |     可执行代码     |----------代码段
                -------------------
           可执行指令放在代码段中,任何时刻,内存中只有一份相同程序的指令拷贝,多个实例共享这些代码。初始化为非零的静态数据和全局数据存放在数据段中,运行相同程序的每个进程,有自己的数据段。
            初始化为零(即未初始化的变量,系统自动填充为0;或者初始化为0)的全局数据和静态分配数据存放在进程的BSS区域中,每个运行的进程都有自己的BSS,程序运行的时候,将数据放到数据段中,由此可知,只有初始化为非零的变量才占用空间,所以对于类似static int ss[1024];这样的数组自动用0来填充,它占的空间很小。
         【BSS“Block Started by Symbol”的缩写,意为“以符号开始的块”。BSS 是Unix链接器产生的未初始化数据段。其他的段分别是包含程序代码的“text”段和包含已初始化数据的“data”段。BSS段的变量只有名称和大小 却没有值。此名后来被许多文件格式使用,包括PE。“以符号开始的块”指的是编译器处理未初始化数据的地方。BSS节不包含任何数据,只是简单的维护开始 和结束的地址,以便内存区能在运行时被有效地清零。BSS节在应用程序的二进制映象文件中并不存在,例如:
      unsigned char var; // 分配到.bss节的8位未初始化变量
      unsigned char var2 = 25; // 分配到.data节的8位已初始化变量
     
          堆,动态内存来自于堆,即:通过malloc得到的空间,通常情况下,堆是向上增长的,即:后面分配的地址比前面的地址在数值上大一些。【注意:这里的堆并不是数据结构中的堆,它的分配方式类似于链表】
     
          栈,分配局部变量的地方,函数参数、函数的返回值和返回地址也放在栈空间中,需要特别注意的是,当函数返回后,存储在栈空间中的函数变量“自动消失”,空间被其他函数使用。栈空间是向下增长的。
     
          在C语言中,一般通过malloc/calloc函数分配空间,通过free()函数释放空间,使用realloc()改变已分配空间的大小。
          分配内存的步骤:
          1.申明一个指定类型的指针
          2.计算要分配空间的大小,一般使用函数sizeof()来实现
          3.调用函数malloc()完成空间的申请,将函数的返回值赋给指针变量,
          4.检查返回值是否不为NULL,保证空间分配成功
          5.分配好的空间是没有经过初始化的,其中可能包含一些垃圾信息,因此调用函数memset()将其用0来填充是个好的习惯
          释放内存步骤:
          1.调用函数free()释放掉空间
           注意:
            1.不可以使用free()之后的空间
            2.free()后,最好将指针置为NULL,因为如果不做这步处理,原来的指针依旧指向刚才释放的空间,可以继续操作
             3.避免重复释放空间
     
           在Unix系统上,提供了函数alloca()函数,可以实现在栈空间上分配指定大小的空间,这样的好处是,函数结束后,空间自动释放,不必显式地调用函数free(),但是该函数有很多弊端,比如不可移植等,因此不建议使用。
           有必要提一下malloc、calloc、realloc函数的底层实现,在Linux系统中,提供了brk()和sbrk()函数,上面几个函数就是在这两个函数的基础上实现的
  • 相关阅读:
    关于celery踩坑
    关于git的分批提交pull requests流程
    SymGAN—Exploiting Images for Video Recognition: Heterogeneous Feature Augmentation via Symmetric Adversarial Learning学习笔记
    AFN—Larger Norm More Transferable: An Adaptive Feature Norm Approach for Unsupervised Domain Adaptation学习笔记
    Learning to Transfer Examples for Partial Domain Adaptation学习笔记
    Partial Adversarial Domain Adaptation学习笔记
    Partial Transfer Learning with Selective Adversarial Networks学习笔记
    Importance Weighted Adversarial Nets for Partial Domain Adaptation学习笔记
    Exploiting Images for Video Recognition with Hierarchical Generative Adversarial Networks学习笔记
    improved open set domain adaptation with backpropagation 学习笔记
  • 原文地址:https://www.cnblogs.com/zhishan/p/3314028.html
Copyright © 2020-2023  润新知