您的位置:首页 > 编程语言 > C语言/C++

C/C++笔记(一) 2015/8/15

2015-08-15 11:17 429 查看

1.C语言中左值和右值的区别

左值可以放在赋值符左边的值,右值是放在赋值符右边的值。
左值,没有内存实体的左值是存在的
右值,可以有内存实体,也可以没有内存实体(寄存器),a+1,&a,右值都在寄存器。


2.调试、下断点

断点:可以中断程序执行,观察内部过程

GPU,显卡的处理器


3.const和define的区别

const会进行类型转换,而define不会,只是的替换


#define K 100.0
const int Knum = 100.0; //赋值号会自动进行数据类型转换

void  main()
{
printf("%d\n", K);
printf("%d", Knum);
}


4.printf的容错机制

printf的容错机制,第一个错误,第二个会连带这错误,而且printf也不会进行类型转换
`printf("%d, %d\n", 10.9, 10);`结果两个数都是错的,而换成两行,则10就会正常输出
//printf按照%d,%u,按照你指定的方式解析二进制


unsigned int data1 = -1;    //二进制数

printf("%u\n", data1);  //二进制数据,%d,有符号十进制
printf("%d\n", data1);  //%u无符号十进制


5.变量的赋值就是拷贝二进,内存之间不能直接拷贝,必须通过CPU

常量必须初始化,const常量只有在初始化的时候是左值。


6.指针的本质

指针是一种数据类型,类型决定了大小(步长),决定了二进制数据的解析方式


void main8()
{ //指针是一种数据类型,类型决定了大小(步长),决定了二进制数据的解析方式int num = 100;
double *p1 = # //这里只是把num的地址赋给了p1,而num并没有进行类型转换
int *p2 = #
float *p3 = #

printf("%f\n", *p1); //乱码
printf("%d\n", *p2); //可以
printf("%f\n", *p3); //不可以,没有进行类型转换0.00000
getchar();
}


手机号使用long long int存数

7.float存储

浮点数按指数(阶码)方式存储

阶码分为三大部分,符号位s,幂数e,系数x
1.第31个bit为符号位,为0表示整数,为1表示负数(32-bit为单精度,64-bit浮点数为双精度);
2.第30~23bit,共8bit为幂数,其读数值用e表示,以2为底;
3.第22~0bit,共23bit作为系数,视为二进制纯小数,假定该小数的十进制值为x;


浮点数的值用十进制表示为

(-1)^s*(1+x)*2^(e-127)

符号位的代码推演

float fl1 = 10.0;
float fl2 = -10.0;
printf("%p, %p\n", &fl1, &fl2);


10.0的十六进制41200000 二进制0 100 0001 0 010 0000 0000 0000 0000 0000

-10.0的十六进制c1200000 二进制1 100 0001 0 010 0000 0000 0000 0000 0000

结论第32个bit是符号位

系数的代码推演

float fl1 = 10.0;
float fl2 = 11.0;
float fl3 = 5.0;

printf("%p, %p, %p\n", &fl1, &fl2, &fl3);


10.0的十六进制41200000   二进制0  100 0001 0  010 0000 0000 0000 0000 0000
11.0的十六进制41300000   二进制0  100 0001 0  011 0000 0000 0000 0000 0000

10      100 0001 0
1010    +130(130-127)        1.010*2^3

11      100 0001 0
1011    +130(130-127)        1.011*2^3


幂数的代码推演

float fl1 = 1.0;
float fl2 = 2.0;
float fl3 = 0.5;

printf("\n%p,%p,%p", &fl1, &fl2, &fl3);


1.0的十六进制3f800000   二进制0  011 1111 1  000 0000 0000 0000 0000 0000
2.0的十六进制40000000   二进制0  100 0000 0  000 0000 0000 0000 0000 0000
0.5的十六进制3f000000   二进制0  011 1111 0  000 0000 0000 0000 0000 0000

1.0      011 1111 1
0000        127         1.000*2^0

2.0       100 0001 0
0000        128         1.000*2^1

0.5       100 1111 0
0000        126(e-127)         1.000*2^(-1)


8.为什么使用补码

补码的优势是运算速度快

例一个字节大小的 1 ,-3 相加
1的原码: 0000 0001
-3的原码:1000 0011

第一步,转换成减法,求绝对值, 看那个数大
第二步  绝对值的大数减小数  得到0000 0010
第三步  判断符号位  得到1000 0010, -2
使用补码
1的补码:   0000 0001
-3的补码:   1111 1101
两个数相加  1111 1110
1111 1110 的原码是1000 0010  就是-2
再例如 3和-1相加
3的补码:   0000 0011
-1的补码:   1111 1111
两个数相加 10000 0010
多的1舍弃,得到0000 0010
0000 0010 的原码还是0000 0010  就是2
利用进位巧妙的把符号位放到运算里来,大大加快了计算的效率


