感觉 C++ 很简单,但为何这么多劝退的?

先用看两个 C++ 程序做个热身

10 天整理完语法?来请题主先看看下面 C++ 程序中语法错误如何解决?

#include 

class A {

};

void f1(std::unique_ptr&& a1) {
    
}

void f2(std::unique_ptr&& a2) {
    //这里编译有错误,如何修改?
    f1(a2);
}

int main()
{
    std::unique_ptr spA(new A());
    //这里编译有错误,如何修改?
    f2(spA);

    return 0;
}

再来看一个问题,以下程序为何会崩溃:

#include 
#include 
#include 

class A {
public:
    A() {

    }

    ~A() {

    }
};

class B {
public:
    B(A* pA) : m_spA(pA) {

    }

    ~B() {

    }

private:
    std::unique_ptr  m_spA;
};


int main()
{
    {
        std::map> mymap;
    
        auto pA = std::make_unique();
        auto pB = std::make_shared(pA.get());

        mymap.emplace(1, pB);
    }

    std::cout << "hello world." << std::endl;

    return 0;
}

校招 C++ 要求

我们再来看下企业是对 C++ 校招生是如何要求的。

C++ 是一门很注重语言和接口背后原理的编程语言,下面分享的关于 C++ 的内容都是比较常用的(不存在为了考察而考察),也就是说这些知识在实际 C++ 编码中都会用到。

C++ 语言篇

考察 C++,不仅仅指 C++,也可能包含部分 C 语言的内容。

  • 请说出 const char *p 、 char const *p 与 char * const p的区别,这个问题答错,面试基本不会通过,说明基本功差
  • static_cast、dynamic_cast、reinterpret_cast 和 const_cast 几种 C++ 类型转换符的区别偶尔也会问到,面试者至少要知道第一种。
  • 看下面代码,计算各个值(这个问题答错,面试基本不会通过,说明基本功差):
// 32位系统上
struct A {
    int  i;
    char j;
};

A a;
A* pa = &a;

sizeof(a) = ?
sizeof(pa) = ?

int arr[8];
int* parr = arr;

sizeof(arr) = ?
sizeof(arr[0]) = ?
sizeof(parr) = ?

char sz[] = "helloworld";
char* psz = sz;

sizeof("helloworld") = ?
sizeof(sz) = ?
sizeof(psz) = ?
  • 关于 C++ 面向对象,问得最多的问题是 C++ 的多态,一般会问多态的概念和多态在 C++ 语言中的实现,如果面试者说出了多态用途并引出了类的虚表,接下来面试官可能会追问虚表的结构。一个常见的问题是“菱形继承”中虚表的个数,即类 A 有子类 B 和 C,类 D 继承 B 和 C,那类 D 有几个虚表呢?

感觉 C++ 很简单,但为何这么多劝退的?_第1张图片

  • 有些面试官会以面向对象的三大特性开始提问,面试者回答出来“封装”、“继承”和“多态”后,注意了,接下来面试官可能要开始考察“继承”和“多态”(多态上文介绍了,不再重复)。常见的继承问题有,我们知道 C++ 的类继承分为 public、protected、private 三类继承方式,面试官会询问如果类 B 有一个 public/protected/private 的方法 f,现在类 A 以 public/protected/private 方式继承,那么在 B 中方法 f 是何种访问权限。此类问题答错,面试基本不会通过。
  • 考察继承的时候也会问一些父子类的构造和析构顺序或者子类析构函数为何要用 virtual 关键字声明的简单问题。这类问题不问则罢,如果被问到,答错,面试必挂。
  • 继承问题中还有常常会问重载(override)和重写(overwrite)的区别。关于重载有两个常见的问题,第一个:一个类方法名和参数数量、类型和顺序都是一样的,但是返回值类型不一样,是否构成重载?这个问题比较简单;第二个问题,一个方法加了 const 和不加 const 是否构成重载?答案是构成重载的,如果读者想不通,可以看下面两个表达式:
a[1] = 1;
b = a[1];

