阿龙的学习笔记---《程序员面试宝典》阅读笔记

  • 第五章:程序设计基本概念

    • 函数传参顺序一般是从右到左

      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关键字需要与函数定义连在一起,而不是函数声明。


  • 第七章:指针和引用

    • 二者差别
    1. 引用不能为空,指针可以为空;
    2. 引用使用前不需要判断是否为空,指针需要;
    3. 指针初始化后可以再被修改指向另一个,而引用只能初始化时指向元素
    • 删除delete一个指针之后,要将这个指针指向NULL,否则可能出问题。
    • new/delete 和 malloc/free之间的差别
      • new/delete是运算符,而malloc/free是函数。
      • new/delete面向C++中的非内部数据类型,可以创建及销毁时调用构造/析构函数。

  • 第八章:循环、递归和概率

    • 没啥总结的。

  • 第九章:STL

    • 还没咋看。

  • 第十章:面向对象

    • struct和class
      • C++中的struct其实和class意义几乎相同,也可以有构造函数、析构函数等。
      • 区别在于struct默认成员类型是public,而class是private
    • 构造函数初始化列表中顺序不是从左到右,而是变量被声明的顺序。
    • 为啥子析构函数要声明为virtual?
      • 无论是否为virtual,派生类析构时都会先调用自己的析构函数,再调用基类的析构函数。那么virtual有啥用????
      • 还是多态的问题,对于指针来说,基类指针指向派生类对象,撤销基类指针时,如果不是virtual,则只会调用基类析构函数,因为是静态绑定;而如果是virtual,撤销时,会像上面一样,先调用派生类析构函数,再调用基类析构函数。
    • string这样管理资源的对象,赋值操作符operator=函数,要判断是否是自我赋值,否则会出错。

  • 第十一章:继承和接口

    • 继承体系中,构造时从基类开始构造。各个层次的同名变量不会覆盖,都是单独的变量。
    • 虚函数
      • 关键字virtual告诉编译器不该早绑定(静态绑定)。
      • 编译器为每个拥有虚函数的class建立一个虚函数表vtable,放置指向特定的虚函数的指针。
      • 在类中有一个指向虚表的指针,称为vptr——虚指针 或 虚函数指针。
    • 虚继承
      • B继承A,C继承A, D继承B、C,那么D拥有两个A对象;
      • B虚继承A,C虚继承A, D虚继承B、C,那么D只拥有一个A对象。

  • 第十二章:位运算与嵌入式编程

    • 位制转换
      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用处
      1. 函数体内,static变量内存只被分配一次,下次时不会分配。

      2. 模块内的static全局变量,被限定作用范围,只能被模块内的函数使用。

        模块内的static函数只能被模块内的函数调用,而不能被外部调用。

      3. 类中的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’,所以使用时要注意。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值