美文网首页
linux c的指针与内存

linux c的指针与内存

作者: 白昔月 | 来源:发表于2019-10-08 18:26 被阅读0次

    指针

    指针的声明用*a来表示,指针是保存内存地址的一种数据类型。另外,取地址用&a来做。

    指针的调用和传值

    int a = 100;
    int point_t(int *a)
    ...
    point_t(&a)
    

    内存

    计算机可以控制、接收电流的高(1)低(0)电位或者通(1)断(0)电路,这就产生了计算机能识别的二进制。

    内存的最小单位是字节(Byte,1 Byte = 8 bits,一个字节是八个二进制位),一个字节可以表示00000000至11111111种意义(可以表示0~255元素)。

    一个十六进制的数字可以表示4位二进制的数字(1111 = 0xf)。一个字节,既可以用8个二进制数字表示,也可以用2个16进制的数字表示。

    32位操作系统

    32位os的cpu地址总线是32位,支持232 个地址位,暨寻址空间是32位,最多能访问0~232次方(大约4GB空间)个内存地址,可以理解为只能访问并操纵最多4GB的内存空间。每个内存地址位记录一个唯一的内存地址编号,每个地址编号对应存储一个字节。

    证明:
    232=210 * 210 * 210 * 2 2
    = 1024 byte * 1024 byte * 1024 byte * 4
    = 1kb * 1024 byte * 1024 byte * 4
    = 1mb * 1024 byte * 4
    = 1gb * 4 = 4gb

    内存管理

    内存由os统一管理(编号、规划内存),内存大小的也会将多根内存条合并后统一计算。os还会对多个程序进行统一调度、分配内存空间,防止不同程序对于内存的相互侵占。

    64位os内存管理草图

    例如,64位os,应用程序使用前48位,暨0x7fffffffffffffff以下的内存地址位,系统内核使用后边的16位内存地址位。

    这样把用户的内存和os的内存隔离开,还有个好处就算,os的内存不会被大量占用,避免机器卡住、卡死、死机等状态的发生。因此,只要os可用,就可以用os将关闭应用程序,解决安全等问题。

    内存规划

    如上图所示,os把内存进行了分片,分为系统内核、栈、自由内存区、堆、数据段、代码段

    代码段

    代码被编译后,会存到磁盘中,形成2进制文件。运行这个程序(例如,调用main函数),其实就是os把二进制数据文件(也就是计算机指令)加载到内存代码段中。C语言不能直接操作代码段的地址。

    在代码段,可以知道程序的二进制字节大小。例如定义如下函数:

    p &rect 0x4005a6
    p &quadrate 0x4005dd
    

    用quadrate 的地址高度减去rect的地址高度,就是加载到内存中rect函数的大小

    数据段

    数据段存储静态变量、全局变量、常量,他们都会分配独立的地址,且地址唯一

    堆内存

    栈内存

    栈内存记录变量的地址和值,还会记录程序已经执行了多少行的行号,记录程序当前状态,调用哪个函数,函数中有哪些变量,变量值。

    变量名其实就是地址别名,变量其实就是内存地址,系统通过变量告诉cpu要到哪个地址取数据。

    #include <stdio.h>
    int global = 0;
    
    int rect(int a, int b)
    {
        static int count = 0;
        count++;
        global++;
        int s = a * b;
        return s;
    }
    
    int quadrate(int a)
    {
        static int count = 0;
        count++;
        global++;
        int s = rect(a, a);
        return s;
    }
    
    int main()
    {
        int a = 3;
        int b = 4;
        int *pa = &a;
        int *pb = &b;
        int *pglobal = &global;
        int (*pquadrate)(int a) = &quadrate;
        int s = quadrate(a);
        int s2 = (*pquadrate)(a);//定义一个函数指针
        printf("%d\n", s);
        return 0;
    }
    
    

    操作系统对于内存的管理

    代码端

    gcc编译器对于代码是如何优化的

    函数栈就不一样了,一个函数可用被多次调用,一个变量也会被多次调用

    gcc会优化存储
    我们在 中看到的地址,例如0x7fffffffddfc就是一个整型变量的首地址,然后ddfc、ddfd、ddfe、ddff都是这个整形变量的地址。(因为,一个整形变量占用四个字节,四八32,一个整形是32bits)

    但是 gcc会做优化

    不连续赋值,编译器会优化,为了让cpu操作指令更方便更快,为了提升程序的执行效率,因此,会对源代码进行优化。那么编译之后的指令存储,跟我们编写的代码的顺序可能会不一样。例如,会先定义基本类型的,然后将基本类型的地址连续放到内存中,然后再放其他类型的,都是把同一类型的变量放到一起。这样可用让程序执行更快,至于为什么,下面会说。另外,64位操作系统下,指针占8个字节(32位操作系统,指针占4个字节),因为,是64bits,一个字节存8个bits,因此,需要8个字节才能将全部的地址存储下来。

    栈这个内存空间,保存的就是函数当前运行的状态。包括代码执行到多少行指令,每一个内存空间到底是什么类型的数据,什么数值。在栈中,函数或者变量会被多次调用,每次调用都会分配新的独立的栈地址。函数调用就是先进后厨的栈内存结构。因此,越到栈顶的函数的地址越小。栈是从后向前压栈的。栈,越往后调用的地址越小。从栈顶向下分配的。

    函数指针

    函数指针定义后,也可以调用函数,这种做法经常用在我们做 回调函数来使用

    一个地址,用括号包起来,表示指向一个代码段的函数。

    带*号表示要访问地址对应的字节块是啥

    数组、动态堆类型创建、指针运算

    数组申明的内存排列

    C语言中的数据声明,也是在栈内存中保存的
    c语言不做指针代码的安全检测。因为,地址是连续排放的,因此,指针运算符就用上了

    指针运算

    指针偏移,的运行效率非常高,性能非常好,这样比cpu根据地址总线取地址要更方便一些

    int *p=&a;

    p+=3;表示将指针偏移3个int 变量占位符。

    在指针上做的加个减,其实做的是地址的偏移。

    数组其实就是一种地址的表示

    int array[3];

    可用表示为,int p = array;
    此时,
    p也就指向了array数组的第一个元素的地址。

    因此我们这样写

    p[0] == array[0];
    p[1] == array[1];
    p[2] == array[2];

    因此,在c中,任何用数组操作的地方,都可以用c来代替。因此,数组是一种指针常量。但是 ,指针是指针变量。因此,定义了数组,数组就永远指向某个地址

    但是 ,指针指向的地址是会改变的
    这就是区别。

    字符数组和指针字符串

    字符串 指针 竟然 存在 代码段?

    注意:c语言字符串数组,以\0结尾,什么时候遇到\0也就标识着字符串结束了。

    malloc可以分配地址

    c语言字符串是原始字符串,其实就是字节数组。

    相关文章

      网友评论

          本文标题:linux c的指针与内存

          本文链接:https://www.haomeiwen.com/subject/zjpepctx.html