如果 a 是一个类的实例,前者调用 a 类的 int operator[](int i) 方法,后者调用 a 类的 int operator[](int i) const 方法。你答对了吗?

  • C++ 也会对常见的 stl 容器进行考察,例如一般问你是否用过 std::map、std::set 等数据结构,如果你说没用过,那面试到此就基本结束了(面试不通过);如果你说用过,接下来会问你这些容器背后的数据结构、插入删除的算法复杂度(最好和最坏的情况)、以及各个容器的使用场景和注意事项。有的面试官还会问,std::vector 的 resize 和 reserve 方法的区别。
  • 如果岗位要求会使用 C++11 或者面试者的简历中提到 C++11 等 Modern C++ 的内容,面试官会问一些 C++11 的内容,通常会问你用过或者熟悉哪些 C++11 的特性,我看部分同学只能简单地说出 auto 关键字等少量语法特性,这样显然是不行的。C++11 问得最多的是几种常用的智能指针,即 std::unique_ptr、std::shared_ptr、std::weak_ptr,面试者需要熟悉这几种智能指针的使用场景和区别,以及智能指针背后的实现原理(引用计数),智能指针还会结合多线程场景来问,例如智能指针是否是线程安全的(引用计数是线程安全的,管理的内容不是),关于 C++11 的智能指针的使用,我写过一篇详细的文章 《Modern C++智能指针详解》。左值、右值、左值引用、右值引用、std::move、std::emplace 和移动构造函数的内容也是 C++11 面试常问的问题。     Modern C++智能指针详解
  • 如果面试的岗位对底层要求有一定的了解,会结合 C 语言考察一些内容,例如函数的调用方式。面试官可能不会一上来就直接告诉你他在考察函数调用的知识点,他可能会结合一些案例来问,例如,假设让你实现 C 语言中的 printf 函数,需要注意哪些事项?一般有两点,第一点支持不定项参数,这个如果看过相关源码的同学,可能知道使用 va_ 系列的宏就可以了,另外一点是这种支持不定参数的函数的调用方式必须是 C 调用(__cdecl),不能是标准调用(__stdcall),因为 C 调用由调用者来平衡堆栈,标准调用由被调用者来平衡堆栈,对于支持不定参数的函数,只有实际被调用时,才知道要平衡多长的堆栈,因此如果使用标准调用,编译器不知道如何为该函数生成平衡堆栈的指令。如果你答得不错,面试官可能会深入下去,考察你栈的结构,各个变量和函数参数在栈中的分布。如果你不明白我在说什么,可以参考这篇文章《你一定要搞明白的C函数调用方式与栈原理》。考察 C++ 中无法返回一个局部变量的地址或引用的原因也是对栈结构知识的考察,当然,可能从栈又会引出堆的概念和用法,这就属于操作系统原理的知识了。
  • C++ 中的 __thiscall 有时候也会在考察范围内,比如面试官问 C++ 的类实例方法和静态方法如何实现的,给一个类增加一个静态/实例方法是否会增加类的大小,给一个类增加一个静态/实例字段是否会增加类的大小,sizeof 一个无任何方法和成员的空类等于多少,为什么不是 0?

操作系统原理篇你      

操作系统原理考察的内容一般比 C++ 语言考察的内容和范围要广,但是在面试官问了几个问题之后,如果面试者答得不好,面试官就会换其他方向问了。

  • 操作系统原理问的最多的是多线程的问题或者叫资源同步或者资源竞态或者锁的问题。最常见的问题如进程和线程的区别,线程同步有哪些技术,一般面试官会期望你能说出几种,在 Windows 操作系统上常用的有 CriticalSection、Event、Semaphore、Mutex,在 Linux 操作系统上有 mutex、semphore、read-write-lock、condition-variable,面试者需要知道这几种对象分别用于什么场景,简单来说,mutex 可以将一大片代码段进行加锁,对于当前线程可以阻塞也可以递归(递归锁),对于其他线程直接阻塞,semphore 用于资源有计数需求的精准控制,读写锁用于有读有写的场景,条件变量用于条件不成熟时的阻塞和条件成熟时的主动唤醒。读写锁和条件变量是问的最多的两个对象,面试者需要知道读写锁读与写竞争的四种情形,以及如何写出正确使用条件变量的代码,我的建议是实际写一写,搞明白就不会忘记了,另外,条件变量的虚假唤醒概念也是经常被问到的。有的同学说不出来,上来就说分布式锁,这里考察的的一般是进程内的多线程之间的锁,分布式锁指的是多个进程之间的锁,建议千万不要在面试的时候信口开河。在《C++服务器开发精髓》一书第 3 章详细地介绍了 Linux 和 Windows 操作系统层面提供的线程同步对象的用法和注意事项。
  • 进程之间的通信方式也是经常考察的知识点,常见的方法有编译器的共享段、共享内存、管道、socket 等,当然,Windows 上还有油槽(mailslot),Linux 和 Windows 都有的剪贴板都是进程共享的常见方法。
  • 另外,关于进程的考察,面试者还需要掌握 Linux 孤儿进程、守护进程、僵尸进程等概念,知道 fork 和 exec 调用的区别。
  • 面试中也可能会考察一些编译链接的知识,如一个 .cpp 文件变成可执行程序会经历哪些阶段,静态变量、全局变量(初始化的和未初始化的)分别放在可执行文件的什么位置,映射到进程地址空间中之后,又放在什么位置。
  • 逻辑地址、虚拟地址和物理地址的区别,同样的道理,面试官也可能会从一个小问题慢慢深入,例如代码中 int i = 0; 中变量 i 的地址属于哪种地址,什么是实模式和保护模式等等,堆和栈在内存地址空间的分布和增长方向。
  • 什么是内存映射文件,何时使用内存映射文件。
  • 动态链接库和静态链接库的区别是什么?如何生成动(静)态链接库。

