记应聘:浙江核新同花顺网络信息股份有限公司 iOS/C软件开发岗位

记在前面

这个公司是APP推荐的,额,试一试。该公司信息。信息
明天下午约技术面,电话面试。加油,结束再来写心得。

电话面

1.我竟然忘记了准备自己介绍,一上来就是自己介绍。忘了准备。
2.简单介绍一下自己在大学里面做过的项目,介绍了一下开发的扫雷游戏,和IPv4的报文重组。遇到了哪些问题,又是怎么解决的?介绍了IPv4报文重组遇到的问题,链表的头结点需要单独的留出来,以及最后一个节点的属性值的设置。
3.内存的分区:有栈区,全局区(存放全局变量,常量等。),堆区(自由空间,用来存储动态分配的对象。)。
4.虚函数是什么?
5.野指针是什么?(会,但回答的不全)(指针变量未初始化、指针释放后之后未置空、指针操作超越变量作用域)如何避免?(我只回答了常规的手动回收的方法,)
6.C语言从源码到可执行文件的过程:预处理,编译(生成汇编代码),汇编(生成机器语言代码 .o),链接(.exe)。
7.引用和指针的区别:区别
(1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。
(2)不能有 NULL 引用,引用必须与合法的存储单元关联(指针则可以是 NULL)。
(3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。

第4、5两个问题回答的不好。关于C++的知识有了一些遗忘。今天,这里把C++里面,关于类的知识,以及智能指针的知识做一些简单的复习记录。

智能指针

有shared_ptr, unique_ptr, weak_ptr三种。

shared_ptr类
#include 
#include 
using namespace std;

int main() {
     
	shared_ptr<string> p1;   //shared_ptr指向string类型
	//list是C++标准模版库(STL,Standard Template Library)中的部分内容。
	//实际上,list容器就是一个双向链表,可以高效地进行插入删除元素。
	//使用list容器之前必须加上STL的list容器的头文件:#include;
	shared_ptr<list<int>> p2;  //指向int的list

	//如果p1不为空,检查它是否指向一个空string
	if (p1 && p1->empty()) *p1 = "hi";  
	//如果两个条件都满足,说明它是一个空串,这时就给p1赋值。
	return 0;
}

记应聘:浙江核新同花顺网络信息股份有限公司 iOS/C软件开发岗位_第1张图片这个表格给出了一些函数,下面接着描述一下shared_ptr等的特殊之处。
最安全的分配和使用动态内存的方法是调用一个名为make_shared的标准库函数,此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr.
具体的使用形式:

    shared_ptr<int>p3 = make_shared<int>(42);
	//p3指向一个值为42的int
	shared_ptr<string>p4 = make_shared<string>(10, 9);
	//p4指向一个值为"9999999999"的string
	shared_ptr<int>p5 = make_shared<int>();
	//p5指向一个值为0的int
	auto p6 = make_shared<vector<string>>();
	//auto自动判断类型,p6指向一个动态分配的空vector
	
    //接下来就是特殊的地方了,每个shared_ptr都会
	//记录有多少个其他shared_ptr指向相同的对象。
	auto p = make_shared<int>(42);  //p指向的对象只有p一个引用者
	auto q(p);  //p和q指向相同对象,此对象有两个引用者。
	//我们可以认为每个shared_ptr都有一个关联的计数器,通常称其为引用计数。
	//拷贝时,计数器都会增加。
	//例如,当用一个shared_ptr初始化另一个shared_ptr时,
	//或将它做为参数传递给一个函数时,
	//或作为函数的返回值时。

	//当我们给shared_ptr赋予一个新值或是shared_ptr被销毁,计数器就会递减。
	//计数器就会递减。
	//一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象。
	auto r = make_shared<int>(42);
	r = q;//给r赋值,令它指向另一个地址,
	      //递增q指向的对象的引用计数,
	      //递减r原来指向的对象的引用计数
	      //r原来指向的对象已经没有引用者,会自动释放。

	//程序使用动态内存出于以下三种原因之一:
	//1.程序不知道自己需要使用多少对象。
	//2.程序不知道所需对象的准确类型。
	//3.程序需要在多个对象之间共享数据。

	shared_ptr<int> p1(new int(1024));  //必须使用直接初始化的形式。
	//使用一个内置指针来访问一个智能指针所负责的对象是很危险的,
	//因为我们无法知道对象何时会被销毁。
unique_ptr

与shared_ptr不同,某个时刻只能有一个unique_ptr指向一个给定对象。即一个unique_ptr“拥有”它所指向的对象。

    //与shared_ptr不同,没有一个类似make_shared的标准库函数返回一个unique_ptr.
	//当我们定义一个unique_ptr时,需要将其绑定到一个new返回的指针上。
	//初始化unique_ptr,必须采用直接初始化的形式:
	unique_ptr<double>p1; //可以指向一个double的unique_ptr
	unique_ptr<int>p2(new int(42));  //p2指向一个值为42的int
	//由于unique_ptr拥有它的对象,因此不支持普通的拷贝或赋值。
	unique_ptr<string>p1(new string("hello"));
	//unique_ptrp2(p1); 这是错误的,不支持拷贝
	unique_ptr<string>p3;
	//p3 = p1; 这也是错误的,不支持赋值。
	
	//那么,怎么赋值呢?
	unique_ptr<string>p1(new string("hello"));
	unique_ptr<string>p2(p1.release());  
	//release()函数的作用:返回unique_ptr当前保存的指针并将其置为空。
	//结果是p2被初始化为p1原来保存的指针,p1被置为空。

	unique_ptr<string>p3(new string("world!"));
	p2.reset(p3.release());
	//现在p2获得的是p3的值,p3被置为空。
//当然,这里也有例外:我们可以拷贝或赋值一个将要被销毁的unique_ptr.
//最常见的例子是从函数返回一个unique_ptr.
unique_ptr<int> clone(int p) {
     
	return unique_ptr<int>(new int(p));
}
unique_ptr<int> clone2(int p) {
     
	unique_ptr<int> ret(new int(p));
	return ret;
}

记应聘:浙江核新同花顺网络信息股份有限公司 iOS/C软件开发岗位_第2张图片

weak_ptr

它指向由一个shared_ptr管理的对象。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放,即使用weak_ptr指向对象,对象也还是会被释放。记应聘:浙江核新同花顺网络信息股份有限公司 iOS/C软件开发岗位_第3张图片

    //当我们创建一个weak_ptr时,要用一个shared_ptr来初始化它:
	auto p = make_shared<int>(42);
	weak_ptr<int> wp(p);
	//wp弱共享p,p的引用计数未改变。
	//这里wp和p指向相同的对象,由于是弱共享,wp指向的对象可能被释放掉。
	//由于可能不存在,所以不能使用weak_ptr直接访问对象,而必须调用lock.
	//来检查weak+ptr指向的对象是否仍存在。
	//如果存在,lock返回一个指向共享对象的shared_ptr.
	//与其他的sahred_ptr类似,只要此shared_ptr存在,它所指向的底层对象就一定会存在。
	if (shared_ptr<int> np = wp.lock()) {
     
		//如果np不为空,则条件成立。
		//在if中,使用np访问共享对象是安全的。
	}

虚函数

\qquad 这个知识和C++面向对象的知识有关。在C++中,通过继承关系联系在一起的类构成一种层次关系。通常在层次关系的根部有一个基类,其他类则直接或间接地从基类继承而来,这些继承得到的类称为派生类。基类负责定义在层次关系中所有类共同拥有的成员,而每个派生类定义各自特有的成员。
\qquad 在C++语言中,基类将类型相关的函数和派生类不做改变直接继承的函数区分对待。对于某些函数,基类希望它的派生类各自定义适合自身的版本,此时基类就将这些函数声明成虚函数。定义时在前面加上 virtual 关键字。

电话面的知识就是这么多,希望好运!
一面过了,接下来是二面。

二面,在线笔试

1.使用过哪些开源库?并简述其原理。(基本没用过,就很惨。只写了个STL.)
2.如果想知道通过new申请了多少内存。可以怎么做?(不会,待查。)
(new[] new new()三者不同? 待查)
vector是存放在哪里的?堆区
3.拿到同事一个接口的文档中,写着是数组,实际调试得到的是字典,怎么办?(直接不会,已发牛客网,希望广大的网友们可以解答。)
4.什么是内存泄漏?会有哪些后果?如何避免?
答案:内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
\quad ①常发性内存泄漏:发生内存泄漏的代码会被多次执行到,每次被执行时都会导致一块内存泄漏。
\quad ②偶发性内存泄漏:发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
\quad ③一次性内存泄漏:发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块且仅有一块内存发生泄漏。
\quad ④隐式内存泄漏:程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到。
\quad 良好的代码书写习惯,准确释放申请的空间。
5.在浏览器输入网址之后发生了什么?全部的过程以及设计到的协议。
6.排序。
7.空间换时间。
8.字符串逆序。
9.N步回家,每次走1或2步,有多少种方法。
(今天查了下,竟然有递归的方法,好简单,第二个中间变量的方法也不难,那天也是疯了,这不不写!)

#include 
using namespace std;
//题目:现在需要走N步回家,每次走1步或2步,问总共有多少种方法。
//第一种方法:递归。
//递归的思想很简单,第N步,转换到N-1步,或者是N-2步,两种选择。
int cal(int n) {
     
	if (n == 1) return n;
	else if (n == 2) return n;
	else {
      return cal(n - 1) + cal(n - 2); }  
	//这就是普通的递归,但是仍然会面临递归层次过多的问题。
}
int main() {
     
	int N = 0;
	cin >> N;
	if (N <= 0) {
      return - 1; }
	else {
      cout << cal(N); }
	return 0;
}

//第二种方法,使用中间变量。
//这种方法,使用中间变量的方法,个人觉得也是递归转为非递归常用的方法。
int main() {
     
	int N = 0;
	cin >> N;
	int one = 1;//相当于记录n-2
	int two = 2;//相当于记录n-1
	int out = 0;
	if (N <= 0) {
      return -1; }
	else
	{
     
		for (int i = 2; i < N; i++) {
     
			out = one + two;
			one = two;  //更新f(n-2)(小的那个)
			two = out;  //更新f(n-1)
		}
		cout << out;
	}
	return 0;
}

10.引用作为返回值的好处以及需要遵守的规则。(不会,待查。)
首先是函数返回值的知识:
\qquad 被调函数运行结束后才会返回主调函数,但是被调函数运行结束后系统为被调函数中的局部变量分配的内存空间就会被释放。也就是说,return 返回的那个值在被调函数运行一结束就被释放掉了,那么它是怎么返回给主调函数的呢?
  事实上在执行 return 语句时系统是在内部自动创建了一个临时变量,然后将 return 要返回的那个值赋给这个临时变量。所以当被调函数运行结束后 return 后面的返回值就被释放掉了,最后是通过这个临时变量将值返回给主调函数的。而且定义函数时指定的返回值类型实际上指定的就是这个临时变量的类型。
  这也是为什么当 return 语句中表达式的类型和函数返回值类型不一致时,将 return 的类型转换成函数返回值类型的原因。return 语句实际上就是将其后的值赋给临时变量,所以它要以临时变量的类型为准,即函数返回值的类型。
好处:在内存中不产生被返回值的副本。
需要遵守的规则:①在内存中不产生被返回值的副本,因此不能返回局部变量的引用(因为随着函数返回,局部变量会被销毁)。
②不能返回函数内部new 分配的内存的引用。虽然不存在局部变量的被动销毁问题,可对于这种情况
又面临其它尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。
③可以返回类成员的引用,但最好是const。主要原因是当对象的属性是与某种业务规则(business
rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯
赋值就会破坏业务规则的完整性。(并不懂。)

今天下午最后一个技术面,加油!

三面,技术面(视频)

今天面的C++部分的知识回答的不好,感觉脑子是个假的。
1.#define和const的区别。
(1)就起作用的阶段而言:#define是在编译的预处理阶段起作用,而const是在编译、运行的时候起作用。
(2)就起作用的方式而言:#define只是简单的字符串替换,没有类型检查。而const有对应的数据类型,是要进行判断的,可以避免一些低级的错误。
(3)就存储方式而言:#define只是进行展开,有多少地方使用,就替换多少次,它定义的宏常量在内存中有若干个备份;const定义的只读变量在程序运行过程中只有一份备份。
(4)从代码调试的方便程度而言:const常量可以进行调试的,#define是不能进行调试的,因为在预编译阶段就已经替换掉了。
(5)从是否可以再定义的角度而言:const不能重定义,而#define可以通过#undef取消某个符号的定义,再重新定义。
(6)从用于类中来看:const可用于类成员变量的定义。#define不可用于类成员变量的定义,但是可以用于全局变量。
(7)const采用一个普通的常量名称,#define可以采用表达式作为名称。
2.static的作用。
①.隐藏:static可以用作函数和变量的前面可表示隐藏。对于函数来讲,static的作用仅限于隐藏。
②.周期不同:存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围。如果作为static局部变量在函数内定义,它的生存期为整个源程序,但是其作用域仍与局部变量相同,只能在定义该变量的函数内使用该变量。退出该函数后, 尽管该变量还继续存在,但不能使用它。
③.自动初始化为0:其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加‘\0’;太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是‘\0’。
在类中声明static变量或者函数时,初始化时使用作用域运算符来标明它所属类,因此,静态数据成员是类的成员,而不是对象的成员,这样就出现以下作用:
(1)类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致了它仅能访问类的静态数据和静态成员函数。
(2)不能将静态成员函数定义为虚函数。
(3)由于静态成员声明于类中,操作于其外,所以对其取地址操作,就多少有些特殊,变量地址是指向其数据类型的指针 ,函数地址类型是一个“nonmember函数指针”。
(4)static并没有增加程序的时空开销,相反她还缩短了子类对父类静态成员的访问时间,节省了子类的内存空间。
(5)静态数据成员是静态存储的,所以必须对它进行初始化。
(6)静态成员初始化与一般数据成员初始化不同:
\quad 初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆;
\quad 初始化时不加该成员的访问权限控制符private,public等;
\quad 初始化时使用作用域运算符来标明它所属类;
\quad <数据类型><类名>::<静态数据成员名>=<值>
3.inline的作用。
\quad 在C/C++中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了inline修饰符,表示为内联函数
\quad inline的使用是有所限制的,inline只适合函数体内代码简单的函数使用,不能包含复杂的结构控制语句例如while、switch,并且不能内联函数本身不能是递归函数。
\quad inline仅是一个对编译器的建议。
\quad 建议将inline函数的定义放在头文件中。因为内联函数要在调用点展开,所以编译器必须随处可见内联函数的定义,要不然就成了非内联函数的调用了。所以,这要求每个调用了内联函数的文件都出现了该内联函数的定义。因此,将内联函数的定义放在头文件里实现是合适的。
\quad 在类中,应该这样定义:

// 头文件
class A
{
     
    public:
    void Foo(int x, int y);
}
// 定义文件
inline void A::Foo(int x, int y){
     }   //在类外显示的定义为inline,否则视为放弃内联。 
void Foo(int x, int y);
inline void Foo(int x, int y) {
     } // inline 与函数定义体放在一起,才会是内联的。

4.友元。
\quad 友元提供了不同类的成员函数之间、类的成员函数与一般函数之间进行数据共享的机制。通过友元,一个不同函数或另一个类中的成员函数可以访问类中的私有成员和保护成员。友元的正确使用能提高程序的运行效率,但同时也破坏了类的封装性和数据的隐藏性,导致程序可维护性变差。
\quad 友元函数 :友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字friend,其格式如下: friend 类型 函数名(形式参数);
\quad 友元类 : 友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员)。当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一类的友元类。定义友元类的语句格式如下: friend class 类名; (其中:friend和class是关键字,类名必须是程序中的一个已定义过的类。)
以下示例代码转载别人的:

#include 
using namespace std;
class TV
{
     
public:
    friend class Tele;   //Tele是友元类。在Tele里面用TV的成员函数和成员变量。
    TV() :on_off(off), volume(20), channel(3), mode(tv) {
     }  //默认构造函数
private:
    enum {
      on, off };
    enum {
      tv, av };
    enum {
      minve, maxve = 100 };
    enum {
      mincl, maxcl = 60 };
    bool on_off;
    int  volume;
    int channel;
    int mode;
};
class Tele
{
     
public:
    void OnOFF(TV& t) {
      t.on_off = (t.on_off == t.on) ? t.off : t.on; }
    void SetMode(TV& t) {
      t.mode = (t.mode == t.tv) ? t.av : t.tv; }
    bool VolumeUp(TV& t);
    bool VolumeDown(TV& t);
    bool ChannelUp(TV& t);
    bool ChannelDown(TV& t);
    void show(TV& t)const;
};
bool Tele::VolumeUp(TV& t)
{
     
    if (t.volume < t.maxve)
    {
     
        t.volume++;
        return true;
    }
    else
    {
     
        return false;
    }
}
bool Tele::VolumeDown(TV& t)
{
     
    if (t.volume > t.minve)
    {
     
        t.volume--;
        return true;
    }
    else
    {
     
        return false;
    }
}
bool Tele::ChannelUp(TV& t)
{
     
    if (t.channel < t.maxcl)
    {
     
        t.channel++;
        return true;
    }
    else
    {
     
        return false;
    }
}
bool Tele::ChannelDown(TV& t)
{
     
    if (t.channel > t.mincl)
    {
     
        t.channel--;
        return true;
    }
    else
    {
     
        return false;
    }
}
void Tele::show(TV& t)const
{
     
    if (t.on_off == t.on)
    {
     
        cout << "电视现在" << (t.on_off == t.on ? "开启" : "关闭") << endl;
        cout << "音量大小为:" << t.volume << endl;
        cout << "信号接收模式为:" << (t.mode == t.av ? "AV" : "TV") << endl;
        cout << "频道为:" << t.channel << endl;
    }
    else
    {
     
        cout << "电视现在" << (t.on_off == t.on ? "开启" : "关闭") << endl;
    }
}
int main()
{
     
    Tele t1;
    TV t2;
    t1.show(t2);
    t1.OnOFF(t2);
    t1.show(t2);
    cout << "调大声音" << endl;
    t1.VolumeUp(t2);
    cout << "频道+1" << endl;
    t1.ChannelUp(t2);
    cout << "转换模式" << endl;
    //t1.SetMode(t2);
    t1.show(t2);
    return 0;
}

5.一个好的代码应该有哪些东西。
基本的两点:
1)、代码命名规范:
①、package 包名全部由小写的ASCII字母组成,用“.”分隔。
②、class 类名应当是名词,每个内部单词的头一个字母大写。应当使你的类名简单和具有说明性。用完整的英语单词或约定俗成的简写命名类名。
【示例】public class UserManager
③、interface 接口名应当是名词,每个内部单词的头一个字母大写。应当使你的接口名简单和具有说明性。用完整的英语单词或约定俗成的简写命名接口名。
【示例】interface TicketManagement
④、Class 成员属性及变量的命名。 变量名全部由字母组成,头一个字母小写,以后每个内部单词的头一个字母大写。变量名应该短而有意义。变量名的选择应该易于记忆。一个字符的变量名应避免,除非用于临时变量。通常临时变量名的命名规则为:i,j,k,m,n用于整数;c,d,e用于字符。
⑤、数组的命名,数组应该总是用下面的形式来命名:byte[] buffer;
⑥、方法的参数和变量的命名规范一致,且应使用有意义的参数命名,如果可能的话,使用和要赋值的字段一样的名字。
【示例】setCounter(int size){ this.size = size; }
⑦、方法命名。方法的命名应当使用动词,头一个字母小写,以后每个内部单词的头一个字母大写。在方法名的选择上应意义明确便于记忆。对于属性的存取方法,应使用getXXX()和setXXX()名称,以isXXX(),hasXXX()来命名返回值为boolean 类型的方法。
2)、代码逻辑规范:
①、需求、设计中的重点功能(结合需求/设计的评审产出)。
②、代码格式校验:
需要存入数据库的数据字段是否有长度校验
③、分支/循环
if-else/switch是否处理了所有分支
分支的条件语句是否有“副作用”;即,条件语句是否会改变系统状态/数据等
循环边界是否覆盖了所有元素、是否有死循环等
④、异常处理
是否有“吃掉异常”的情况、是否记录了异常日志、如果二次抛出,是否有合理的异常层次/结构
如果内部处理,对异常的处理是否能保证后续代码正常运行
⑤、单元测试
是否有单元测试、单元测试是否自动化、单元测试是否能完整覆盖需求
⑥、事务处理(并不是很懂)
事务范围是否合理;或者说,是否把不必要的操作放到了同一个事务中
事务传播方式是否合理(required,never,new等配置)
⑦、sql语句
sql语句是否正确、使用mybatis的动态语句时,是否有潜在的sql语法问题
⑧、第三方组件
使用Redis,RabbitMQ等组件,是否真的对组件完全了解,在使用的过程中是否正确执行了开启与关闭操作。
6.队列和栈在实际生活中的应用。
7.定义一个口罩类。
8.输入一个网址,发生了什么?(面试常考题)
\quad 首先是DNS解析,在浏览器缓存,本机缓存,路由器缓存等查找对应的IP地址,如果找不到,就去DNS服务器查找,这里的查找又分递归和迭代。
\quad 在得到IP地址之后,开始建立连接,在传输层使用TCP或者是UDP。TCP需要三次握手。再每一次的传输中都需要更下层的数据链路层和物理层帮忙传输。
\quad 浏览器发送HTTP请求,服务器处理请求,服务器发回一个HTML响应。浏览器开始显示HTML,浏览器发送获取嵌入在HTML中的对象的请求。浏览器还需要发送异步AJAX请求,处理动态内容等。(不是很清楚)
\quad 当浏览器需要的全部数据都已经加载完毕,一个页面就显示完了。
\quad 当数据完成请求到返回的过程之后,根据Connection的Keep-Alive属性可以选择是否断开TCP连接,HTTP 1.1一般支持同一个TCP多个请求,而不是1.0版本下的完成一次请求就发生断开。TCP的断开与连接不一样,需要经过4次挥手。
9.有多少代码量。

好啦,知识点就是这么多了。希望顺利。

记在后面

三面过了一星期了,没回应。应该是凉了。。后面有回应,评论区里加。还有二面第2道题有知道的,可以评论区里回复我。

你可能感兴趣的:(大四求职,指针,面试,编程语言,c++)