第一题
int main()
{
unsigned int i = 0;
for (i = 9; i >= 0; i--)
{
printf("%u\n", i);
}
return 0;
}
这个代码输入的结果是什么?
答:首先,代码肯定是一个死循环,原因是当i=0;i--后,i变成-1,对应的二进位制补码为11111111111111111111111111111111,因为我们的i是无符号数字,所以对应的数字是2^32-1,2^32-1大于0,所以再次执行循环,执行到结果为0为止,近似于死循环
结果如图所示
第二题
int main()
{
char a[1000];
int i;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d", strlen(a));
return 0;
}
输入的结果是什么?
答:255,原因是strlen求字符串个数,strlen函数的返回值是size_t,是无符号数,strlen是检测'\0'前面的字符个数,’\0'对应的数字是0,所以我们只要找到0,求出0前面的元素个数就可以了,我们画一个图进行解释
可以发现,在\0前面的数字为-1到-128,一共有128个数字,127到1一共127个数字,所以结果是255,由此可见,char类型的元素对应的数字的范围是[-128,127],
第三题
unsigned char i = 0;
int main()
{
for (i = 0; i <= 255; i++)
{
printf("hello world\n");
}
return 0;
}
答:既然是无符号类型的char型,其对应的数字范围就是0——255,所以i所对应的数字始终满足for循环,所以死循环打印helloworld。
第四题
int main()
{
if (strlen("abc") - strlen("abcdef") >= 0)
printf(">\n");
else
printf("<\n");
return 0;
}
打印结果是什么?
答:(strlen("abc") - strlen("abcdef")这个结果等于-3,但是由于strlen函数的返回值是size_t,也就是无符号数,既然是无符号数,那么-3对应的二进位制10000000000000000000000000000011,因为无符号数不分正负,1并不代表符号位,所以结果是非常大的整数,所以打印的结果是大于号
结果如图所示
算术转换
当下面的类型与上面的类型进行计算时,先把下面的类型转换为上面的类型,同类型后再进行计算
浮点型在内存中的存储
常见的浮点数:3.14159
1E10=1.0*10^10
浮点型数字家族包括:float,double,long double
浮点型表示的范围在float.h中定义
整型类型
浮点型类型
浮点型存储的例子
int main()
{
int n = 9;
float*pFloat = (float*)&n;
printf("n的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
*pFloat = 9.0;
printf("num的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
return 0;
}
运行结果如图所示
我们把这个代码的意思说一下:
首先创建整型n,n=9,取出n的地址,把这个地址强制类型转化为float类型,然后传递给float类型的指针变量*pFloat,,然后我们打印n的值,我们的打印pFloat指针指向参数的值,我们将*pFloat赋值为9.0,打印n的值,我们再打印*pFloat的值
为什么结果差异这么大呢?接下来,我们来讲解浮点型在内存中存储和取出的规则
浮点型存储规则
任何一个二进位制的浮点数都可以写成这种形式
1:(-1)^S表示符号位,当S为0时,为正数,当S为1时,为负数
2:M表示有效数字,大于等于1,小于2
3:2^E表示指数位
我们举个例子
v=5.0f;
换算成上面这种形式
首先,为正数,(-1)^0
5对应的2进位制数字是101,所以M=1.01
E的结果为2
换算后得到v=(-1)^0*1.01*2^2
我们再化简一个
v=9.5f
首先,为正数,(-1)^0
9.5对应的二进位制数字为1001.1,所以M=1.0011
所以E=3
换算后可得v=(-1)^0*1.0011*2^3
有一些数字是不能精确保存的
例如V=9.6f,有效数字是很长很长的,float类型只占4个字节,32个比特位,所以我们无法精确保存一些数字
1:在计算机内部存储有效数字M时,会首先去除M的1,因为有效数字始终大于等于1且小于等于2,我们存储的数字M就为0.xxxxxxx,
在我们要从计算机中读取数字M,时,我们再+1表示真正的有效数字,这样做的目的是节省一个比特位
接下来,我们分析E
E是一个无符号整数,无符号整数就决定了E不会为负,但是在科学计数法的表示中,指数部分也可以用-号表示,例如
v=0.5f
换算成二进位制结果为0.1=1*2^(-1),由此看见,指数部分可以为-1,如何表示-1呢?
我们知道,在32位平台上,E所占的字节数为8,因为E是无符号整数,所以E的结果为0——255,在64位平台上时,E所占的字节数为16,因为E是无符号整数,所以E的结果为0——2047,
我们引入一种方法,既然我们的E不能存储负数,在32位平台上,我们让E的真实值加一个中间值127,这样保证了存进去的E值不会为负数,在64位平台上,我们加上一个中间值1023
我们举一个存储的例子
float f=5.5
如何进行存储呢?
答:首先,存储s,s占一个比特位,因为5.5为整数,所以结果为(-1)^0,所以S为0
5.5=101.1=1.011*2^2,所以M的结果为1.011,占23个比特位,前四位存储的是1.011,后面的19个结果都为0
接下来,我们求E,E的真实值为2,E+127=129,所以我们存进去的E为10000001,所以我们存储的值对应的二进位制为0 10000001 0110000000000000000000
我们在内存中的存储是16进位制表示的,一个16进制是由四个二进位制组成的,所以存储的结果为0100 0000 101 1000 0000 0000 0000 0000,
对应的结果为4 0 b 8 0 0 0 0 对应的就是40 b8 00 00,因为我们的存储是小端存储,则结果为00 00 b8 40,我们进行运行检测一下
运行结果如图所示
接下来,我们讲解如何从内存中取出。
E从内存中取出分三种情况
E不全为0或不全为1:E-127求出E的真实值,然后在M(有效数字)前面补一,根据二进位制数字求出FLOAT类型的数字
例子01000000010110000000000000000000,如图所示,S是最前面的0,因为E:10000001不为全0或不为全1,我们让E-127,结果为2,所以E的真实值为2,0110000000000000000000为有效数字M,我们在M的最前面加上一个1,就为真实的M的值,也就是11011,因为E为2,所以M的数字实际上是对应的Float类型的数字为5.5
所以对应的类型为(-1)^0*110.11*2^2
当E全为0时,E的真实值就是-127,因为-127+127=0,换算成二进位制数字就是2^(-127),这个数字是无限趋近于0的,所以我们可以换一种简单的方法,既然这个结果很小,那我们就省略求M有效数字的真实值,不再+1,因为1.xxxx乘2^E是非常小的数字,所以1是可有可无的,我们直接把E的值锁定为1-127,或1-1023
当E全为1时,11111111=255,所以E的有效数字=255-127=128,2^128是无穷大的数字,所以表示+-无穷大的数字
接下来,我们分析上面举过的例子
int main()
{
int n = 9;
float*pFloat = (float*)&n;
printf("n的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
*pFloat = 9.0;
printf("num的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
return 0;
}
第一个printf,因为我们存进去的是整型9,我们拿出来的也是整型类型,所以我们打印的结果也为9
第二个printf,我们看这句话float*pFloat = (float*)&n;这里的意思是取出n的地址,将n强制类型转化为浮点型,整型n对应的二进位制为00000000000000000000000000001001,我们既然把这个二进位制转为浮点型,我们进行解引用浮点型,就是按照浮点型的方式进行转化:
s=0,E=00000000,m=0.000000000000000000000000101,我们上面已经知道,当E的结果为全0时,对应的数字为1-127=-126,并且有效数字M不再+1,所以对应的浮点数为1*0.000000000000000001001*2^(-126),是非常小的数字,近似为0,所以打印的结果为0
第三个printf,我们通过解引用的方法,将浮点型类型的指针指向地址的元素赋值9.0,所以我们是用浮点型数字存进去的9.0,也就是9.0对应的二进位制数字为1001,所以N=3+127=130,s=0,有效值为1.001,所以对应的二进位制数字为01000001010010000000000000000000,我们用整型的方式读二进位制序列可得到的结果是非常大的
第四个printf,我们以浮点型的形势存进去9.0,则用浮点型拿出来的结果也是9.000000
void move_odd_even(int arr[10],int sz)
{
int left = 0;
int right = sz - 1;
while (left < right)
{
while (arr[left] % 2 == 1)
{
left++;
}
while (arr[right] % 2 == 0)
{
right--;
}
if (left < right)
{
int tmp = 0;
tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
}
}
int main()
{
int arr[10] = { 0 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
scanf("%d", arr + i);
}
move_odd_even(arr,sz);
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
第一个问题: for (i = 0; i < sz; i++)
{
scanf("%d", arr + i);
}为什么这里没有取地址
答:因为我们的arr本身就是地址,arr+i可以表示数组下标为i元素的地址,所以不再需要取地址
第二个问题while (arr[left] % 2 == 1)
{
left++;
}
while (arr[right] % 2 == 0)
{
right--;
}这串代码的意义
答:第一个while循环,其满足条件是下标为left的元素为奇数,换言之就是只要是奇数,就跳过,直到找到第一个偶数为止,第二份while循环则是以找到第一个奇数为限制条件,找到之后,我们再执行if语句,如果左边的依旧小于右边的,那么我们就把这两个元素进行交换,就是把左边的偶数换到右边,右边的奇数换到左边,这是一整个循环,所以我们要在外面再嵌套一个while循环,循环条件是left<right
但是,代码依旧有不足的地方,例如
假如我们输入的全是奇数,则我们执行第一个while循环,始终满足条件,当我们输入到第十个数字时,依旧满足,我们再让left++,然后访问,就会造成越界访问,如何解决这种问题
我们在两个while循环加上left<right
如图所示
最后一题
int main()
{
int m = 0;
int n = 0;
scanf("%d%d", &m, &n);
int arr1[1000] = { 0 };
int arr2[1000] = { 0 };
int i = 0;
for (i = 0; i < m; i++)
{
scanf("%d", &arr1[i]);
}
for (i = 0; i < n; i++)
{
scanf("%d", &arr2[i]);
}
int j = 0;
int k = 0;
while (j < n&&k < m)
{
if (arr1[j] < arr2[k])
{
printf("%d", arr1[i]);
j++;
}
else
{
printf("%d", arr2[k]);
k++;
}
}
if (j < n)
{
for (; j < n; j++)
{
printf("%d", arr1[i]);
}
}
else
{
for (; k < m; k++)
{
printf("%d", arr2[k]);
}
}
return 0;
}
问题1: for (i = 0; i < m; i++)
{
scanf("%d", &arr1[i]);
}是什么意思
答:,这种方法是实现输入数组内部元素的过程,&arr[i]表示数组下标为i元素的地址,把我们输入的数字,存入数组内部
我们对函数部分进行解读
while (j < n&&k < m)
{
if (arr1[j] < arr2[k])
{
printf("%d", arr1[i]);
j++;
}
else
{
printf("%d", arr2[k]);
k++;
}
}
if (j < n)
{
for (; j < n; j++)
{
printf("%d", arr1[i]);
}
}
else
{
for (; k < m; k++)
{
printf("%d", arr2[k]);
}
}
j,k是我们创建的两个变量,while (j < n&&k < m),while循环的条件是j和k的下标都没有到达数组下标的最大值,为什么要这样做,原因是在j=n或k=m之前的计算模式相同,之后的计算模式不同
if (arr1[j] < arr2[k])
{
printf("%d", arr1[i]);
j++;
}
else
{
printf("%d", arr2[k]);
k++;
}
假设j对应的数组是第一个数组,k对应的是第二个数组,假设第一个数组相对与第二个数组小时,打印这个数字,然后继续沿这一个数组,跳过这个元素,如果第二个数组大,情况则是相同,最终的结果是某一个数组首先达到结尾
if (j < n)
{
for (; j < n; j++)
{
printf("%d", arr1[i]);
}
}
else
{
for (; k < m; k++)
{
printf("%d", arr2[k]);
}
}
我们进行判断,如果是第一行数组未达到末尾,也就是第二行数组首先达到末尾,那我们依次打印剩余的第一行数组的元素,反之,也相同。
假设,我们要创建一个数组来接收这些元素,并把数组的方法进行打印,那么该如何修改呢?
int main()
{
int m = 0;
int n = 0;
int r = 0;
scanf("%d%d", &m, &n);
int arr1[1000] = { 0 };
int arr2[1000] = { 0 };
int arr3[2000] = { 0 };
int i = 0;
for (i = 0; i < m; i++)
{
scanf("%d", &arr1[i]);
}
for (i = 0; i < n; i++)
{
scanf("%d", &arr2[i]);
}
int j = 0;
int k = 0;
while (j < n&&k < m)
{
if (arr1[j] < arr2[k])
{
arr3[r++]=arr1[j];
j++;
}
else
{
arr3[r++] = arr2[k];
k++;
}
}
if (j < n)
{
for (; j < n; j++)
{
arr3[r++] = arr1[i];
}
}
else
{
for (; k < m; k++)
{
arr3[r++] = arr2[k];
}
}
for (i = 0; i < m + n; i++)
{
printf("%d", arr3[r]);
}
return 0;
}
问题1:arr3[r++]=arr1[j];r++是什么意思,r++是后置++,是先使用,再++,所以我们这里是把arr1[j]先赋给arr3[r],然后r再++