美文网首页
内存管理

内存管理

作者: 晓晓桑 | 来源:发表于2020-04-21 18:48 被阅读0次

类型转换

1.隐式转换


image.png

2.显示转换/强制类型转换
浮点型强制转换成int 直接把小数点去掉,保留整数部分

int main(void) {
    printf("%lf,%d\n", 12 + 12.2, 12 + (int) 12.2);//24.200000,24
    return 0;
}

强转指针类型/强转地址类型

指针/地址的类型决定着指针/地址的读写方式(字节数)

int main(void) {
    //正常是操作四字节的内存,强转之后p就操作8字节的内存,所以就越界了
    //把小的转成大的就越界
    int a = 12;
    double *p = (double *) &a;//❌,导致越界
    *p = 24;

    //正常是操作8字节的内存,强转之后操作4字节的内存,操作上不会报错,但是值会有误。
    double b = 12;
    int *p1 = (int *) &b;
    *p = 12;

    //因为int是四字节,float也是四字节,不越界
    int c = 12;
    float *p2 = (float *) &c;
    *p2 = 12.3f;

    //double是8字节的,强转成4字节的
    double d = 12;
    int *p3 = (int *) &d;
    *p = 12;//操作的是前四个字节的内存
    *(p + 1) = 23;//操作的是后面四个字节的内存

    //那么,怎么操作8字节的中间四个字节呢❓
    double e = 12;
    int *p4 = (short *) &e;
    //p4转换成short类型的指针,这种操作是一个字节(转成char *也行)
    //+2:指向第3个字节。 (short *)p4+2
    // (int *) 转成int * 就开始操作4字节的内存啦
    // * 赋值
    *(int *) ((short *) p4 + 2) = 23;

    return 0;
}

大小端存储

字节对齐决定数据在内存中存储的起始地址。
大小端存储决定数据在内存中存储的顺序(计算机存储数据的方式)

小端存储:数据的高位存在内存的高位(高位地址)
image.png
大端存储:数据的高位存在内存的低位(低位地址)
image.png

如何测试系统是大段存储还是小端存储

方法1:强转、方法2:联合

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

union UN {
   int i;
   char c[4];
} u = {134480385};

int main(void) {

   //方法一
   int a = 134480385;//二进制是:1000 100 10 1
   char *p = (char *) &a;
   for (int i = 0; i < 4; ++i) {
       //0x7ffee4082938,1
       //0x7ffee4082939,2
       //0x7ffee408293a,4
       //0x7ffee408293b,8
       //因为0x7ffee4082938最小,小地址存的1,而1是a的数据低位,所以是小端存储
       printf("%p,%d\n", &p[i], p[i]);
   }

   //方法二 联合测试
   for (int i = 0; i < 4; ++i) {
       //0x10933b018,1
       //0x10933b019,2
       //0x10933b01a,4
       //0x10933b01b,8
       //也是小端存储,原理其实是一样的,就是能一次可以读取一个字节就可以了
       printf("%p,%d\n", &u.c[i], u.c[i]);
   }
   return 0;
}

类型重命名 typedef

重命名之后,其实是一模一样的。
作用:
1.增加代码的可读性
2.使用起来更方便

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

typedef int myInt;
typedef myInt hisInt;
typedef unsigned int unint;

int main(void) {
    typedef myInt herInt;//也可以写在里面
    
    myInt a = 1;
    hisInt b = 2;
    printf("%d,%d", a, b);//1,2

    return 0;
}
typedef int myInt;
typedef int myInt;//相同类型,名字可以重复
//typedef double myInt;//不同类型不可以 ❌
int main(void) {
    typedef myInt herInt;

    myInt a = 1;
    printf("%d", a);//1,2

    return 0;
}

指针重命名

typedef int *pint; //int * 指针重命名成pint
int main(void) {
    pint p = NULL;
    return 0;
}

结构体重命名

//方法一
typedef struct stru {
    int a;
} _Node;//请注意:如果重命名,这个地方就不能定义变量了

//方法二
typedef struct stru node;

//结构体更适合无名结构体,这样可以随心所欲的定义结构体
//如果没有重命名,只能在外面声明
typedef struct {
    int a;
} Name;//请注意:如果重命名,这个地方就不能定义变量了

int main(void) {
    _Node s;
    node n;

    Name name1;
    return 0;
}

函数体重命名

void fun(int a, double b) {
    printf("%d,%lf", a, b);
}

//这是函数指针类型void (*)(int , double)
typedef void (*pFun)(int, double);

int main(void) {

    void (*p)(int, double) =fun;//定义一个函数指针
    void (*p1)(int, double) =fun;
    pFun p3 = fun;//使用重命名 定义一个函数指针
    p3(2, 1.3);//函数调用就是函数地址加上参数 //2,1.300000

    return 0;
}

宏 #define

enum是给整数命名,typedef是给类型命名,那么宏可以给一切“重命名”
本质就是:单纯的替换

define:预处理指令

常量宏

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

#define ONE 1
#define ONE 1

