程序员面试宝典第四版第五章

1.在C++中同一个程序中可以定义相同名字的变量的,但是先定义的变量如i会被后定义的变量i给覆盖,
int i = 1;
void main()
{
int i = i;
}
这里,main函数里面的i与main(外的i无关,而是一个未定义的值。)

2.1.“==” 与 “=”
“==”: 是判断左右两值是否相等,一般情况下,最好将常量作为左值,因为常量在“=”中不能不能作为左值,如果将“==”写成了“=”,这样编译器就会报错。
“=”: 是赋值语句,即将右边的值赋给左边的变量中,因此,”=“左边不能为常量。
2.2位与 与 与运算(位或 与 或运算)
”&“: 这个是位与,计算时需要将数转换为二进制之后进行相应位之间的与运算。位或(|)同理。4 & 0 = 4.  4 | 0 = 4.
”&&“: 是与运算,即左右两边的值只有零与非零两种情况,只有当两边的值都不为零的时候才位1.或运算(||)也同样的看待。4 && 0 = 0.   4 || 0 =1. 

3.二进制中包含1的个数
int func(int x)
{
int count = 0;
while(1)
{
count ++;
x = x &(x-1);
}
return count;
}
该函数返回的是x转换为二进制之后为1的个数。假如x = 9999(10011100001111),count = 8;
count = 10011100001111 & 10011100001110 = 10011100001110
count = 10011100001110 & 10011100001101 = 10011100001100
count = 10011100001100 & 10011100001011 = 10011100001000
count = 10011100001000 & 10011100000111 = 10011100000000
count = 10011100000000 & 10011011111111 = 10011000000000
count = 10011000000000 & 10010111111111 = 10010000000000
count = 10010000000000 & 10001111111111 = 10000000000000
count = 10000000000000 & 01111111111111 = 0


4.1. "&&"与运算中,左右两边值真才为真,当左边的值为假时右边的条件就不执行了。
4.2. i++: 是先对i进行了操作之后,才进行i++;
++i: 是先进行i + 1之后才对i进行操作。


5. C中printf计算参数时是从右到左压栈的。
main()
{
int b = 3;
int arr[] = {6,7,8,9,10};
int * ptr = arr;
*(ptr++) += 123;
/* 打印结果:129 7 8 8 */
printf("%d %d %d %d\n",*(ptr - 2),*(ptr - 1),*ptr,*(++ptr));
/* 打印结果:7 7 8 8 */
printf("%d %d %d %d\n",*(ptr - 1),*(ptr - 1),*ptr,*(++ptr));
}
分析:首先一开始指针ptr是指向数组arr的第0个下标,即指向6,
但是*(ptr++) += 123;先对*ptr += 123 = 129进行了操作之后,ptr + 1,即指针指向数组的1下标,即7.
在printf计算参数时是从右到左压栈的,所以第一个进行运算的参数也是最右边的参数是*(++ptr):即指针ptr + 1,指向数组下标2的数,即8,然后取*(ptr)打印出来,打印8.
第二个参与运算的参数是*ptr,因为这个时候指针没有进行任何的移动,所以指针还是指向数组下标为2的数,即8.
同样的道理,下一个参数为*(ptr - 1):指针先减1,即ptr指向数组下标为1的数,即7.

疑惑:*(++ptr) 与 *(ptr + 1)的区别
*(++ptr): 操作结束之后,指针会进行ptr+ 1移动,而*(ptr + 1)进行操作的时候,只是根据指针ptr+ 1 计算对应的值出来,但是指针还是指向原来的位置,并没有进行ptr + 1移动。

6.1. if ('A' == a)的写法好过if (a == 'A'),因为这时如果将”==“误写为”=“,因为编译器不允许对常量赋值,就可以检查到错误。
6.2. 在进行循环运算的时候,一些算法可以放到循环体外,这样不需要每次进行循环的时候都要进行运算,可以提高程序的效率,但是缺点就是代码不够简洁。


7.浮点数在内存中的存放方式:
任何数据在内存中都是以二进制的形式存储的,在Intel CPU架构的系统中,存放的方式是小端的方式。
浮点数在目前所有的C/C++编译器都是采用IEEE所制定的标准浮点格式,即二进制科学表示法。
V = (-1)s * M * 2E的形式来表示一个数:
s(sign)符号: 决定这个数是负数(s = 1)还是正数(s = 0).对于数值0的符号位作为特殊情况处理。
M(significand)尾数:(规定M的整数部分恒为1,所以这个1就不进行存储了) 是一个二进制小数(有效数字位),它的范围是1~2的n次方-1,或者是0~1-2的n次方。
E(exponent)阶码: 对浮点数据加权,这个权重的2的E次幂。
类型 符号位 阶码 尾数 Bias(偏置值)
float 1 8 23 127 (32位)
double 1 11 52 1023 (64位)
举例:float型数据125.5转为标准浮点格式:
125的二进制表示形式1111101,小数部分表示为二进制位1,则125.5二进制表示为1111101.1,
由于规定尾数的整数部分恒为1,则表示为1.1111011 * 2^6, 阶码为6,则加上偏置位127为133,133的二进制:10000101
对于尾数将整数部分1去掉,为1111011(1.1111011的尾数),在其后面补0使其位数达到23位:11110110000000000000000.
则125.5的二进制表现形式:0 10000101 11110110000000000000000
然后125.5在内存中的存放方式为:
高地址 低地址
01000010 11111011 00000000 00000000

