(可以说这是我读书笔记吧,里面也许有一些不对,在这里拜求各位大神指教,也在这里建议大家在读我这篇文章的时候结合着书一起读,当读书笔记来看O(∩_∩)O)
1.P47 问题5.4
float f = 1.0f,为什么在内存中是0x3f80 0000???
答:这主要归结于float类型变量的存储
符号位 | 阶码位 | 尾数位 |
1 | 8 | 23 |
以上表格就是float类型变量在内存中存储结构其中1、8、23代表占了多少位。
EX100.0f,在内存中怎么表示呢?
step1.将100.f表示为二进制1100100.0f,
step2.将1100100.0f右移写为1.1001000f,右移了6位
因而100.0f的符号位为0,阶码为127+6,尾数为1001 0000...0(127是一个其实基数,如果浮点数是0.001f偏移量为负,那么就在127的基础上减去这个偏移量)
step3 这样我们就得到了100.0f的内存布局
0 1000 0101 100100...0
四个四个一组的话就算出其16进制表示42C80000
有了上面这个基础就很快的算出1.0f的内存表示符号位为0 阶码为0,也就是127,尾数为000...0,写出来就是0 0111 1111 000...0即3f80 0000
-----------------------------------------------------------------------------------------------------------------------
P51 5.5运算符问题
int main(void)
{
unsigned char a = 0xA5;
unsigned char b = ~a>>4+1;
printf("b = %d",b);
}
结果输出b = 250
Step1:首先进行隐式类型提升,>>运算符两边的运算数都转化为int类型最后在给b赋值时,将结果转为unsigned char型
Step2:那么a转为int型后就不是8位了而是32位,对a取反后就是111…110101 1010
Step3:因为+运算符的优先级大于>>运算符,所以先进行+运算,也就是对(~a)右移5位,得到00000111…11111010
Step4:最后进行类型强转,截取后8位得到250
补充:运算符优先级如下
[] 、()、.、 ->
单目运算符(-、(类型)、++、--、*、&、!、~、sizeof、)
双目运算符 (加减乘除)
位移 (<< >>)
算术判断 >=、<=、>、<、==、!=
位运算 &、^、|
逻辑判断 &&、||
三目运算符
赋值运算符
逗号表达式
注:从上到下,优先级依次降低
--------------------------------------------------------------------------------------------------------------------------
3.P52 5.5 面试题2
用一个表达式判断某数是否为2的N次幂(2,4,8….)
答案:!(X&(X-1))
问:为什么是这个结果??
答:如果num是2的N次幂,那么它仅有一位是1,假设该位为第n位,那么num-1在第n位绝对不是1,所以num&(num-1)可以用来判断num是否为2的n次幂
-------------------------------------------------------------------------------------------------------------------------
4.P60 面试题2
Const与define的区别??
Tips1.const较define有如下两个优点:
1)const修饰只读变量,其值有类型检查,而define只是进行宏定义,在程序编译时就进行了替换,没有安全性检查,且在替换时可能会发生意想不到的意外
2)const变量可以参与调试
Tips2:在C语言里,const被认为是只读变量,且是外部链接就是在编译时无法确定出该变量的值,所以这样的写法
const int size =100;
int arr[size];在C环境下是不支持的
而在C++环境下,以上写法是被支持的,C++对const进行了拓展,默认是内部链接,所以在C++环境下在定义const变量的时候就要对其进行初始化,这样C++编译器会对其进行类型检查之后进行替换,C环境下可以先不对其进行初始化。
Tips3:const变量的应用
1)修饰变量为不可变变量,但不是绝对的,你可以拿到这个变量的地址,完了修改该地址的内容
2)修饰函数参数
3)修饰函数返回值
4)修饰类的对象为常对象
注意:可以用const_cast去掉常对象的const属性
5)修饰类的成员函数为常函数,不改变对象的属性
注意:可以用mutable修饰类的某属性,该属性可以在常函数中被修改
---------------------------------------------------------------------------------------------
5.P64 内存对齐问题
使用#pragma pack()修改系统默认对齐方式
---------------------------------------------------------------------------------------------------------------------
6.P66 sizeof与strlen的主要区别
1)sizeof用来计算某类型对象所占内存大小
其返回类型是size_t,就是unsigned int,以容纳最大类型变量的大小
2)sizeof针对的对象是类型,而strlen针对的对象是char *且必须以’\0’做结尾
3)sizeof是一个运算符,strlen是函数
4)sizeof()的结果可编译阶段就可确定,而strlen()的结果必须在程序运行阶段才能得到,所以sizeof()的结果可以用于数组的大小
P69 面试题7
Tips1.sizeof()括号里的值会自动转化为sizeof(括号里变量类型)的大小,这个结果会在编译阶段确定其值,()里的语句不会被执行
EX:int a = 8; sizeof(a = 6);执行完后,a的值仍为8,因为sizeof不会计算括号里的值只是sizeof(int)的大小
Tips2:sizeof(string) = 4个人理解string里有一个char*pstring这个指针,所以sizeof(string)实质上是sizeof(char*)的大小
---------------------------------------------------------------------------------------------------------------
7.内联函数与宏定义
1)内联函数是一个函数,有类型检查,而宏定义只是一个宏,进行简单替换没有类型检查
2)内联函数不会产生编译阶段的代码,在函数运行到调用函数的地方,将内联函数嵌入到被调用处,而宏会在程序编译阶段进行替换进行宏展开
?什么时候函数可以被声明为内联函数
答:函数被多次调用,函数简单短小无for、while、switch等复杂结构
--------------------------------------------------------------------------------------------------------------
8.指针与引用的区别
指针通过获取记住某变量的地址,借助这个地址间接操纵变量
引用是变量的一个别名
引用和指针的区别主要在以下几点:
指针-------可以赋值为NULL,可改变其指向,sizeof(指针)=4
引用-------只能赋以对象,不可改变其指向,sizeof(引用)=被引用的对象的大小
指针主要用于在程序运行过程中其指向的对象需要不断改变或者有时候需要其指向为空(判断指针合法性)
引用主要用于指向一个对象且指向在程序运行中一直不变
--------------------------------------------------------------------------------------------------------------
9.P92 空指针与野指针
当一个指针所指向的对象被delete掉后,该对象所占用的那块内存被释放,原指针已不能在操纵这块内存,但指针仍指向这块不属于他自己的区域,也可以理解为乱指向,
在这时对野指针所指区域的任何操作都可能会引起程序崩溃
如果在对象被释放后,将该指针设置为NULL,该指针就成为了空指针,虽然对空指针的操作也会引起程序崩溃,但较之野指针空指针不会导致程序变得不可控,我们可以根据if(p==NULL)判断指针是否能用
--------------------------------------------------------------------------------------------------------------
10.P92 句柄
句柄是一个32位的整数,用来标识windows内部各个对象在内存物理地址列表的整数索引,相当于指针的指针
具体内容请参见《句柄小悟》http://blog.youkuaiyun.com/w_miracle/article/details/12158811
--------------------------------------------------------------------------------------------------------------
11.智能指针
有关智能指针请看《C++智能指针auto_ptr》一文,http://blog.youkuaiyun.com/w_miracle/article/details/12217511
--------------------------------------------------------------------------------------------------------------
12.P107请简单解释一下STL
(在这里大致描述一下STL,后面我会额外写一篇有关STL的文章,因为强大的STL不是区区几行字就能写完的)
STL——标准模板库,现已归入C++中,被嵌入在编译器中,我们可以方便的使用。
STL将模板的概念体现的淋漓尽致,所有的容器与算法都使用模板来实现,达到很好的代码复用性。
STL可以说由容器、算法、迭代器组成:
容器——用来装数据的一个容器,相当于用来盛饭的碗,就像不同的饭有不同的碗一样,对于不同的数据结构以及不同的要求我们有不同的容器——容器主要分为两大类:顺序容器(vector、list、deque、queue)以及集合(set、multiset、map、multimap)
算法——STL中有很多大牛写好的效率很高的算法,我们可以直接拿来为我所用而不用再编写算法,而且由于不同的容器对应了不同的数据结构,不同的数据结构会由其自己较为适合的高效算法,建议一般使用容器本身的算法。比如vector只是在末尾作insert操作较高效。而list容器做insert操作不讲究位置在哪里都很高效,所以如果程序中会有很多insert操作,建议使用list容器,且在用算法时使用list容器本身的算法
迭代器(iterator)——可以说是容器元素的指针,通过迭代器可以方便的操纵容器中的元素
--------------------------------------------------------------------------------------------------------------
13.P108 面试题2
题中首先涉及到拷贝构造函数的调用:
有三种情况会调用
1)当用类的一个对象初始化该类的另一个对象时.例如:
int main()
{
point A(1,2);
point B(A);//用对象A初始化对象B,拷贝构造函数被调用.
}
2)如果函数的形参是类的对象,调用函数时,进行形参和实参结合时.
void f(point p)
{
}
main()
{
point A(1,2);
f(A);//函数的形参为类的对象时,当调用函数时,拷贝构造函数被调用.
}
3)如果函数的返回值是类的对象,函数执行完成返回调用者时.
point g()
{
point A(1,2);
return A;//函数的返回值是类的对象,返回函数值时,调用拷贝构造函数.
}
void main()
{
point B;
B = g();
}
4)需要产生一个临时类对象时。
下来讨论一下深拷贝与浅拷贝问题:
对象B是对象A的深拷贝,就是另外克隆了一个A对象命名为B,它们两是两个独立的对象,各自有各自的一块内存,两者互不干涉,只是两个对象里的数据的值是一样的
假如对象B是对象A的一块浅拷贝,呢么就是说对象B的指针也指向对象A所述的那块区域,对A对象的操作也是对B的操作,他们两个其实是一个东西,B就是A的一个别名,只占了一块内存
题中 a1->push_back(d1),调用了默认的拷贝构造函数,浅拷贝了对象d1,pushback之后,vector里的d1与对象d1其实是两个对象,但他们内部数据str却指向同一块区域,在delete时就会发生对同一块区域多次delete的情况,造成程序崩溃
如果自己实现深拷贝就可以避免上述情况
--------------------------------------------------------------------------------------------------------------
14.P109 面试题3
?如何删除容器内重复的元素
题中
for(itr; itr!= myvec.end();)
{
if(1== *itr)
{
Itr2 = itr;
myvec.erase(itr2);
}
else
itr++;
}
会引发程序崩溃,因为myvec.earse(itr2)会导致itr失效,完了再对itr进行操作必是错误的
正确做法如下:
Way1:for(itr; itr!= myvec.end();)
{
if(1== *itr)
{//earse操作会返回所erase的元素的下一个元素的位置
itr=myvec.erase(itr);
}
else
itr++;
}
该方法不会改变原容器内数据的顺序
Way2:
sort(myvec.begin(),myvec.end());//首先对原容器数据进行排序,这样才能使用unique算法
myvec.erase(unique(myvec.begin(),myvec.end()),myvec.end());
//unique将相邻的重复的元素移到最后,返回一个iterator指向最后的重复元素
//再用erase删除就达到了去除重复的目的
显而易见,方法2虽然简单快捷但会改变原容器的顺序
------------------------------------------------------------------------------------------------------------------------
15.P140 有关继承方式,private、protected、public
下面这张表将不同继承方式对子类带来的影响说得很清楚,感谢我家那位的贡献哦O(∩_∩)O:
----------------------------------------------------------------------------
基类访问特性 继承方式 子类的访问特性
private no access
protected private private
public private
------------------------------------------------
private no access
protected protected protected
public protected
------------------------------------------------
private no access
protected public protected
public public
----------------------------------------------------------------------------
这里有一个我当时很纠结的问题:
如果有一个类B公有继承A,类C公有继承B类,那么A类里的私有成员在C类里还有吗?
答:有,B类里基类的私有成员变为不可访问成员,C继承B,那么C原封不动的也涵盖了B类所有的数据成员拿了过来
对于权限这一说,不可访问是最低的权限,没有比这个还低的权限,这种三层的继承关系不要将三个类联系起来想,要将它们拆为两个类:B类和C类,B类理由4种不同类型的成员:public、protected、private及不可访问的,那么C累的数据成员的存取权限现在就取决于它的继承方式
还有要再次声明:派生类其对象所占大小=其新增数据大小+基类对象大小+其他(虚函数、虚继承、内存对齐带来的额外的内存开销)
对其基类的大小不作任何改变,直接加上来
------------------------------------------------------------------------------------------------------------------------------------
16.P173关键字volatile
仅就volatile这个单词而论,他的意思是易变的、不稳定的
在我们编程环境中意思就是,这个变量除了在我这个进程中会被改变其值也有可能会在另一个进程中被改变,所以编译器不能对这个变量的取值做某种优化处理(在当前程序中对此volatile变量赋值后再也没有改变,那么我下次引用这个变量指的时候就不用从他的地址空间去取),因为别的进程随时有可能修改该变量,你每次取值都要重新取。
const volatile int i = 10;意思就是变量i在当前环境中不能被修改,但有可能会在别的进程中被修改
?哪些变量会是volatile类型
答:1)并行设备的硬件寄存器(状态寄存器)
2)一个中断服务子程序(ISR)会访问到的非自动变量
3)多线程应用中被几个多任务共享的变量
【上面提到中断服务子程序ISR,对于嵌入式开发这是必不可少的知识储备】
【ISR程序中的要点:】
【无返回值、无参数、函数体简单、不定义double型变量、不进行double的运算、不出现printf函数】
---------------------------------------------------------------------------------------------------------------------------------
17.P210排序算法的复杂度及稳定性比较
稳定的排序 | 时间复杂度 | 空间复杂度 |
冒泡排序(bubble sort) | 最差、平均都是O(n2); 最好是O(n) | 1
|
双向的冒泡排序 (鸡尾排序) | 最差、平均都是O(n2); 最好是O(n) | 1 |
插入排序(insertion sort) | 最差、平均都是O(n2); 最好是O(n) | 1 |
归并排序(merge sort) | 最差、平均、最好都是O(nlogn) | O(n)
|
桶排序(bucket sort) | O(n) | O(k)
|
基数排序 (Radix sort) | O(dn) (d是常数) | O(n) |
二叉树排序(Binary tree sort) | O(nlogn) | O(n) |
0000000000000000000000000 | 0000000000000000000000000 | 0000000000000000000000000 |
不稳定排序 | 时间复杂度 | 空间复杂度 |
选择排序(selection sort) | 最差、平均都是O(n2) | 1 不稳定 |
希尔排序(shell sort) | O(nlogn) | 1 不稳定 |
堆排序(heap sort) | 最差、平均、最好都是O(nlogn) | 1 不稳定 |
快速排序(quick sort) | 平均O(nlogn); 最坏情况O(n2) | O(logn) 不稳定 |
有关这些复杂度个人认为死记硬背实行不过的,必须手动写写代码不断熟悉过程,才能牢记于心,有关算法稳定性,我推荐大家看看这篇文章:http://www.cnblogs.com/wonderKK/archive/2012/10/09/2715986.html
------------------------------------------------------------------------------------------------------------------------------------
18.P260软件测试之白盒测试
Sorry,待补充O(∩_∩)O~
------------------------------------------------------------------------------------------------------------------------------------
19 P266 进程间通信
具体请参见本博客《进程间通信》一文,链接地址http://blog.youkuaiyun.com/w_miracle/article/details/12253499
-------------------------------------------------------------------------------------------------------------------------------------
20 P268 死锁的避免
具体请参见本博客《死锁的产生与处理》一文,链接地址http://blog.youkuaiyun.com/w_miracle/article/details/12254159
(持之以恒.....虽然是欢度国庆但是我一定要坚持,坚持这几天完了在十月收获自己的offer)
......未完,持续更新中2013.10.03