0
点赞
收藏
分享

微信扫一扫

CS107编程范式-学习笔记-3


第三讲

这两节课咱们再来学习一下C语言。

double d = 3.1416;
char ch = *(char*)&d;
cout << ch << endl;

这段代码,跟上节课一样,char只存储double的低8位数据

CS107编程范式-学习笔记-3_偏移量

short s = 45;
double d = *(double*)&s;
cout << d << endl;

这里的话,同样,45保存到double的低16位,就会是非常小的一个数字

CS107编程范式-学习笔记-3_数组_02

CS107编程范式-学习笔记-3_赋值_03

一般来说,这种方式在大小端存储中会有不同, 比如Linux下的1可能会在Solaris中成为256,

其实大端小端,大尾小尾机器在上计算机组成原理时就模模糊糊的,这里也不是太懂,我一直是按照左低右高作为记忆,不管它了,以我的测试机器运行结果为准吧。

结构体

struct fraction {
int num;
int denum;
};

fraction pi;
pi.num = 22;
pi.denum = 7;

结构体可以认为一段连续存储域,执行pi.num = 22时,机器无法区分这是pi.num还是整个pi的存储地址,实际上,是在pi的存储域的基地址偏移量为0的4byte区域,赋值为22,因为机器知道这是pi.num的存储区。同样赋值pi.denum时,是在整个存储域的基地址偏移量为4的4byte区域赋值。

CS107编程范式-学习笔记-3_偏移量_04

小高能

((fraction*)&(pi.denum))->num = 12;
cout << "pi.denum is :" << pi.denum << endl;

这段代码,首先取pi.denum的地址,是一个(int*)类型的指针,我们强制转换成fraction*类型,之后对这个fraction*区的num赋值12。下图中,深灰色为&pi.denum, 浅灰色是&fraction,那么可以看出,*fraction->num就是原来的pi.denum,那么结果就是:pi.denum = 12。

CS107编程范式-学习笔记-3_数组_05

运行结果:

CS107编程范式-学习笔记-3_偏移量_06

再执行下面语句:

((fraction*)&(pi.denum))->denum = 33;

那么存储区会是这个样子:

CS107编程范式-学习笔记-3_偏移量_07

机器会默认*fraction->denum合法的,因此在fraction*的基地址偏移地址为4的4byte区赋值33,虽然在程序中并没有对应的存储方式,但是在内存中该地址的值已经变为33了。

数组

int array[10];
array[0] = 44;
array[9] = 100;

CS107编程范式-学习笔记-3_偏移量_08

array = &array[0],相信学过的都知道了,array其实是指向数组存储区的首地址的指针,在函数调用时,数组传入的实际上是&array[0],并非整个array。

array[5] = 45;
array[10] = 1;

机器会尝试在基地址偏移量为10,即40个byte之后的4byte区赋值,但是C语言中原始数组并不提供边界查询,会不会产生问题又是另一码事了。

CS107编程范式-学习笔记-3_偏移量_09

 

array[25] = 25;
array[-4] = 77;

这段代码同样,机器会尝试执行代码,但是不会检查边界,同上的原理,尝试在基地址前面16个byte或者后100个byte后的4byte区域内赋值。 

CS107编程范式-学习笔记-3_赋值_10

 同样,array + k = &array[k],即array[k]实际上是寻找array[0]地址向后偏移k单位的内存区。

*array = array[0];

*(array + k) = array[k];

那之前array[-4] = 77,其实就是*(array - 4) = 77了。

int arr[5];
arr[3] = 128;
((short*)arr)[6] = 2;
cout << arr[3] << endl;

这段代码,将int数组强制转换为short类型的,可以知道,128是在arr[3]的低16位中的,那么((short*)arr)[6]指向的是arr[3]的高16位,对其赋值2,那么相当于整个arr[3]多赋值了512,打印结果应该是512 + 128

CS107编程范式-学习笔记-3_偏移量_11

实验结果并非如此:

CS107编程范式-学习笔记-3_赋值_12

首先,((short*)arr)[6]对应高16位的话,即使相加也是应该增大2^17次方,即131072,并且在小端存储机器中,低地址对应低位,存储结构应该是这样的:

CS107编程范式-学习笔记-3_赋值_13

我们执行以下代码,如果我的推论没错的话,结果应该是131072 + 128 = 131200;

int arr[5];
arr[3] = 128;
((short*)arr)[7] = 2;
cout << arr[3] << endl;
cout << pow(2, 17) << endl;

结果:

CS107编程范式-学习笔记-3_偏移量_14

推论正确,老师说的可能是大端机器的运行结果,结果并不重要,理解强制类型转换在数组内的作用即可,知道数组内部存储方式和寻址方式是最重要的。

小高能

((short*)(((char*)(&arr[1])) + 8))[3] = 100;

首先&arr[1]是取arr[1]的存储地址,类型是(int*),将它强制转换成(char*)类型,再向后寻址8个单位即32byte,即为原来数组的arr[5]的地址,再强制转换为(short*)类型,则这时新“数组”的基地址就为&arr[5]了并对其后面3个单位偏移量的内存赋值100。

CS107编程范式-学习笔记-3_赋值_15

再谈结构体 

struct student {
char *name;
char suid[8];
int numUnits;
};

student pupils[4];
pupils[0].numUnits = 2;
pupils[2].name = strdup("Adam");
pupils[3].name = pupils[0].suid + 6;
strcpy(pupils[1].suid, "40415xx");

执行这段代码,内存区会是这样子的: 

CS107编程范式-学习笔记-3_数组_16

再继续执行下面这段代码: 

strcpy(pupils[3].name, "123456");

注意,上面pupils[3].num已经指向为pupils[0].suid + 6,那么当执行上面这段代码时,就是从pupils[0].suid + 6的地址向后6个字节存储"123456",

CS107编程范式-学习笔记-3_偏移量_17

然后pupils[0].nameUnits会是一个非常大的数字3 * 2^24 + 4 * 2^16 + 5 * 2^8 + 6;

这里我认为3 4 5 6应该不是直接传进去,应该是它们对应的ascii码值,即 51 52 53 54。

pupils[7].suid[11] = 'A';

这里仍然是以pupils[0]为基地址偏移7个单位的一个结构体存储区,它的suid[0]为基地址偏移量为11的存储区赋值为'A';

机器是这样子理解上面的代码的,但是实际运行肯定会有问题的。

void swap(int *ap, int *bp) {
int temp = *ap;
*ap = *bp;
*bp = temp;
}

int x = 7;
int y = 117;
swap(&x, &y);

CS107编程范式-学习笔记-3_偏移量_18

无论是int还是float,double,struct,交换模式都是位模式,并无区别。

举报

相关推荐

0 条评论