开发一款商业 C++ 项目还需要哪些知识?

除了 C++ 本身的语法,你还需要学习操作系统的常用 API 接口函数、Socket 编程等等,不然可能还是很难写出有用的程序,这也是为什么很多同学在学习完 C++ 语言之后,始终只会写黑洞洞的命令行程序。所以开发一款商业 C++ 软件需要:

  1. C++ 语言本身熟练使用程度。
  2. 仅熟悉 C++ 语法你啥也干不了(随着 C++ stl 库的完善,这种现状正在慢慢改变),你必须结合一个具体的操作系统平台,所以得熟悉某个操作系统平台的 API 函数,比如 Linux,以及该操作系统的原理。这里说的操作系统的原理不局限于您在操作系统原理图书上看的知识,而是实实在在与系统 API 关联起来的,如熟练使用各种进程与线程函数、多线程资源同步函数、文件操作函数、系统时间函数、窗口自绘与操作函数(这点针对 Windows)、内存分配与管理函数、PE 或 ELF 文件的编译、链接原理等等。
  3. 网络通信,网络通信在这里具体一点就是 Socket 编程。这里的 Socket 编程不仅要求熟练使用各种网络 API 函数,还要求理解和灵活运用像三次握手四次挥手等各种基础网络通信协议与原理。

说了这么多,你可能会觉得很抽象。我这里给你举两个例子,一个是 C++ 客户端软件,一个是流行的 C/C++ 后台服务(Redis,严格来说,是 C)。

例子 1 电驴

假设我们现在要开发一个类似电驴这样的软件,软件界面如下图:

感觉 C++ 很简单,但为何这么多劝退的?_第2张图片

感觉 C++ 很简单,但为何这么多劝退的?_第3张图片

 源码下载:

如上图所示,假设操作系统选择 Windows,使用语言使用 C++,这就要求您必须熟悉 C++ 常用的语法,如果还不熟悉,就需要补充这方面的知识。

在熟悉 C++ 语法的前提下,从这款产品实现技术来看,我们的目标产品分为 UI网络通信部分。下面将详细介绍这两部分。

UI 部分

对于 UI 部分,我们的认识是,这里需要使用 Windows 的窗口技术。可以直接使用原生的 Win 32 API 来制作自己的界面库,也可以选择一些熟悉的界面框架,如 MFC、WTL、Duilib、wxWidgets 等。无论您是在阅读别人的项目还是需要自己开发这样的项目,在确定了这款软件使用的 UI 库(或者使用原生 Win 32 API),您就需要对 Windows 的窗口、对话框、消息产生、派发与处理机制进行了解。同样的道理,如果不熟悉您需要补充相关的知识(关于这一点,下文不再赘述)。

接着,根据上图中的软件功能,大致分为三大模块,即资源下载分享。这三大块是可以使用一个 Windows Tab 控件去组织,这个时候您需要了解 Windows Tab 控件的特性。

  • 对于资源模块,本质上是一个窗口中嵌入了一个浏览器控件(WebBrowser 控件),那么您需要了解这一个功能点的相关知识。当用户点击了某个列表中某个具体的资源,可以对其进行下载。这就又涉及到 WebBrowser 控件与 C++ 宿主程序的交互了,那么如何实现呢?可以选择使用 ActiveX 技术,也可以使用 JavaScript 与 C++ 交互技术。
  • 再来看下载模块,当产生一个下载操作时,界面上会产生以下下载列表,每个列表项会实时显示下载进度、下载速率等参数,同时正在下载的项目也可以被暂停、停止和删除。那么这又涉及到 ListView 控件的相关功能,以及 ListView 如何与后台网络通信的逻辑交互。
  • 分享模块是将本地资源分享到服务器或者给其他用户。界面左侧是文件系统的一个快照,那么这又涉及到如何遍历文件系统(了解枚举文件系统的 API),右侧也是一个 ListView 控件,这里不再赘述。

