一道笔试题
char str1[] = "hehe";
char str2[] = "hehe";
char* str3 = "hehe";
char* str4 = "hehe";
if(str1 == str2)
{
printf("hehe");
}
if(str3 = str4)
{
printf("haha");
}
(1)由于"hehe"是一个常量字符串,是无法被修改的,且拥有其独立的一块内存空间。
(2)要是将一个常量字符串赋值给指针变量,严格的语法形式应该如下:
const char* ptr = "hehe";
意思是指针所指向的内容不可被修改。
故这ptr3和ptr4指向的是同一块空间。
(3)而常量字符串赋值给字符数组,字符串会到字符数组开辟的空间上储存.
ptr1和ptr2是数组的首元素地址,指向的是不同的地址。
综上所述,答案是输出一个haha
1的原码 = 00000000000000000000000000000001
-1的原码 = 10000000000000000000000000000001
相加的结果 10000000000000000000000000000010
转换为十进制 = -2
但正确结果显然为 0
1的补码 = 00000000000000000000000000000001
-1的补码 = 11111111111111111111111111111111
相加的结果 = 10000000000000000000000000000000
多出的一位被舍去了,因为超出了32个bit位
最终的结果为32个0
内存中的每个地址是一个字节,但是int类型的变量占四个字节,这四个字节存储的顺序就可以有多种情况,比如每8个bit挨着存放,或者不挨着存放,显然后者是没有任何意义的,只会给自己添加麻烦,所以我们讨论前者。
内存的地址有低地址和高地址之分,二进制数有低位和高位之分(类似与十进制数的个十百千万),所以二进制存储方式就会有两种情况:
(1)小端存储——二进制数的低位存在低地址,高位存在高地址。
(2)大端存储——二进制数的低位存在高地址,高位存低地址。
理解方法
数组-指针
数组是修饰指针的形容词
缩写下来,数组指针 就是 指针
所以在创建一个数组指针变量的时候就需要让这个变量先与*结合:
(*arrp)
如果这个指针指向一个有五个元素的数组:
(*arrp)[5]
这个被指向的数组中元素类型为整形:
int (*arrp)[5]
按照这个流程就可以轻松地理解和定义一个数组指针变量了
理解方法
指针-数组
指针是修饰数组的形容词
缩写下来,指针数组 就是 数组
所以在创建一个指针数组变量的时候就需要让这个变量先与[]结合:
parr[]
如果这个数组含有5个元素,存储的元素都是指针:
*parr[5]
每个元素都是整形指针:
int *parr[5]
按照这个流程就可以轻松地理解和定义一个指针数组变量了
小数都有小数点,那么是否应该用数字来表示小数点呢?
小数点的部分转化为二进制的时候也常常无法精确地转换,如何最高效地提升精确度呢?
基于小数的一系列的问题,IEEE(电气电子工程师学会)发布了一个国际标准,下面一一介绍。
任何一个浮点数可以表示为如下形式
(-1)^S * M * 2^E
其中(-1)^S 决定正负号
M * 2^E 表示一个二进制数用科学计数法表示的形式
就先拿我们熟悉的十进制数进行举例
100.1 -> 1.001 * 10^2
再与二进制对比
101.0 -> 1.01 * 2^2
令S = 0,M = 1.01,E = 2
而我们发现M的取值范围是在[1,2)中,
所以储存M的时候只会保留小数点后的数字,
即保留01,等到读取的时候再将第一位的1加上。
由于E可能会出现负数的情况,故储存的时候需要加上一个中间值,
来保证E大于0,这个中间值和类型有关。
float类型的E加127
double类型的E加1023
最终上面的101.0f的S = 0, M = 01, E = 129
对一个float类型相当于一个数的次方本来是-127,加上127显示出E为0,这个数显然是个无限趋于0的数。
这里直接规定,取出这种数字的时候,M不再加上之前去掉的1,直接还原为0.xxxxxxxxxxxxxx的小数。
易知这是一个趋于无穷的数。