C 基础回顾
1.读程序求输出
#include<stdio.h>
int i = 2;
int main(){
int i = i;
printf("%d\n",i);
}
编译可以通过,不过会有警告,因为
int i = i 企图使用自己初始化自己,用一个未初始化的去初始化自己,没用。
2. 读取程序求输出
#include<stdio.h>
int func(int x){
int cnt = 0;
while(x){
++cnt;
x = x & (x - 1);
}
return cnt;
}
int main(){
printf("%d\n",func(9999));
return 0;
}
x & x - 1 :干啥了呢?
去掉了最后一位的1
x & (-x) =》lowbit(x)的意思是将 x 转化成二进制数之后,只保留最低位的1及其后面的0,截断前面的内容,然后再转成10进制数。
3 . 用位运算实现两个整数的加法运算。
- 递归
int getSum(int a, int b){
return a == 0 ? b : getSum((unsigned)(a & b) << 1,a ^ b);
}
- 迭代
int getSum(int a, int b){
while(a){
b = a ^ b;
a = (unsigned)(a & (b ^ a)) << 1;
}
return b;
}
a ^ b : 无进位的相加 ,
a & b : 每一位的进位 ,
补充:
(a & b) + (a ^ b) << 1,求的是(a + b)/ 2 ,注意的是unsigned 来处理负数。所以说1+-2也是可以的,因为-2 是用补码的形式在内存中存储的,所以说可以和正数的相加一起处理。
补充:
-b = ~b+1
所以a - b => return getSum(a,getSum(~b,1));
4.以下程序输出是啥
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <conio.h>
using namespace std;
int main() {
float a = 1.0f;
cout << (int)a << endl;
cout << &a << endl;
cout << (int &)a << endl;
cout << boolalpha << ((int)a == (int&)a) << endl; ??
float b = 0.0f;
cout << (int)b << endl;
cout << &b << endl;
cout << (int &)b << endl;
cout << boolalpha << ((int)b == (int&)b) << endl; ??
return 0;
}
推荐阅读:
(int)x 强制类型转换,是将浮点数x为参数构造整数(即float转换为int)
(int &)y 则是告诉编译器将y看成int对待(内存里的数据不做任何转换),所以(int &)x值为1071 644 672。
至于(int*)的话,我就不多说啦,就是强制转换成整型指针,一般人们容易混淆的是(int)和(int&)这两个。
补充:浮点数0.0是比较特殊的,它并不按照上面说的浮点数的格式存储,浮点数0.0在内存里的存储是000.....000(全零)。
5. 以下这段程序的输出是啥?
#include<stdio.h>
int main(){
unsigned int a = 0xFFFFFFF7;
unsigned char c = (unsigned char)a;
char *b = (char *)&a;
printf("%08x,%08x\n",c,*b);
return 0;
}
-
大小端 推荐阅读 详解大端模式和小端模式
举一个例子,比如数字0x12 34 56 78在内存中的表示形式。
1)大端模式:Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。(其实大端模式才是我们直观上认为的模式,和字符串存储的模式差类似)
低地址 --------------------> 高地址
0x12 | 0x34 | 0x56 | 0x78
2)小端模式:Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
低地址 --------------------> 高地址
0x78 | 0x56 | 0x34 | 0x12
可以编写一个小的测试程序来判断机器的字节序:
BOOL IsBigEndian()
{
int a = 0x1234;
char b = *(char *)&a; //通过将int强制类型转换成char单字节,通过判断起始存储位置。即等于 取b等于a的低地址部分
if( b == 0x12)
{
return TRUE;
}
return FALSE;
}
联合体union的存放顺序是所有成员都从低地址开始存放,利用该特性可以轻松地获得了CPU对内存采用Little-endian还是Big-endian模式读写:
BOOL IsBigEndian()
{
union NUM
{
int a;
char b;
}num;
num.a = 0x1234;
if( num.b == 0x12 )
{
return TRUE;
}
return FALSE;
}
- 整数提示 推荐阅读 整数提升
C99标准中有明确提到整数提升的概念:"如果int能够表示原始类型中的所有数值,那么这个数值就被转成int型,否则,它被转成unsigned int型。这种规则被称为整型提升。所有其它类型都不会被整型提升改变。"
为什么会有整数提升?这是因为对于int类型数据作运算时,CPU运算速度是最快的,所以C语言会对数据作整数提升的处理,使得程序的运行速度尽可能地快
比如下面的这个例子:
int main()
{
char a=127;
unsigned char b=255;
short c=32767;
unsigned short d=65535;
printf("char: a+1=%d %d\n",a+1,sizeof(a+1));
printf("uchar: b+1=%d %d\n",b+1,sizeof(b+1));
printf("short: c+1=%d %d\n",c+1,sizeof(c+1));
printf("ushort: d+1=%d %d\n",d+1,sizeof(d+1));
return 0;
}
结果:
char: a+1=128 4
uchar: b+1=256 4
short: c+1=32768 4
ushort: d+1=65536 4
针对源题目
- 在小端的机器上c = 0xF7;大端就是0xFF
- b 存在整数提升,又因为是负数,所以自动补1结果就是0xFFFFFFF7
6. 以下程序输出结果是
#include<stdio.h>
int main(){
unsigned char a = 0xA5;
unsigned char b = ~a>>4+1;
printf("%d\n",b);
return 0;
}
这个就是上述的整数提升过程,~a >> 5
5 是int 所以会将~a 提升到int 4 字节
然后截取1字节赋值给b。
7.不用”if“,”?:“,”switch“或其他判断语句,求两个数中较大的数或较小的数
int Find1(int a, int b) {
return ((a + b) + abs(a - b)) / 2;
//return ((a + b) - abs(a - b)) / 2;
}
/*
当a大于b时,a-b为正,右移sizeof(int) * 8 - 1后,最右侧一位为0,0^1 = 0;
当a小于b时,a-b为负,右移后最右侧一位为1,1^1 = 1
*/
int Find2(int a, int b) {
int c[2] = {a, b};
int z = a - b;
z = (z >> (sizeof(int) * 8 - 1)) & 1;
return c[z];
/*
int c[2] = {b, a};
int z = a - b;
z = (z >> (sizeof(int) * 8 - 1)) & 1;
return c[z];
*/
}
int Find22(int a, int b) {
int flag = ((a - b) >> (sizeof(int) * 8 - 1)) & 1;
return (a - (a - b) * flag);
//return (b - (b - a) * flag);
}