网络通信部分

网络通信部分,主要有两大块,第一个是程序启动时,与服务端的交互;第二个就是文件下载与分享的 P2P 网络。您在阅读或开发的过程中,如果对这些技术比较陌生,您需要补充这些知识,具体的就是 Socket 的各种 API 函数,以及基于这些 API 逻辑的组合。当然可能也会用到操作系统平台所特有的网络 API 函数,如 WSAAsyncSelect 网络模型。

另外一点,网络通信部分如何与 UI 部分进行数据交换,是使用队列?全局变量?或者相应的 Windows 操作平台提供的特殊通信技术,如 PostMessage 函数、管道?如果使用队列,多线程之间如何保持资源的一致性和解决资源竞态,使用 Event、CriticalSection、Mutex、Semaphore 等?

当然,笔者这里只列举了这个软件的主干部分,还有许多方方面面的细节需要考虑。这就需要读者根据自己的情况,斟酌和筛选了。您想达到什么目的,就要去学习和研究相关的代码。

总结起来,可以得到如下公式:

一款 C++ 软件 = C++ 语法 + 操作系统 API 函数调用

例子 2 Redis

这里以学习 Redis 为例。将 Redis 源码从官网下载下来以后,使用喜欢的代码阅读器进行管理。我这里使用的是 Visual Studio,如下图所示:

感觉 C++ 很简单,但为何这么多劝退的?_第4张图片

 在大致了解了 Redis 有哪些代码模块以后,我们把代码拷贝到 Linux 平台,然后编译并使用 GDB 调试器跑起来。如下图所示:感觉 C++ 很简单,但为何这么多劝退的?_第5张图片

 然后按 CTRL+C 将 GDB 中断下来,输入 info threads 查看当前程序的所有线程:

感觉 C++ 很简单,但为何这么多劝退的?_第6张图片

 接着挨个使用 thread + 线程编号 和 bt 命令去查看每个线程的上下文调用堆栈:

感觉 C++ 很简单,但为何这么多劝退的?_第7张图片

对照每个线程的上下文堆栈,搞清楚其逻辑,并结合主线程,看看每个线程是在何时启动的,端口在何时启动侦听的,等等。做完这一步,关于 redis-server 的框架也基本清楚了。

接着我们可以选择一个自己感兴趣的命令,搞清楚 redis-cliredis-server 命令的交互流程。

最后,如果对 redis-server 源码中各种数据结构和细节感兴趣,我们可以进一步深入到具体的代码细节。

当然,不熟悉 GDB 的读者看笔者这段操作流程比较困难,这是正常的,说明如果想通过调试去研究 Redis 这一款开源软件,您需要去补充一点 GDB 调试的知识。这就是我上文中所说的,针对性地补缺补差。

以下是《gdb 高级调试实战教程》目录:

感觉 C++ 很简单,但为何这么多劝退的?_第8张图片

 感觉 C++ 很简单,但为何这么多劝退的?_第9张图片

 我写 C++ 快 9 个年头了,目前人在某大厂做技术专家,感觉在 C++ 方面只是个熟练工,因为要学的东西太多了,我用一张图来概括下 C++ 技术栈,虚线框里面为 C++ 开发必备的技能。

感觉 C++ 很简单,但为何这么多劝退的?_第10张图片

 

看了上文,如果你还没被劝退,能坚持看到这里的同学必须给自己点个赞~

我是这么学习 C++ 的

算上本科 4 年,硕士 3 年,我接触 C++ 也算十五六年了,分享一下我的 C++ 打怪升级之路。

1. 第一阶段

无论你是科班还是非科班,建议你一定要学好 C 语言,它应该作为你必须掌握好的语言。你要熟悉 C 语言的基本语法,包括:

  • 顺序、条件、循环三大控制语句
  • C 中几大基元数据类型的用法
  • 熟悉掌握数组的用法
  • 熟练掌握指针的用法
  • 熟练掌握结构体、枚举、联合等数据类型的用法
  • 熟练使用常用 C 库函数,如控制台输入输出流、字符串操作、文件操作、时间函数等等

以上阶段算是启蒙阶段,在这个阶段,是基础编程语法的学习,当然,你不仅仅要掌握这些基本语法,你还要反复练习。

