C语言学习笔记——内存管理

C语言学习笔记

内存管理

隐式类型转换几大原则

  1. 当出现在表达式中,有无符号的char和short会被自动转化成int参与运算。当short与int一样的时候(字节数一样,16位系统),unsigned short会被转化成unsigned int。这一条了解即可
    在K&R标准(C语言最初标准)中,float会被转化成double。
  2. 包含两种数据类型的任何运算中,两个值都会被转化成两种类型中较高的类型,然后参与计算。2+3.4中2会被转化成double在进行运算,同类型的数据才能直接运算。
    类型级别高到低的顺序long double,double,float,unsigned long long,long long,unsigned long,long,unsigned int,int,当long和int有相同大小时(32,64位操作系统都是),unsigned int > long;这排名没有涉及到char、short,因为已经被提升到int或者unsigned int。
    混合运算转换过程:例如3+4/5.0f+6-9.0先运算4/5.0f,所以4转换成4.0f,然后3+0.8f-9.0,先算3+0.8f,3转换成3.0f,然后3.8f+6,6转换成6.0f,最后9.8f-9.0,9.8f转换成9.8,最后答案是0.8(double型)。注意点是不是所有数据都先转换成double再运算。
  3. 再赋值语句中,会被转换成赋值运算符左侧的类型,可能升级,也可能降级。int a = 12.23;这是double型转成int型,降级(数据丢失)。double d = 12;升级
  4. 作为函数参数时,char和short会被自动转换为int,float会被转成double,可通过函数的声明防止提升,这一点我们通常是不用考虑到。

显式类型转换

  • 也就是我们常说的强制类型转换。
  • 写法:(type)数据
  • 强转基本数据类型:12+12.2 这是隐式转换,都转成了double,12+(int)12.2 会先int转换,在进行加法运算。
  • 注意越界:c = (unsigned char)1234.2,通过调试,我们可以看到c的值时210(因为unsigned char的范围是0~255)为得到该数据,首先要去掉小数位,对于数据1234,有两种方法得到210这个数据:1、数据截断:转化成二进制0100 1101 0010,只取后8位,也就是210了;2、1234%256 = 210。
  • 强转指针(地址类型),这也是最常见的强转。
int a = 12;
double *p = (double*)&a;
*p = 21.3;
  • 这样的操作是非法的。int类型数据只占了4字节,但是double占了8字节,这样越界操作是非法的(illegal)。
double a = 12.3;
int *p = (int*)a;
*p = 12;
  • 这样是符合语法的,有点像联合(union),修改了前四字节,读取了前八字节。
int a = 12;
float *p = (float*)&a;
*p = 23.3;
  • 这样的写法也是完全正确的,这是因为float和int都占了4字节的空间。
double d = 12;
int *p = (int*)&d;
*p = 23;	//操作前四个字节
*(p+1) = 34;	//操作后四个字节
*(int*)((short*)p + 1) = 12;	//操作中间四个字节
  • 指针/地址的类型,决定了至臻的读写方式(也就是字节数)。

大小端存储

  • 作用:及算你就存储数据的方式(还有内存对齐)
  • 小端存储(以134480385为例),其存储图示如下:
    小端存储
  • 一般个人计算机基本都是小端存储
  • 大端存储刚好与其相反:
    大端存储
  • ★测试大小端存储两种方式:1、强转:运用前面学到的知识,将其每个字节依次拿出来,然后依次打印(char*),该数的每个字节分别是1 2 4 8可知小端存储。
    2、使用联合:原理和强转比较相似,但是他更方便。
union Un
{
	int a;
	char c[4];
}u = {134480385};
printf("%d,%d,%d,%d"u.c[0],u.c[1],u.c[2],u.c[3]);
  • 这样也能得到1,2,4,8的结果,但是更好理解。

typedef详解

  • 意义:数据类型重命名,也就是别名(类似于枚举enum),使用的方式一模一样。typedef int myint;``typedef myint yourint;这几个xxint都是一模一样的,不管是用法,意义等。
  • 指针重命名:typedef int * pint;这样写法不会出任何问题,也就是说,最后一个没用空格分开的关键词是新的关键词。
  • 结构体重命名:
typedef struct Node
{
	int a;
}_Node;
  • 这里的_Node就是新的名字,也就是说在原来可以定义变量的地方现在无法进行定义了,这样的方式适用于无名结构体“起名字”
  • 函数指针重命名:
void fun(int a, double b)
{}
void (*p)(int,double) = fun;	//这是正常的函数指针定义方法
typedef void (*pFun)(int,double);	//这样是用重命名法重新命名函数指针
pFun p = fun;	//这是该种方法的变量的定义方式

宏define

  • 作用:就像enum给整数命名,typedef给数据类型命名,这个宏可以给一切“重命名”,本质还是替换。
  • 宏的实质是预处理指令
  • 正确写法:#define ONE 1这里的ONE是宏名,1是本体。(宏名一般大写),记得分号一般是不加的。
  • 意义:在进行编译时,宏会被本体替换掉,这是一种傻瓜式替换,如果是一个表达式,那替换前不会进行运算,替换后才开始,所以要注意括号的位置。
    #define PRINT printf ("%d,%d\n"),ONE,1);,这时在任何地方输入PRINT,都会打印1,1。一定要注意分号的有无
    #define THREE后面不加任何东西的宏叫做空宏,可以定义也没有问题,但是意义不大。
  • 表达式宏的注意点:#define ONE 1+1
    printf("%d\n",2*ONE);答案是3。
  • 参数宏:
#define PRINTF(x) printf("%d\n",x);
PRINTF(12)
输出结果是:
12
  • 这说明参数宏类似于函数,可以传递参数(用x,y这类的数表示就可),多个参数用逗号隔开。

宏的几个指示符

  • \:拼接,意思是本行的宏的本体还没写完,下行也属于宏的本体。要求放在本行的最后,并且后面无空格
  • #:字符串指示符,例如#define NUM(x) #x将x解释成字符串,也就是自动带上了双引号,这时候输入x的时候不需要双引号了。
  • ##:字符串拼接符,
#define NUM(x,y) #x ## #y
printf("%s",NUM(a,qwe));
  • 这样的输出结果是aqwe,且#x,#y之间的所有空格都可以不加。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lanciberrr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值