-
第五章:程序设计基本概念
-
函数传参顺序一般是从右到左
C++编译器默认使用的是 __cdecl 模式(还有stdcall、pacal等标准),参数是通过栈传递的,因此是从右到左的传参顺序。
int f(int a, int b, int c) { return 0; } int main(){ return f(printf("a"),printf("b"),printf("c")); }
函数的参数是通过栈传递的。因此参数从右往左入栈顺序是:printf(“c”),printf(“b”),printf(“a”)。依次计算出结果:cba
-
大小端模式(总是记不住)
小端:低位在低地址
大端:低位在高地址
内存读取都是从低到高 -
类型转换和运算符
这里b最终输出多少呢?首先,由于表达式中有4和1,所以编译器会将式中的值都转换为int型,再进行计算。然后计算完成,再转换为unsigned char赋值给b。
运算符优先级:~ 大于 + 大于 >>。
//所以首先将a取反,10100101变成01011010。 a = 10100101, ~a = 01011010; //再4+1 = 5。 4+1=5; //最后右移5位。 ~a >> 5 = 00000010;
这样对吗????不对! 因为忘记了第一条说的,要先转换为int再计算!答案如下!
//a转换为int a = 00000000···10100101 //将a取反,前面的0都变成1 a = 0000000······10100101, ~a = 11111111······01011010; //再4+1 = 5。 4+1=5; //最后右移5位。 ~a >> 5 = 11111010;
-
((x & y) + (x ^ y) >> 1)代表什么??
x位与y,x和y都是1,结果才是1;而x异或y,x或者y为1时才为1;右移1位代表除以2。
那么位与是相同位之和的一半,而异或除以2则是不同位之和的一半,加起来则是两个数之和的一半! 所以这是求平均数!
-
C++程序中调用C编译器编译的函数需要加上extern “C”?
是因为两个编译器编译函数名称的规则不同,C++编译需要区别不同重载函数 名称,所以内部会将函数名加上参数列表,以一定的规则作为名称。为了使C++编译器能够解决函数名匹配问题,需要加上extern “C”。
-
-
第六章:预处理、const、sizeof
-
用define表示一个常数,表示一年中有多少秒
重点是默认转换为int可能会超出长度,没说32位还是16位,16位则会溢出。所以在后面加上 UL 表示无符号长整形。
# define SECOND_PER_YEAR (365 * 24 * 60 * 60)UL
-
如果需要让class中的const成员函数修改某个成员变量,可以在变量定义前加上mutable。
-
sizeof计算拥有静态变量的class长度
class A{ int a; static int b; A(); ~A(); }
这样的话,sizeof(A)的值不为8,而是4,因为sizeof计算栈中分配的大小,而static int b位于全局变量区。
-
内联函数 与 宏定义
内联函数可以直接镶嵌到目标代码中,而宏定义则是直接替换。
内联函数会做参数类型检查,更加安全。
C中宏定义很重要,而C++中能不用就不用,用内联函数更好。
内联函数需要短小,无分支语句。再者最好是需要重复调用等,节省函数调用的切换时间。
inline关键字需要与函数定义连在一起,而不是函数声明。
-
-
第十章:面向对象
-
struct和class
- C++中的struct其实和class意义几乎相同,也可以有构造函数、析构函数等。
- 区别在于struct默认成员类型是public,而class是private。
-
构造函数初始化列表中顺序不是从左到右,而是变量被声明的顺序。
-
为啥子析构函数要声明为virtual?
- 无论是否为virtual,派生类析构时都会先调用自己的析构函数,再调用基类的析构函数。那么virtual有啥用????
- 还是多态的问题,对于指针来说,基类指针指向派生类对象,撤销基类指针时,如果不是virtual,则只会调用基类析构函数,因为是静态绑定;而如果是virtual,撤销时,会像上面一样,先调用派生类析构函数,再调用基类析构函数。
-
string这样管理资源的对象,赋值操作符operator=函数,要判断是否是自我赋值,否则会出错。
-
-
第十二章:位运算与嵌入式编程
-
位制转换
printf("%f", 5); printf("%d", 5.01);
结果是什么???结果令人匪夷所思···
首先,5 会被当做一个int型,占用4个字节,不会自动隐式转换成double或float。但 “%f” 会读取8个字节然后把读取到的值作为一个double型。因此,答案未知,会产生不可预期的答案。但测试所得会输出0.00000。
第二个也是同样的道理,5.01作为一个double型,占用8个字节。而“%d”读取了前4个字节作为一个int型输出。测试得到一个大数。
-
C++中的类型转换
- static_cast 是类似C中一般的转换,但有更多限制。首先不能做一些编译器觉得匪夷所思的转换,比如将(char*)转换为(int),有逻辑的一般都是可以的。而且还不能转换const属性,因为有 const_cast 来完成
- const_cast 是用来转换去除constness属性,如果你做转换constness或volatilness范围之外的转换,const_cast会报错。
- reinterpret_cast是从比特的角度去转换,与static_cast转换的结果会有所不同。假如都是将一个 int 转换为 double 。static_cast是安全的,假如 a=9,则转换成double b=9.0。但是reinterpret_cast则会将 a=9 的比特位复制到 double b,结果肯定不是9。 这个是不可移植的,在不同体系下可能会不同。
- dynamic_cast用于继承体系中向下转型(向上转型也可以,但是没必要,直接虚函数多态即可),即基类到派生类的转换。如果没有继承关系,编译器会报错。并且不是强制转换,有可能会转换失败返回NULL。
-
Volatile用处
- volatile是告诉编译器这个对象在运行过程中,可能由外部改变,编译器不能做优化。
-
Const一定不能说是“常数”,而是“只读”!
-
Static用处
-
函数体内,static变量内存只被分配一次,下次时不会分配。
-
模块内的static全局变量,被限定作用范围,只能被模块内的函数使用。
模块内的static函数只能被模块内的函数调用,而不能被外部调用。
-
类中的static成员变量属于整个类,所有对象只有一份。
类中的static成员函数属于整个类,不接收this指针,且不能访问普通成员变量,只能访问static成员变量。
-
-
-
第十三章:数据结构基础
-
heap和stack的区别
-
堆heap和栈stack都是程序内存分配时的区域,除此之外还有全局变量(静态static)区、文字常量区、代码区。
-
申请后的响应不同:
自动变量都是被分配到栈stack上,而new、malloc申请的数据内存会在堆heap中。
-
申请大小限制不同:
栈中的数据是自动管理的,作用域退出时清理,所以是连续的内存。所以分配时只要剩余空间够大就可以分配成功。
而堆中的则是链表形式,不是连续的内存,在空闲链表中找一个能装下的分配,有可能不是刚好,剩余的空间会继续加入空闲列表。
windows下,栈是从高到低分配,而堆是从低到高。
-
申请效率不同:
栈的效率高,但是无法控制;而堆的效率较低。
-
-
关于代码段、数据段、堆、堆栈
- 代码段就是代码放置的位置。
- 数据段好像包含了全局(静态)变量区和字符常量区,字符常量、static静态变量,global全局变量都放在这里。
- 堆(heap)就是动态数据区,动态分配的new或者malloc产生的数据都放在这里。
- 堆栈就是所说的栈(stack),局部变量,函数参数传递等依靠栈的运作。
-
1亿个4字节浮点数,找出最大的10000个
-
首先,由条件反映出,1亿个浮点数无法一次性读入内存。所以不能直接进行排序。
-
分块查找:
每次查找100万个数据,找出其中1万个最大的;找100次,找到100万的数据;然后再从这100万数据里找1万最大的。
从100万数据里面找到1万个最大的,采用快速排序。并且不用完全排序,只需要找到基准,基准右边为1万个数据即可。
-
-
快速排序
【未完】其中有快排的代码,可以看看跟自己的那个有什么差别??哪个更好,更好记忆。
-
-
第十四章:字符串
-
strcpy函数编写
char* strcpy(char* dest, char* src){ //assert函数是说:如果条件不成立就捕捉到这种错误,并打印出错误信息,终止程序执行 assert((dest!=NULL) && (src!=NULL)); char* address = dest; //一个while循环就解决...牛逼 while((*dest++ = *src++) != '\0'); return address; }
-
在使用字符数组时,如果要当做字符串,使用strcpy、strlen等函数,则需要在末尾手动添加 ‘\0’。
-
strlen()函数不会计算结尾的那个‘\0’,所以使用时要注意。
-