这个阶段可以推荐一本非常的图书《C 语言设计 现代方法》,这本书在国外是作为大学教材的,引进到国内翻译的也很棒。

感觉 C++ 很简单,但为何这么多劝退的?_第11张图片 

2. 第二阶段

如果你想毕业后进大厂,从这时开始学习算法和数据结构。学习算法和数据一定不能急功近利,算法和数据结构的知识一定要作为一个长期的学习目标。

对于非科班的同学,由于没有像科班同学那样接受到系统的学习,一定要找一两本经典书籍系统地学习下常见的算法理论、思想和常用的数据结构知识。等熟悉了这块的知识,再适当地刷一些算法题目或者做一些算法练习。

如果你觉得算法和数据结构很吃力,推荐一下《算法(第四版)》

感觉 C++ 很简单,但为何这么多劝退的?_第12张图片

 面试大厂之前要适当刷一些题目,推荐leetcode 和 《剑指 offer》。

3. 第三阶段

如果你是学生,想走 C++ 开发这条路,其实大学四年(研究生三年)说长不长,说短也不短。接下来就挨个补相应的基础知识了。如果你是非科班,也建议自己找点经典的书籍系统地学习一下。下面挨个说一下这几大方面的基础知识。

3.1 C++ 语言学习

如果想系统地学习 C++,请抛弃各种总结经验技巧的面经,逐个掌握 C++ 语言的各个语法,包括但不局限于:

  • 指针和引用的概念
  • 指针与内存关系
  • 程序编译过程,静态链接库和动态链接库
  • static、const、#define的用法和区别
  • C和C++区别
  • 内存中的栈和堆分配
  • 面向对象理解
  • 访问限定符 public/protected/private
  • 构造函数/析构函数/拷贝构造
  • 多态
  • 虚函数与纯虚函数、虚函数实现机制、虚函数表
  • 继承原理、虚继承、菱形继承
  • new/delete和malloc/free
  • 重载、重写和覆盖
  • 类型转换方式
  • RAII 与 pimpl 惯用法
  • 内存溢出和内存泄漏
  • STL标准模板库
  • 迭代器、空间配置器理解
  • 常用容器特点、用法以及底层实现vector、list、deque、set、map、unorderedmap

C++11/14/17 新标准也慢慢成为主流,这块也要熟悉,要熟悉新标准常用的语法与新功能,包括:另外,时至今日,你一定要熟悉 C++11/14/17 常用的语言特性和类库,这里简单地列一下:

  • 左值/右值/std::move/std::forward
  • 统一的类成员初始化语法与 std::initializer_list
  • 注解标签(attributes)
  • final/override/=default/=delete 语法
  • auto 关键字
  • Range-based 循环语法
  • 结构化绑定
  • stl 容器新增的实用方法
  • std::thread
  • 线程局部存储 thread_local
  • 线程同步原语 std::mutex、std::condition_variable 等
  • 原子操作类
  • 智能指针类
  • std::bind/std::function

C++11/14 网上的资料已经很多了,C++17 的资料不多,重点掌握的还是 C++11 引入的各种实用特性,这就给读者推荐一些我读过的书:

《深入理解 C++11:C++11 新特性解析与应用》 《深入应用 C++11:代码优化与工程级应用》 《C++17 完全指南》 《Cpp 17 in Detail》

感觉 C++ 很简单,但为何这么多劝退的?_第13张图片

3.2 学习 C++ 相关的开发工具链

学习 C++ 的相关的 IDE 开发环境,Windows上我推荐 Visual Studio,Mac 上可以使用Subline 或者VSCode,如果你最终的程序需要在 Linux 跑,你需要熟悉 cmake/make/gcc/g++/gdb 工具链,vim 的基本操作也要熟悉,但绝对不要直接在 vim 中写代码,纯属浪费时间,实际企业级开发也没人会这么做。这里强调一下,如果可能,至少要熟悉 Visual Studio 和 gdb 调试,调试需要掌握哪些内容呢?

建议掌握:

  • 如何启动和结束调试
  • 如何添加/删除/启用/禁用断点(包括普通断点、条件断点和数据断点)
  • 如何查看当天断点下的调用对战
  • 如何查看程序运行过程中的线程信息(这块可以放到下文再学)
  • 如何查看某个变量的内存值

掌握了一门编程语言加其开发的工具链,你的想法就可以通过动手变为现实了。熟悉了 C/C++ 语言和其相关的开发工具链,接下来你可以根据你的兴趣学习相关的开发知识了。

