小言_互联网的博客

阿龙的学习笔记---《后台开发:核心技术与应用实践》读书笔记(一)--- 编程语言 与 编译调试

382人阅读  评论(0)

听说是一本腾讯员工写的书,快到面试时候,拿来读一下看看,内容好像不太精,少了一丢丢所以然,但是包含了一些对面试有用的知识点,记录一下。


一、编程语言

  • c语言 是面向函数。

  • size_of 对函数来讲是得到 函数返回值类型 的size。

  • 字节对齐的规则是按照结构体中长度最长的来对齐,比如说有double类型是8byte,那么20字节的结构体会占用24字节的内存。

  • 宏定义一段语句的时候可以使用do…while(0); 这样的话整个代码块还算是一个语句。

  • extern ”C" 告诉编译器用C的方式来编译这一段。

  • 空类的占用空间为1byte,有虚函数的类为一个指针的大小为8byte。

  • 构造函数顺序:先调用基类的构造函数 -> 再调用成员的构造函数 -> 再调用派生类自己的构造函数。子成员对象的构造按照定义顺序,而不是初始化列表的顺序。析构反过来。

  • 重写=覆盖, 覆盖了基类中的virtual函数,必须返回值与参数列表一致,通过不同的指针调用,则调用不同的函数,体现了多态。而重载是参数个数或类型不一致,并无多态的体现,根据参数选择函数运行。最后 重定义(也叫隐藏) 则是重新定义了这个函数,原来的函数被隐藏。

  • 构造函数不能为虚函数:一是因为构造时必须要知道确切的类型,二是调用虚函数需要使用虚指针,而没有构造前又没有虚指针。而需要被继承的类最好设置虚析构函数

  • string类的简易实现

    • 可以参考https://www.cnblogs.com/zhizhan/p/4876093.html

    • 内层是字符指针的操作,有一些需要注意的点,以及一些字符串操作。

    • 通用构造函数:传入字符串指针,需要判断指针是否空,然后new申请内存存放,如果为空,则需要将 ‘/0’ 放入。

      String::String(const char *str)//通用构造函数
      {
             
      	if(!str){
             
      		length = 0;
      		data = new char[1];
      		*data = '\0';
      	}else{
             
      		length = strlen(str);
      		data = new char[length + 1];
      		strcpy(data, str);
      	}
      }
      
    • 拷贝构造函数:需要进行深拷贝,不只是复制指针,而是复制内容。string不会为空,则只需要使用成员函数size(),然后复制内容即可。

      String::String(const String &str)//拷贝构造函数
      {
             
      	length = str.size();
      	data = new char[length + 1];
      	strcpy(data, str.c_str());
      }
      
    • 析构函数:delete[] 释放内存,改变长度。

      String::~String()//析构函数
      {
             
      	delete []data;
      	length = 0;
      }
      
    • 拷贝操作符重载:首先要判断自赋值。然后将新的复制过来,长度不定所以需要重新申请内存。

      String& String::operator=(const String &str) //重载=
      {
             
      	if (this == &str)	
      		return *this;
      	delete []data;
      	length = str.length;
      	data = new char[length + 1];
      	strcpy(data, str.c_str());
      	return *this;
      }
      
    • 获取长度函数:内联加快速度。

      inline size_t String::size() const//获取长度
      {
             
      	return length;
      }
      
    • 重载输入运算符:先申请一块足够大的内存用来存放输入字符串,再进行新字符串的生成。这是一个比较简单朴素的实现,网上很多直接is>>str.data的方法是错误的,因为不能确定str.data的大小和即将输入的字符串的大小关系。

      istream& operator>>(istream &is, String &str)//输入
      {
             
      	char tem[1000];  //简单的申请一块内存
      	is >> tem;
      	str.length = strlen(tem);
      	str.data = new char[str.length + 1];
      	strcpy(str.data, tem);
      	return is;
      }
      
    • 重载输出运算符,只需简单地输出字符串的内容即可。注意为了实现形如cout<<a<<b的连续输出,这里需要返回输出流。上面的输入也是类似。

      ostream& operator<<(ostream &os, String &str)//输出
      {
             
      	os << str.data;
      	return os;
      }
      
    • 获取C字符串:

      inline const char* String::c_str() const//获取C字符串
      {
             
      	return data;
      }
      
  • vector的简易实现

    • vector底层是一个c格式的数组,通过维护first(开始位置迭代器),last(存储元素的末尾di),end(分配内存的末尾)来工作。
    • 书中实现的方式是通过theSize表示存储元素的末尾,theCapacity表示分配内存的末尾。
    • 通过一个insert_before来实现push_back、push_front、带参数的构造函数。
    • 通过一个成员函数allocator()来分配内存、deallocator()来销毁内存。在容量不够的时候,重新分配内存,然后销毁原来内存。
    • 拷贝构造函数文中写的是 *this=other,但这样的话是浅拷贝,数组会被直接copy过来。而stl的vector应该是深拷贝,深拷贝则需要将数组中的元素都拷贝过来,而不是只是将指针指向同一个数组,这样会有问题。POD类型可以直接memncpy(),非POD可以for循环然后new。(POD(Plain Old Data,普通旧数据)是指int,float等兼容C的内置类型,支持memncpy)。
    • vector拷贝构造函数——深拷贝也是一个知识点,参考这个博客:https://blog.csdn.net/double_happiness/article/details/71305435。
    • map是键值类型的数据结构,采用红黑树,时间Ologn;unordered_map是:哈希表+bucket解决hash冲突(bucket为链表<8或者红黑树>=8),较好情况下是O(1)。不需要有序的情况下,用哈希的更好。

二、编译调试

  • 静态链接库动态链接库的特点,可以复习一下。

  • GCC 和 G++:

    • G++会把 .c 和 .cpp 文件都当做C++文件。而GCC会区分。
    • G++在编译阶段会调用gcc,因为gcc不能对C++程序进行链接,所以一般都直接用G++。
  • makefile的编写是个问题,没怎么用过,死记硬背也不好记。

  • strace命令可以让你看到一个程序后台运行的系统调用;还可以显示收到的信号;还可以用来统计系统调用的次数等。调试中,可以看到一些系统调用的返回的错误码的值,这样可以定位到程序出错。

  • GDB调试,不仅可以调试运行程序,还可以调试coredump文件。core dump(段错误)是程序发生某些错误时保存下来的一手资料,包含内存值、CPU寄存器的值、函数调用栈等。一般内存越界、多线程竞争问题,非法指针操作,堆栈溢出等情况会触发。

  • top是linux下的性能分析工具,类似任务管理器,显示资源占用情况。可以展示CPU、内存等的总体使用情况,以及各个进程的使用情况。

  • linux下C程序的内存空间布局

  • 堆heap和栈stack的区别:

    • 申请方式不同

      • 栈由系统自动分,声明在函数中一个局部变量 int 系统自动在栈中开辟空间。
      • 堆则需要程序员自己申请,并指明大小。
    • 申请响应不同:

      • 栈是自动增长的,有空间则可以申请,否则报栈溢出。
      • 而堆则是类似段表类型,空闲节点链表,一块一块的内存,分配一块够大的,然后占用一部分,剩下的又被分为一块,放入链表中。
    • 申请速度:

      • 栈较快,而堆较慢。
    • 结构:

      • 栈是一块固定大小的空间,有系统限制,一般较小;
      • 而堆是链表结构,不连续,有内存碎片,容量较大,受虚拟内存限制 。

转载:https://blog.csdn.net/weixin_39596127/article/details/113808198
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场