int main(void) {
    //ONE 会被替换成1,在计算机眼里,没有ONE,只要1
    //宏后面不能加分号,要不然,会把ONE看成1;
    //宏可以重复,最后那个起作用
    printf("%d", ONE);
    return 0;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define ONE 1+2*3

int main(void) {
    //宏不进行运算,只是替换的作用
    //相当于printf("%d", 1+2*3)
    printf("%d", ONE);//
    return 0;
}

宏是可以相互使用的,但是注意前后顺序,按照正常逻辑去写

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

#define ONE 1
#define TWO 2
#define TWOTWO  TWO //宏可以这样写
#define PRINTF printf("%d,%d\n",ONE,TWO);//宏是可以相互使用的
#define PRINTF2 printf("%d,%d\n",

int main(void) {
    //实际就是:printf("%d,%d\n",1,2)
    PRINTF //1,2
    PRINTF2 ONE,TWO);//1,2
    return 0;
}

define ONE

空宏也可以,相当于定义了变量未初始化,但宏不是变量,可以说是个符号,替代

#define ONE //空宏也可以
int main(void) {
    ONE
    return 0;
}
宏的注意点

宏的表达式一般要加小括号 #define TWO (1+1)

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

#define ONE 1+1

#define TWO (1+1)
int main(void) {
    //单纯的替换:2*1+1=3
    printf("%d\n", 2 * ONE);//3

    //表达式一般要加小括号
    printf("%d\n", 2 * TWO);//4
    return 0;
}

参数宏

参数宏没有类型,只是替换,但是替换后语法要正确哦

//宏没有类型,只是替换
#define PRINTF(x) printf("%d\n",x);

int main(void) {
    //12传给x,x又传给printf的x
    PRINTF(12)//12

    //printf("%d\n",12*12);
    PRINTF(12*12)//144
    return 0;
}

参数宏也要加小括号 #define PRINTF(x) printf("%d\n",(x)*2);

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

//宏没有类型,只是替换
//参数宏一般也要加小括号
#define PRINTF(x) printf("%d\n",(x)*2);
#define PRINTF2(x) printf("%d\n",x*2);
int main(void) {
    //12传给x,x又传给printf的x
    PRINTF(2+2)//8
    PRINTF2(2+2)//6
    return 0;
}

多个参数的宏

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

//宏没有类型,只是替换
//参数宏也要加小括号
#define PRINTF(x, y) printf("%d\n",(x)*(y));

int main(void) {
    PRINTF(2 + 2, 3 + 3)//24
    return 0;
}
标准宏要这么写 #define SUM(x, y) ((x)+(y)) //表达式和参数都加小括号哦
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//表达式和参数都加小括号哦
//标准宏要这么写
#define SUM(x, y) ((x)+(y))
#define SUM2(x, y) (x)+(y)

int main(void) {
    printf("%d\n", SUM(3, 4) * 3);//21
    printf("%d\n", SUM2(3, 4) * 3);//15
    return 0;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


//比较大小
#define MYMAX(x, y) ((x)>(y)?(x):(y))
int main(void) {
    printf("%d\n", MYMAX(5, 4));
    return 0;
}

宏的指示符 \、# 和 ##

  1. \ :拼接
    注意:\后面什么都不能有,空格也不能有
#define SUM() 1+2\
 +3+4

#define PRI printf("aaa");\
printf("bbb");\
printf("ccc");
int main(void) {
    printf("%d\n", SUM());//10
    PRI //aaabbbccc
    return 0;
}

2.#(x) 字符串指示符
不管x是什么,#之后就是个字符串
相当于给x的参数加双引号

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

#define SUM(x) #x
int main(void) {
    //替换成字符串aaaa
    printf("%s\n", SUM(aaaa));//aaaa
    //替换成字符串"aaaa"
    printf("%s\n", SUM("aaaa"));//"aaaa"
    return 0;
}

3.##:字符串拼接

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

//#x 转成字符串,## 拼接在一切
#define ONE(x, y) #x ## #y ## "feh"

int main(void) {

    printf("%s\n", ONE("123", "4"));
    return 0;
}

相关文章

  • iOS内存管理详解

    目录 block内存管理 autorelease内存管理 weak对象内存管理 NSString内存管理 new、...

  • 第10章 内存管理和文件操作

    1 内存管理 1.1 内存管理基础 标准内存管理函数堆管理函数虚拟内存管理函数内存映射文件函数 GlobalMem...

  • 操作系统之内存管理

    内存管理 包括内存管理和虚拟内存管理 内存管理包括内存管理概念、交换与覆盖、连续分配管理方式和非连续分配管理方式(...

  • JavaScript —— 内存管理及垃圾回收

    目录 JavaScript内存管理内存为什么需要管理?内存管理概念JavaScript中的内存管理JavaScri...

  • OC - OC的内存管理机制

    导读 一、为什么要进行内存管理 二、内存管理机制 三、内存管理原则 四、MRC手动内存管理 五、ARC自动内存管理...

  • 3. 内存管理

    内存管理 内存管理包含: 物理内存管理; 虚拟内存管理; 两者的映射 除了内存管理模块, 其他都使用虚拟地址(包括...

  • Go语言——内存管理

    Go语言——内存管理 参考: 图解 TCMalloc Golang 内存管理 Go 内存管理 问题 内存碎片:避免...

  • jvm 基础第一节: jvm数据区

    程序内存管理分为手动内存管理和自动内存管理, 而java属于自动内存管理,因此jvm的职能之一就是程序内存管理 j...

  • 内存管理

    内存管理的重要性。 不进行内存管理和错误的内存管理会造成以下问题。 内存泄露 悬挂指针 OC内存模型 内存管理是通...

  • 11-AutoreleasePool实现原理上

    我们都知道iOS的内存管理分为手动内存管理(MRC)和自动内存管理(ARC),但是不管是手动内存管理还是自动内存管...

网友评论

      本文标题:内存管理

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