3.3 学习操作系统的 API 接口

很多人说,操作系统的 API 接口不用刻意学习,根据我个人的经验,我反对这一观点,操作系统的很多 API 涉及到很多操作系统原理和使用技巧,绝非是想用的时候去查一查就可以了。就和开卷考试一样,如果不熟悉,且不说不知道如何查、在哪查,就算查到了,是否可以用好又是另外一回事了。

Linux 系统推荐学习《Linux 系统编程》,Windows 系统推荐《Windows 程序设计(第五版)》和 《Windows 核心编程》。

学习操作系统的接口不仅是学习相关操作系统 API,同时也是在培养自己的动手和实践能力。常用的操作系统 API 并不多,以多线程相关的为例,Windows 系统提供的线程同步原语常用的有临界区、Event、互斥体、信号量等,Linux 常用的有互斥体、读写锁、信号量和条件变量,掌握这些并不困难,你只要花一周时间挨个地学习联系一下,很快就能掌握。

熟悉了常用操作系统的 API 之后,你就可以自由地写出自己想要的程序了,这个时候处于初级的通透状态,例如,你可以随手写出这样一个功能的程序:

主线程创建一个新的工作线程,等待工作线程获取系统时间后写入文件;主线程从文件中读取时间内容显示出来。

这个例子中,我们用到了创建线程的 API、线程等待与通知 API、获取系统时间的 API、显示到控制台的 API

在 Windows 上,我们用到:

  1. CreateThread
  2. WaitForSingelObject
  3. GetSystemTime
  4. CreateEvent/SetEvent
  5. std::cout

在 Linux 上,我们可以使用:

  1. pthread_create
  2. pthread_mutex_init/pthread_cond_init/pthread_cond_destroy/pthread_cond_wait
  3. time
  4. std::cout

这么一分析是不是觉得一下子清晰起来,因为 C/C++ 这么编程语言不是功能完备性的,如果 C/C++ 的 C 库或者 stl 本身没有提供这些功能,你不得不使用操作系统的 API。

如果你掌握到这个阶段,恭喜你,你已经可以去胜任中小企业的 C/C++ 开发了。所以,我推荐的这条路线,如果你认真学习,保底能让你找到一份小公司的 C++ 开发工作的。

3.4 学习多线程编程

这块与上文有一点重叠,我们再次说一下。多线程知识,你需要掌握理解线程与进程的关系、熟练使用常用的线程同步技术。推荐的一种学习方式,就是找一个开源项目,使用调试器跑起来,然后看看这个进程有多少线程,每个线程在何时被创建,每个线程的作用是什么,线程之间如何通信的。这也是上文建议你熟练掌握调试器的原因。

3.5 学习操作系统原理

操作系统原理无论是面试还是自我提高的五大基础之一,我的建议学习操作系统知识时,不一定要看完所有操作系统书籍,但一定将一些基础概念(如进程、线程、内存模式等)看懂、理清。Tanenbaum.A.S《现代操作系统》是一本讲解操作系统理论不错的书,作者 Tanenbaum.A.S 是 Linux 内核创始人 Linus Torvalds 的老师。

你如果还有时间强烈推荐看看俞甲子的《程序员的自我修养:链接、装载与库》。这本书同时涉及到了 Windows 和 Linux 两个操作系统平台,用各种辅助工具剖析了程序从源码到二进制文件再到装载到进程地址空间里面的各个细节,甚至连进程地址空间中的堆结构、栈结构也分析得清清楚楚,同时也分析了 C Runtime(CRT)、glibc 这样的操作系统接口库的原理和执行逻辑,是一本实实在在帮你实战操作系统原理的好书。

当然,学有余力的同学,可以进一步了解一些关于操作系统的模式(如实模式、保护模式)、系统的启动与初始化、虚拟内存与物理内存、内存分表分页机制、进程与线程的调度算法等知识。

3.6 学习计算机网络和 Socket 编程

学习计算机网络要从以下三个方面学习:

3.6.1 计算机网络理论知识

计算机网络编程你需要掌握基础的如三次握手和四次挥手的过程以及各个状态值,我建议使用 tcpdump 命令实际抓下包就一目了然了,然后就是网络分层,各层的用途,重点熟悉下 TCP/IP 层相关的知识,还有就是 TCP/UDP 的区别,TCP 的滑动窗口机制、拥塞控制算法、TCP 的保序、重传、确认机制。