int main()
{
float a = 1.0f;
/* 强制输出a 的整数部分。 */
cout << (int)a << endl;
/* 输出的是在内存中a所对应的地址。*/
cout << &a << endl;
/*  浮点数在内存里和整数的存储方式不同。(int &)a相当于将该浮点数地址开始的sizeof(int)个字节当成(int)型的数据输出,因此这取决于float型数据在内存中的存储方式*/
cout << (int &)a << endl;
/* boolalpha: 函数名称,功能是把bool值显示为true 或false. 否则输出将为1/0 */
cout << boolalpha << ((int)a == (int &)a) << endl;

float b = 1.0f;
cout << (int)b = endl;
cout << &b << endl;
cout << (int &)b << endl;
/* float a = 1.0f在内存中的表示都是3f800000,而浮点数和一般的整型不同,当(int &)b强制转换时,会把内存值3f800000当做int型输出,*/
cout << boolalpha << ((int)b == (int &)b) << endl;
return 0;
}


8.指针类型的转换
时时刻刻记住一点,所有的指针都是占四个字节。
int main()
{
unsigned int a = 0xFFFFFFF7;
/* 将int类型的a转换为unsigned char类型,由于int类型在内存中占4个字节,而unsigned char 类型在内存中占1个字节,所以输出的时候只能输出1个低字节,3位和高于3位的数据将被程序自动抛弃。*/
unsigned char i = (unsigned char)a;
/* 0x000000F7. */
printf("%08x\n",i);
/* 将&a是一个指向unsigned int的指针,现在被强制转换为char *型的指针,因为所有类型的指针在内存中都是占4个字节,所以没有数据丢失,可以输出原本的数据。*/
char *b = (char *)&a;
printf("%08x\n,"*b);
}

9.C语言中符号运算符的优先级
第1级: [] () . -> (从左到右)
第2级: -   ~ ++ -- * & ! (type) sizeof (右到左)
第3级: / * %
第4级: + -
第5级: << >>
第6级: > >= < <=
第7级: == !=
第8级: &
第9级: ^
第10级: |
第11级: &&
第12级: ||
第13级: ?:
第14级: = /= *= %= += -= <<= >>= &= ^= |=
第15级:
例子: b = ~a >> 4 + 1;
分析:b = (~a) >> (4 + 1);

10.判断一个数是否是2的n次方
如果该数是2的n次方的话: 00000000 0
00000001 1
00000010 2
00000100 4
00001000 8
00010000 16
00100000 32
01000000 64
10000000 128
那么该数x与(x-1) 相位与的话,结果便为0. 00000100 & 00000011 =  0.
所以可以根据 !(x & (x - 1))。


11.
int f(int x, int y)
{
return (x & y) + ((x ^ y)>> 1);
}
那么:(729,271) = __.
分析:(x & y) :是表示x 与 y 相应位都为1的时候才为1,这个结果是x与y的相同位的一半。
 (x ^ y) >> 1: 是表达的x 与 y 相应位不同的时候就为1(即表示的是x 与 y 的不同位),>>1:可以表示除2(x 与 y 的不同位的一半).
那么f(x,y)求的是x与y的平均值。

12.位运算实现两个整数的加法运算。
int Add(int a, int b)
{
if(b == 0)
{
return a;
}

int sum, carry;
sum = a ^ b; //位运算没有进位
carry = (a & b) << 1; //完成第二步进位的加法运算并且左移
return Add(sum, carry); //进行递归,相加。

}

13.找出两个数的较大数
1. int max = ((a + b) + abs(a - b)) / 2;
/*根据两数相减后的数的符号,如果为负数,那么31位存放的是1, 如果是正数,31位存放的是0. */
2. int c = a - b;
char * strs[2] = {"a Large ", “b Large ”};
c = unsigned(c) >> (sizeof(int) * 8 - 1);


14.获得三个数中的中间数(非最大,非最小)
inline int max(int a, int b){return a>= b ? a : b;}
inline int min(int a, int b){return a<= b ? a :b;}
inline int medium(int a, int b, int c)
{ //求两两数的最大值,则可以排除最小值
int t1 = max(a,b);
int t2 = max(a,b);
int t3 = max(a,b);
//返回最大两个数中的最小值,那么就是中间值
return min(t1, min(t2, t3));
}

15.a,b两个数不使用中间值进行交换
1. 缺点就是如果a,b都是较大的两个数,a = a + b会越界
a = a + b; //两个数相加,为接下来做准备
b = a - b; //a - b得到的就是原来的a,赋给b
a = a - b; //a - b得到的就是原来的b,赋给a
2. 最好的方法
a = a ^ b; //得到相应位不同则为1,
b = a ^ b; //又进行异或,则可以得到原来的a
a = a ^ b; //得到原来的b

16.在C++程序中被C编译器编译后的函数,为什么要用“exter C”?
因为C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为void foo(int x, int y).
该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。
C++提供了C连接交换制定符合extern "C"解决名字匹配问题。

17.头文件中的ifndef/define/endif是干什么用的?
防止该头文件被重复引用。

18.switch语句:
当从满足case 'c':开始,如果执行了相应的指令之后没有break终止switch
语句,那么余下的case操作,不管满不满足相应的条件,都会被执行的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值