预备:局部变量存在栈区,且使用习惯:先高地址再低地址,且数组随着下标的增长,地址也由低到高 之前的死循环代码,Release版本下,代码会被直接一次性执行完,没有死循环要暂停,system(""pause),因为代码被优化了 因为在release版本下,代码中变量的存储顺序有改变,i先创建,但在更低的地址,而arr地址在i上面,所以没有死循环
所以release版本可能运行有BUG,但你在debug下测说没问题,因为可能版本不一样
数据在内存中的存储
- 内置:
float f = 0.0; int a = 0; 不同类型的意义:开辟空间大小不一样、内存中看待这两个0的视角不一样(内存中所存不一样,不是全0)
- 整型(分有符号和无符号):char(也算整型,一个字节,8bit)、short、int、long
比如char:unsigned char(最高位是符号位)、signed char 而char到底是signed char还是unsigned char是不确定的,取决于编译器的实现 char大部分是signed char 比如 short int a= 10; 中 int 可省略,short和int默认是有符号的int short是2字节 - 浮点类型: float、double都是小数,没有符号区别
- 自定义类型(构造类型):
数组类型、结构体类型(struct)、枚举(enum)、联合(union) 指针类型:** void* p (无类型指针)** 空类型:void表示空类型(无类型),用于函数返回类型、函数参数、指针类型 ===================================== 整型在内存中的存储
数据内存中存的是2进制,vs在展示内存为方便,显示的是16进制 且内存中是倒着存的
内存中数字存储有三种:原(正数没有反码)、反(负数反码不反符号位)、补(反最低+1),且都有符号位。负数的原、补不一样
必须知道,内存中所存为二进制形式 内存中做减法,其实是做(补码)正+(补码)负,如果用两个原码加,结果不对,于是发明了补码 做1+(-1)补码结果为1+032,会丢符号位 ->032,正好结果为0
用补码计算可以计算减法,且方便符号位(符号)计算,所以计算机内存中都用补码,使得数值域和符号可以统一处理,其实补码取反加1,也得到原码
- 范围:要分有符号和无符号
比如char有8位:如果是signed char :1+07 = 128(直接规定) -128~127,正数第一位必为0,因为需要一位标记符号,不存在说正数就比负数多一位去存储值了, 01111111(7)是最大正数,即:0+17最大 如果无符号(必然无负数,从0开始) 0~255 unsigned short n = 0; // 0~65535 signed short m = 0; //-32768~32767
解释数值在内存倒着存的原因
大小端字节序 大端字节序存储(看着正常):把一个数的低位字节序内容放在高地址处,高位字节序内容放在低地址处。 小端存储(逆序的): 把数的低位放在低,高位放在高 当前visual 用的是小端
有大端和小端的原因是因为在计算机系统中,以字节为单位,每个地址单元对于一个字节,因为存在多字节安排问题,所以需要大端存储模式和小端存储模式 00 00 00 01 大:00 00 00 01 小:01 00 00 00 经典百度面试题 简述大端字节序和小端字节序概念,设计小程序判断机器字节序: 思路:对比第一个字节 就用1来判断,大小端时的高位不同 00 00 00 01 大端:00 00 00 01 小端:01 00 00 00 要判断第一个字节的内容 拿char指针看这个第一位? char 取低地址,从左向右从低到高,大端中低是00,小端中低是01 某类型的指针指向其它类型时候,右边需要强制转化 解引用大小为1字节
int a = 1;
char* p = (char*)&a;
if(*p==1)
{
printf("小端");
}
判断机器是大端存储还是小端存储, 简单写法和普通写法
int check_sys()
{
int a = 1;
char* ret = (char*)&a;
if (*ret == 1)
{
printf("小端");
}
else
{
printf("小端");
}
return 0;
}
int check_sys()
{
int a = 1;
return (* (char*)&a);
}
读代码题
char a = -128;
printf("%u", a);
char a = -128; 首先写-128原码 内存中读-128是个整型,用32位存 10000000 00000000 00000000 10000000 把它存入内存是补码 111111111 111111111 111111111 011111111(反) 111111111 111111111 111111111 10000000(+1得补码) 用char接收 只能存8位,存后8:10000000 而打印的时输出%u:无符号整型,是整型:所以有32位 整型提升:有符号就补符号位1,结果是无符号整型,直接计算 111111111 11111111 11111111 10000000 是最终结果 然后求它的值: 2^32 - 128
我错在了:输出上,转为无符号整型,要取32位,补最高位1
读代码题
char a = 128;
printf("%u", a);
128就是10000000。 放到char中时,a = 10000000 转无符号整型,而a是char,char默认有符号,补最高位:1 所以又成了上面的结果
读代码题
int i = -20;
unsigned int j = 10;
printf("%d\n", i + j);
10000000 00000000 00000000 0001 0100 111111111 111111111 111111111 1110 1011 (反码) 111111111 111111111 111111111 1110 1100(补码) 00000000 00000000 00000000 0000 1010 (10的补码即原码) 111111111 111111111 111111111 1111 0110 (补码转回去)
111111111 111111111 111111111 1111 0101 10000000 00000000 00000000 0000 1010
我犯的错:对于10,明明是正数,求出原码即是补码,我又多做了两步 结果:10
内存中的数据理解
- 整型:
unsigned int i; 无符号整型数据永远不会小于0,拿它做循环变量,不可以,因为减到0,以后,它再减,又会变得很大。0的补码是0,而减去1是2的32次-1。 -128:1000 0000 1+0*7 -129放不下 char类型取值范围:-128~127 -128:1000 0000,减1 -> 0111 1111,成了127,继续减成了126…1…0 128+127=253
unsignedchar只有8位,无符号最大为255,加到255为1111 1111 此时再加1,会成为0 i永远在0~255范围,所以死循环
- 浮点数在内存的存储
整型家族的范围定义都在->limits.h中 - float.h中定义范围:float.h
分析: n是整型,地址是int*,所以把地址给float需要强制转化 float解引用,float也是4字节,解引用能拿到4字节 X: n:9, pf:9, n9 , pf:9.0 正确结果: 显然,float和int类型在内存中不一样, 前两行:以整型存到float中,以%d拿出来本身n,还不变,但以float拿出来,结果不一样,说明内存中不一样。 后两行:正常float读、取
============ 内存中存float 5.5存float:先转2进制 1 0 1 . 1 1 1 2^2 2^1 2^0 2^-1 2^-2 2^-3 小数点后第n位是2的负n次 2^-1 = 0.5 => 101.1 => 1.0112^2 解读第一行: -1^S… 正数让这里为1,负数为- float实际存储: 按规定存,最后分别拿出求最终
float存时(double也类似),都是1.几,因为都化为1.几,所以不存1.,有效数m只存小数点后面,读取时再加1.,而指数e:它是无符号整数,但是科学计数法规定e可以是负数,所以存入内存E必须加一个中间数,而读取取出时,再减去中间数 比如5.5f: 101.1 1.011*2^2 (-1)^0 * 1.011 *2 ^2 最高位符号位:0,幂e存e+127 即129(可以用8位),存m,省略整数1.,存 011 ,后面全补0 0 (1000 0001) (011 000000000) 0100 0000 1011 0000 0000 0000 0000 0000 40 0b 00 00 小端模式:逆着存,如下图右侧 当e不全为0或不全为1,就按上面 当e全为0:e=1-127,有效位不加第一位的1,还原为0.xxx,这样表示非常接近0的数字 当e全为1:下图
=再分析开始问题== 把int型的n的补码0 0000000 000000000000001001放入float中,解引用float,会认为这串是浮点数,按浮点数的规则解析,高位0 (s),e中全0(第二种特殊情况)指数等于1-127 再加后面的0.000000000001001,后面本来就很小,再乘以2^-126,接近0。 而第三行*pf=9.0就按float规则存,按整型的二进制读出来,得到很大的值,第四行以float读,当然是本身9.0 short是两个字节 回忆:每当用不同类型指针接收其它类型地址时,需要先强制转换,且这样之后的引用因为步长大小可能不一致,会造成不能修改或访问到全部原始地址中数据
=====================================
分析: 因为是小端,char *pc 拿到低地址的44 *pc = 0,44位置为0 最后输出%x(c语言以16进制输出) : 0x11223300 指针数组
A. int* arr是个指针,加了[]就是指针数组,所以对 数组创建不给大小说明有初始化,但是B都没初始化 C是二级指针 D是数组指针,*和arr结合是数组,这是指向数组的指针
char初始化、接收 int main() { // char数组用0来初始化 char arr[100] = {0}; scanf(“%s”, arr); strlen(); // strlen()字符串数组求长度在函数内可以,需要传入指针,而整型数组需要传长度 // return 0; }
回调函数
qsort(): 第1个参数 void* p,无类型指针,不能解引用,所以在函数写的时候,一般要把它强制转换 第4个参数解释:不同类型的比较方法不一样,所以需要自己手动定义
void *用法:可以接收任何类型,作用就是为了接收任意类型地址 不能±指针,因为不知道指针所存数据大小
结构体指针,解引用, p->name 字符串比较字典序:strcmp();
=写一个回调函数= 参数中有函数指针,初始时候,要用什么函数,这个回调函数的参数:函数指针处传什么函数。 冒泡排序:
智力题:
- 烧香:
两根香,一根点一端,另一根点两端,两端燃完30分,另一根差30分,这时把它两端都点着 - 坐电梯
电梯有人和下雨天坐到32,没人坐到10楼,爬上去? - 下水道井盖是圆的
|