学习这些知识的时候,一定不要死记硬背,注重理解。我近来面试了一部分学历学校非常好的同学,然而,在问到这块的知识时却大失所望。例如,有的同学只是单纯把三次握手背下来了,我稍微变通一下他就不知道怎么回答了:

  1. 如果连接一个目标主机不存在的 IP 地址握手过程是怎样的?连接一个目标 IP 存在但是端口号不存在的主机又是怎样的握手过程呢?
  2. A 机器上的进程与 B 机器上的进程进行网络通信,分别经历了哪些网络层。

3.6.2 学习 Socket 编程

Socket 编程你需要先掌握常用的 Socket API,包括但不局限于:

感觉 C++ 很简单,但为何这么多劝退的?_第14张图片

学习这些 Socket API 的时候,不是让你单纯地记忆这些函数的参数,而是掌握每一个函数的重难点。

例如:

  1. 如何将一个 socket 设置成非阻塞模式
  2. 阻塞模式下,send 和 recv 函数行为是什么样子的?非阻塞模式下 send/recv 的返回值分别是什么?
  3. 客户端发起连接时,如何主动指定通过本地某个端口号去连接?bind 函数如果端口号设置为 0 是什么行为?
  4. listen 函数的 backlog 参数用途是什么?
  5. 如何实现异步的 connect 函数?
  6. accept 函数调用时,三次握手是否已经完成?
  7. 如何实现半关闭状态?
  8. nagle 算法的用途是什么?
  9. select 函数的第一个参数怎么设置?select 函数的超时参数如果设置为 NULL 是什么行为?

需要重点学习下常用的网络模型:

  1. Windows 上常用的网络模型有 select、WSAEventSelect、WSAAsyncSelect、完成端口模型;
  2. Linux 上常用的网络模型 select、poll、epoll,epoll 需要重点关注的是水平模式和边缘模式。

当然,也建议一定要理解,不要死记硬背。C++ 的同学来面试的时候,我会给他们准备如下面试题:

  1. epoll 边缘模式下,某次读取了某个 socket 上的部分数据,下次是否会出发读事件?如果此时又来了一个字节的新数据,是否会触发读事件?
  2. epoll 边缘模式建议尽量一次把数据读完,怎样判断当前数据已经读完?
  3. epoll 边缘模式下,对于写事件应该如何处理?

接着还要熟悉 TCP 协议的流式特性,如何解决粘包问题;还要掌握常见的网络协议格式,像 HTTP、FTP、POP3/SMTP/WebSocket协议的格式都建议熟练掌握。

以 HTTP 协议为例,HTTP 协议包的格式是什么样的,包头和包体如何分界的,GET 与 POST 请求的数据分别放在 HTTP 包的什么位置,如果放在包体中,如何知道包体的数据有多长。

3.6.3 学习常用网络命令

学习了常用的网络命令,可以用来排查网络故障与定位问题,反过来,也可以加深对网络理论知识的理解,建议掌握以下命令:ifconfig、ping、telnet、netstat、lsof、nc、curl、tcpdump。

掌握了这些命令要做到学以致用,例如现在某个服务器连接不上,如何使用这些命令判断是自己网络的问题还是目标主机的问题;开发了一个服务器程序,手头上没有可用的客户端,如何使用 nc 命令模拟一个;或者反过来,开发了一个客户端程序,如果用 nc 模拟一个服务器端用于测试。

推荐《TCP/IP 网络编程》和《Linux 高性能服务器编程

3.7 学习数据库

数据库需要掌握的基础知识有:

  1. 熟悉基本 SQL 操作 包括增删改查(insert、delete、update、select语句),排序 order,条件查询(where 子语句),限制查询结果数量(LIMIT语句)等
  2. 稍微高级一点的 SQL 操作(如 Group by,in,join,left join,多表联合查询,别名的使用,select 子语句等)
  3. 索引的概念、索引的原理、索引的创建技巧
  4. 数据库本身的操作,建库建表,数据的导入导出
  5. 数据库用户权限控制(权限机制)
  6. MySQL 的两种数据库引擎的区别
  7. SQL 优化技巧

以上属于对开发的基本的数据库知识要求,你可以找一本相关入门级的数据库图书,我推荐一下《《MySQL技术内幕(第5版)》。

高级开发除了以上要求还要熟悉高可用 MySQL、主从同步、读写分离、分表分库等技术,这些技术的细节一定要清楚,它们是你成为技术专家或者高级架构的必备知识。我们在实际面试时,在讨论高可用服务服务方案时,很多面试者也会和我们讨论到这些技术,但是不少面试者只知道这些技术的大致思想,细节往往说不清楚,细节不会就意味着你的高可用方案无法落地,企业需要可以落地的方案。

这些技术我首推《高性能 MySQL》这本书,这本书高级开发者一定要通读的,另外还有 2 本非常好的图书也推荐一下:一本是《MySQL 排错指南》,读完这本书以后,你会对整个“数据库世界”充满了清晰的认识;另外一本是《数据库索引设计与优化》,这本书读起来非常舒服,尤其是对于喜欢算法和数据结构的同学来说。

3.8 学习汇编

如果你熟练掌握汇编,你就比其他人多很多优势,你会能透彻地知道你写的每一行 C/C++ 代码背后的机器指令的效率。无论是做安全工程还是自己技术提升上都是非常不错的。这里推荐一本王爽老师的《汇编语言(第 3 版)》,这本书不厚,语言通俗易懂,你也不用刻意去记忆,基本上当小说书看一下就能很快看完了。汇编实战类图书还有另外一本《老码识途:从机器码到框架的系统观逆向修炼之路》。我个人是非常喜欢这本书的。当年读这本书的时候,真的有一种“笑看妻子愁何在?漫卷诗书喜欲狂”的感觉。

3.9 学习代码规范,培养良好代码风格

在你学习的过程中,请一定要认真对待自己每一个变量名、函数名,养成良好的代码习惯。我学生时代花了大量时间去学习一些教人写出优美风格的代码书籍、资料、源码,在你还是个小白的时候,要认真精读一些优秀代码,不仅要学习它们的整体设计思路,还要学习它们的代码风格和细节。这里推荐《程序设计实践》《代码整洁之道》这两本书,特别是《程序设计实践》,强烈建议学生朋友看一下,能大幅度地提高你实际编码的技巧和编码风格。

高级部分

接下来是高级部分,高级部分已经与具体的编程语言无关了,这些技术需要长期的专研和经验积累。

高可用与容灾容错

服务都是人开发的,既然是人开发的,必然有宕机的可能性,当然宕机的原因可能是程序自身 bug,也可能是物理故障(断电、磁盘损坏等等),作为开发人员,针对不同的业务场景,我们没法做到服务 100% 可能,至少让其尽量可用,八仙过海各显神通。在宕机时如何尽量不影响业务、如何尽量快速恢复、如何保证数据和业务状态不丢失等等。这当然有一定的固定套路,例如主从、主备,当然固定的套路不是万能的,尤其是对于一些有状态要求的服务,这需要不断的磨练与自我总结。

分布式

分布式你需要掌握基本的分布式理论和原理,常见的分布式算法,然后是分布式系统设计的初衷和技巧,在实际并发量高的业务中,如何利用分布式解决高可用和访问效率问题。

RPC

很多人都听说过这个词,在面试时也可以说出来个大概,但是当问到 RPC 技术解决的核心问题是什么就说不清楚了。当然,学习 RPC,我们还要考虑协议的设计(协议格式、序列化与反序列化、兼容性问题)、网络连接的重试与反馈、接口 stub 的设计等等。

消息中间件

目前除了自己公司自研的消息中间件,主流的有 Kafka、RabbitMQ、RocketMQ,如果想学习,建议选择其中一种深入学习一下,要掌握消息中间件的用途、选举策略、保序策略、重试策略、高可用策略等。

缓存

缓存的设计是一个很大的方面,个人觉得与其说这是一种设计思想而非单纯的某个缓存服务。当然,老生常谈的有缓存雪崩、缓存穿透、缓存击穿的解决思路。当然,以缓存为代表的服务是 Redis,Redis 的常用数据类型、适用场景、持久化、主从复制、哨兵与集群,这些建议你掌握,如果你从来没机会吃猪肉,那就看看猪跑吧,一些技术书籍和项目案例都有 Redis 的用途说明。

数据库高级知识

包括 SQL 调优、数据库调优、分表分库、主从同步等等。

成为研发专家不是一朝一夕的事情,既要相关工作经验的积累,也需要个人勤奋的努力和不断总结,才能达到融会贯通阶段

希望各位小可爱可以多多支持哟~

小编会为大家奉上更多有趣的小知识的!可以关注一下呀~

如果有想学习C语言C++的小伙伴可以来UP的交流群: 【828339809】

你可能感兴趣的:(计算机/it,C语言,编程语言,c++,大数据,网络,开发语言,c语言)