9.位运算

与   &   0&0->0  0&1->0  1&0->0  1&1->1
或   |   0|0->0  0|1->1  1|0->1  1|1->1
异或 ^   0^0->0  0^1->1  1^0->1  1^1->0     相同为0,不相同为1
使用异或交换数据,避免溢出的风险

unsigned char ch1 = 15; //0000 1111
unsigned char ch2 = 255;//1111 1111

ch1 = ch1^ch2;  //ch1 = 1111 0000  ch2 = 1111 1111
ch2 = ch2^ch1;  //ch2 = 0000 1111  ch1 = 1111 0000
ch1 = ch1^ch2;  //ch1 = 1111 1111  ch2 = 1111 0000

位取反 ~   ~0 = 1     ~1 = 0
!0 = 1是逻辑运算符不是位运算符

unsigned char ch1 = ~0;   ~0之后  1111 1111
unsigned int int1 = ~0;   ~0之后  1111 1111 1111 1111 1111 1111 1111 1111

左移 <<   0001<<1 -->0010       0001<<2 -->0100 //相当于每次乘以2
int num = 1;
printf("%d\n", num << 1);   //打印的值是2,但是num的指没有改变
printf("%d\n", num << 2);   //打印的值是4,但是num的指没有改变
printf("%d\n", num);        //打印的值是1
printf("%d\n", num <<= 2);  //打印的值是4,num的值改变   <<=  相当于+=
printf("%d\n", num);        //打印的值是4
右移 >>   0100>>1 -->0010       0100>>2 -->0001     //相当于每次除以2


与:某一段清零

或:指定的字符置一

异或:指定的字符反转

10.求一个数的二进制中1的个数

利用与操作来求
例如    100         1100100     3个1
100-1=99        1100011
100&99=96       1100000     少了一个1   +1
96-1=95         1011111
96&95=64        1000000     少了一个1   +1
64-1=63         0111111
64&63=0         0000000     少了一个1   +1

代码实现
for (; num; num = num&(num-1))
wei++;
递归
int getwei3(int num)
{
if (num == 0)
return 0;
else
return 1 + getwei3(num&(num - 1));
}


11.求一个数的补码和原码

求补码

因为一个数在内存中就是按照补码的方式存的
所以1的二进制是0000 0000 0000 0000 0000 0000 0000 0001
-1的二进制是1111 1111 1111 1111 1111 1111 1111 1111
我们构造一个数 1000 0000 0000 0000 0000 0000 0000 0000
把这个数与1或者-1位求与就可以得到这个数的这一位是什么,然后再把1或者-1左移一位,就是到了第二位的值,以此类题
int num = 1;
int data = 1 << 31;
for (int i = 1; i <= 32; i++) {
printf("%c", (data&num ? '1' : '0'));
num <<= 1;
if (i % 4 == 0)
printf(" ");
}
递归的写法是
void buma(int n, int cnt)
{
int data = 1 << 31;
if(cnt == 0)
return;
if(cnt %4 == 0)
printf(" ");
printf("%c", (data&n ? '1' : '0'));
n <<=1;
--cnt;
buma(n,cnt);
}


求原码

根据原码和补码的转换规则,正数的原码和补码一样,负数的原码和补码的转换规则是:
原码->补码:符号位不变,其余位取反+1
补码->原码:符号位不变,其余位取反+1

例如num = -1;
num = ~num +1;//这是全部按位取反,再加一,但是符号位也取了反
num = num | (1<<31);//这是把符号位再重新置为1
//然后在按位把这个数读出来

代码就是:
if (num < 0) {
num = ~num + 1;//根据补码求原码
num = num | data;
}
for (int i = 1; i <= 32; i++) {
printf("%c", (data&num ? '1' : '0'));
num <<= 1;
if (i % 4 == 0)
printf(" ");
}


反码:

-1的原码 1000 0001
反码 1111 1110
补码 1111 1111
所以一个数的反码就是这个数的补码-1

代码:
if (num < 0) {
num -=1;
}
for (int i = 1; i <= 32; i++) {
printf("%c", (data&num ? '1' : '0'));
num <<= 1;
if (i % 4 == 0)
printf(" ");
}


补码求原码 正数不变 负数的话取反+1在重置符号位

补码求反码 正数不变 负数的话-1
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: