z*********@p*********.c**这人是的邮箱可以显示完吗?steam号被盗了

&p&我是先自学C后来再C++,刚开始学习的时候只知道自己爱编程,但是对于到底要去编什么程序并没有概念。大概在2007年左右,无意间在网上看了2001年的高分科幻电影《Artificial Intelligence》,才决定转服务器开发。电影里面人类灭亡了,我想如果我要做一个程序能够运行到世界末日,这个程序应该是一个服务器程序,因为服务器需要一直运行。这决定的出发点是感性的,但是我还是理性地去考虑过,服务器本身需要稳定高效,如何实现稳定高效就充满了挑战,而挑战也是正是我想要去追求的。&/p&&p&然后我学习linux环境编程,加入了鹅厂,从此成为一名后台开发。这些年对于用C/C++做服务器开发积累了一些心得。我觉得,对于高效,C、C++都能做到,但是对于稳定,C++比C要容易得多。&/p&&p&关于C/C++,我最想吐槽的就是:void*是万恶之首。因为它有指鹿为马的强大威力,所以可能让人无法自拔。任何void*最初的类型都需要靠程序员人肉掌控,代码写多了,就总有出错的时候,后果就是轻则程序崩溃,重则是指鹿为马无限久还不知道鹿已是马,面目全非了。&/p&&p&void*在C里面无法被取代,但是在C++里面基本上是可以不用的。首先,C++类的继承关系,使得子类和父类的指针在转换时不需要用void*。子类指针到父类指针无需人肉转换,父类指针到子类指针用dynamic_cast,这个是运行时安全的。其次,C++模板使得在通用算法上的void*也变得没有必要了。&/p&&p&在我们现在的代码里面,只有我老大2008年写的一个200行左右的mempool里面用了void*,实际上这里面的void*也是可以用模板改掉的,但是一来是因为它已经很稳定无需增加新功能了,二来是因为我老大现在已经不写代码了,这个文件是仅存的有他署名的代码,所以我一直没改掉。&/p&&p&总之呢,C++比C最大的改进之一就是增加了很多类型安全的功能,在编译(或者运行时如dynamic_cast)时避免了指鹿为马事件的发生,程序运行起来更加的安全。&/p&&p&从类型安全的角度来说,我觉得不存在任何人肉类型转换的代码就是好代码,我现在就是尽量做到这一点。这里的人肉转换包括C语言风格的转换,也包括C++风格的***_cast。如果需要任何类型转换,就说明存在设计的缺陷。当然了,如果实在有避免不了的时候,建议把含有类型转换的代码隔离起来,放在单独的模块或文件中,对其作充分的代码review、写完善的单元测试。&/p&&p&形如short s = 5; int i = 8888888; s = i;这种narrow conversioin也是极其危险的,但是现代C++编译器都会对这种代码给出警告甚至错误。&/p&&p&内存泄露与越界是C/C++世界两大灾难。关于内存泄露,因为服务器可以用mempool,除了不需要delete以外还可以避免内存碎片提高运行速度,所以对于如何应付内存泄露我没有特别的心得。但是对于如何应对内存越界,有一些小经验。&/p&&p&例如memcpy函数&/p&&div class=&highlight&&&pre&&code class=&language-text&&void * memcpy ( void * destination, const void * source, size_t num );
&/code&&/pre&&/div&&p&就是容易发生越界的一个经典函数,刚开始做后台的几年里面因为这个函数出现的内存问题次数不少,但是后来发现可以用C++的方法来改造它。&/p&&p&总结了一下实际memcpy函数使用主要有两种场景,一是对于结构体的拷贝,二是对于char类型的数组的拷贝。先说结构体的拷贝,对于Struct A {....};A a, b;如果要把b的内容拷贝到a,有两种方法:memcpy(&a, &b, sizeof(a));或者:a = b;前面一种方法容易出错,改成后面一种。&i&(a=b这种赋值拷贝的方法在C里面也是可以的,不能算是C++的改进,感谢miloyip指正)&/i&。&/p&&p&memcpy函数需要使用的第二种场景,是将结构体拷贝到一个buffer中,类似这样:&/p&&div class=&highlight&&&pre&&code class=&language-text&&char buff[N]; memcpy(buff, &a, sizeof(a));
&/code&&/pre&&/div&&p&这个当然也不是类型安全的,容易出问题,用C++的方法可以这样改造:&/p&&div class=&highlight&&&pre&&code class=&language-cpp&&&span class=&k&&template&/span&&span class=&o&&&&/span&&span class=&kt&&size_t&/span& &span class=&n&&N&/span&&span class=&p&&,&/span& &span class=&k&&class&/span& &span class=&nc&&A&/span&&span class=&o&&&&/span&
&span class=&kt&&void&/span& &span class=&n&&memcpy_safe&/span&&span class=&p&&(&/span&&span class=&kt&&char&/span& &span class=&p&&(&/span&&span class=&o&&&&/span&&span class=&n&&arr&/span&&span class=&p&&)[&/span&&span class=&n&&N&/span&&span class=&p&&],&/span& &span class=&k&&const&/span& &span class=&n&&A&/span&&span class=&o&&*&/span& &span class=&n&&p&/span&&span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&k&&static_assert&/span&&span class=&p&&(&/span&&span class=&k&&sizeof&/span&&span class=&p&&(&/span&&span class=&n&&A&/span&&span class=&p&&)&/span& &span class=&o&&&=&/span& &span class=&n&&N&/span&&span class=&p&&,&/span& &span class=&s&&&error blabla&&/span&&span class=&p&&);&/span&
&span class=&o&&::&/span&&span class=&n&&memcpy&/span&&span class=&p&&(&/span&&span class=&n&&arr&/span&&span class=&p&&,&/span& &span class=&n&&p&/span&&span class=&p&&,&/span& &span class=&k&&sizeof&/span&&span class=&p&&(&/span&&span class=&n&&A&/span&&span class=&p&&));&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&然后调用memcpy_safe(buff, &a)就安全了。&/p&&p&当然这里有个问题,就是拷贝的目标地址buff,必须声明为char buff[N]的形式,不能直接用char buff = new char[N]的形式,需要动态创建buffer的时候可以将buff封装在结构体里面,如:struct buffer_type { ...; size_ char buff[MAX_LEN]; }; 然后buffer_type* p = new buffer_ memcpy_safe(p-&buff, &a); 我们后台代码中类似用法已经全部改掉了。同理把snprintf之类的函数也全部用C++的方法封一遍,常见的内存问题都解决了。这些年我带的项目运营事故越来越少了,慢慢地在老大心中形成了一种口碑。&/p&&p&C与C++&真正&的区别在哪里?因为大家都已经回答了很多了,我提供一个比较感性的答案。在我看来,我觉得C就像一片充满荆棘的沼泽地,各种坑无处不在;而C++呢,就像是一座高山,C有的坑它都有,而且增加了无数新坑,但它还是竭尽所能地提供了一条安全上山的道路,想要冒险的时候可以去路边的丛林里玩耍,需要在工程中应用的时候又可以回到路上,工作娱乐两不误。&/p&&p&当然了,C与C++都有一个共同的与众不同的能力,就是让你能够把你的优选代码封装成祖传程序库,传给下一代。&/p&
我是先自学C后来再C++,刚开始学习的时候只知道自己爱编程,但是对于到底要去编什么程序并没有概念。大概在2007年左右,无意间在网上看了2001年的高分科幻电影《Artificial Intelligence》,才决定转服务器开发。电影里面人类灭亡了,我想如果我要做一个程…
&p&因为 C++ 比 Java 复杂,说说我学 C++ 时遇到的一些 Java 里不存在的问题吧。&/p&&p&&b&函数传值&/b&。C++ 要在函数间传递对象有传值、传引用、传指针的区别;传值的话要考虑复制构造和移动构造的问题,传引用的话会涉及到只读引用、可读写的引用和右值引用,传指针的话就更复杂了,用裸指针还是智能指针?智能指针要用 &code&shared_ptr&/code&,&code&unique_ptr&/code&,&code&weak_ptr&/code&还是其他什么玩意?你看人家 Java,基本类型就是复制传值,其他的就是传引用,简单得不是一点半点!&/p&&p&&b&创建对象&/b&。C++ 创建对象也是一门学问,光写法就有直接初始化、复制初始化、列表初始化三种,还能选择在栈里还是在堆里创建。隔壁 Java 呢?所有引用类型都在堆里创建,一个 &code&new&/code& 就能初始化对象,真的简单!&/p&&p&&b&生命周期&/b&。打个比方吧,Java 里的对象,你只用管生管养,用完扔了就是,不用管到死,有专业的 GC 帮你擦屁股;C++ 的对象你不仅要管生管养,用完了还要管死管埋,于是就会遇到死的对象没有埋(内存泄漏)、召唤僵尸干活(引用已释放的对象)、埋错对象(错误释放对象)、埋完又埋(重复释放对象)等等问题,这也是 C/C++ 等没有 GC 的语言必须面对的问题。&/p&&p&&b&类型声明&/b&。我非常不喜欢 C/C++ 复杂的类型声明,&code&const int *const&/code&、&code&int *(&arr)[10]&/code&什么的要死人啦!用 &code&using&/code& 定义类型别名还好,&code&typedef&/code&绝对是「晦涩难懂还容易出错」的代名词。一个常见的 C++ 类型往往包含有 &code&const&/code& 声明、指针(*)和引用(&)声明、命名空间、类型别名、泛型参数一堆成分,这也在一定程度上加大了 C++ 的复杂度。Java 的类型虽然也比较长,但一般不会写完整的包名,只包含类型本身的名字(不存在别名)和泛型参数,而且统一采用驼峰式命名法,整体观感上比 C++ 类型整齐简单很多。&/p&&p&&b&引用其他源文件&/b&。&code&#include&/code& 比 &code&import&/code&复杂了不止一点半点,还不好用。 &/p&
因为 C++ 比 Java 复杂,说说我学 C++ 时遇到的一些 Java 里不存在的问题吧。函数传值。C++ 要在函数间传递对象有传值、传引用、传指针的区别;传值的话要考虑复制构造和移动构造的问题,传引用的话会涉及到只读引用、可读写的引用和右值引用,传指针的话就…
&p&这个问题其实可以这么问,为什么现有的游戏引擎,多数还是以c++来实现的&/p&&p&c是pure pp的语言,java是pure oop的语言&/p&&p&c++则可以选择pp或者是oop&/p&&p&游戏其实是极为适合oop的领域&/p&&p&但是,pp的性能比oop要好,一些简单的操作,oop封装一层之后,简单说就会变慢&/p&&p&游戏是一种对于性能比较敏感的领域,可以说是软实时领域&/p&&p&硬实时是军工这种,比如无论如何,都不能暂停,哪怕几毫秒的暂停都不允许的那种&/p&&p&软实时可以满足多数民用领域的需要了&/p&&p&那这个是有具体的指标的&/p&&p&什么指标呢?我们来看一组数据&/p&&p&人眼对于刷新频率的感知,大概是60fps上下,也就是说,如果低于60fps,那么你的眼睛可能可以感觉到停顿,就会觉得卡,但是如果达到了60fps的话,那基本上人眼是感知不到的&/p&&p&你现在的手机上的刷新频率,安卓,苹果,基本上都是60fps&/p&&p&包括格斗游戏,街霸5这些,也都是60fps&/p&&p&那好,硬性的指标有了,剩下的就可以算了&/p&&p&那我们要求,在1/60s内,完成一次屏幕上的渲染&/p&&p&那这个时候就看了,oop的特性加上去之后,是否会影响到这1/60s的渲染?&/p&&p&如果机器它无法保证在1/60s内完成渲染,那就不行,如果可以完成渲染,那就没问题&/p&&p&所以这个问题同样可以问,为什么java不能做游戏?难点在哪里?&/p&&p&那就看嘛,如果是oop特性加上去,是不是能够在1/60s内完成渲染?&/p&&p&如果可以,就可以,如果不行,就不行&/p&&p&oop特性加上去之后,会有一些问题,比如gc这些,还有编译成字节码,编译执行效率低&/p&&p&还有渲染的时候,效率的问题,这些都是问题,都是需要解决的问题,如果不解决,就做不了&/p&&p&那这个时候,我们说一句,这些技术问题,靠纯粹的游戏公司,或者说游戏引擎公司&/p&&p&不行的,他们的技术实力,不够,无法完成这么高级的优化,这些年他们也都没有成功&/p&&p&他们完不成这种优化,所以就只能把接口留出来说,哎呀,你们还是用pp的特性来实现吧&/p&&p&这样快一点,因为他们做不到这种优化嘛&/p&&p&那这个时候,还是要看斯坦福大学,加州硅谷,google等公司的工程师,有这个本事&/p&&p&这个时候去看dart,flutter的优化,怎么优化的&/p&&p&aot, jit, gc pause优化,硬件加速渲染,各种搞&/p&&p&现在flutter已经claim说,gc不会掉帧了,其次aot之后,跨平台带来的一些性能问题也就迎刃而解了,还有gpu,硬件加速的渲染这些,也都可以了&/p&&p&那在google的努力下,以后通过pure oop语言来实现游戏开发,并不是不可能&/p&&p&反正我们在实践的过程中,发现2d游戏用pure oop语言开发,已经完全不是问题了,java那边也在抄google的技术进步&/p&&p&比如10ms以内的zgc项目,反正开源的东西嘛,都是抄来抄去的&/p&&p&所以回答你的问题,他们用c++做引擎,是因为当时的技术实力不行,很多性能问题还没有解决&/p&&p&但是人类的技术进步是不会停滞的,jit, aot, pauseless gc, gpu, 硬件加速这些技术都在发展&/p&&p&现在回头看,用非c++语言,用一些比较pure的oop语言,e.g. java, dart etc.开发游戏&/p&&p&即将可行,所以以后游戏开发也会变得简单起来,而且我们把各种语言的引擎套上这些api之后&/p&&p&发现,同样也能完成60fps的渲染效率,换句话说,你将来用什么语言做都不是问题了&/p&&p&当然fp语言还有点问题,主要是immutable会带来的gc压力,这个还是要看gc的效率,理论上说,oop会先于fp语言先在软实时领域得到应用&/p&&p&但是从长远看,所有语言都会变得available是大势所趋&/p&&p&所以以后你会见到各种语言版本的游戏引擎,不奇怪,这点还真是要感谢一下Google&/p&&p&这些年在low latency领域作出的巨大贡献,最近实践证明,不用c++写一个游戏是完全可行的&/p&&p&而且是网游,回头我把一些关键的步骤写出来,其实在我的想法等,已经写出很多来了&/p&&p&学习能力强一点的,看看我过往的一些想法之类的,就能明白肿么做了&/p&&p&flutter+vert.x,以后语言你随意,当然缺省是dart和java&/p&
这个问题其实可以这么问,为什么现有的游戏引擎,多数还是以c++来实现的c是pure pp的语言,java是pure oop的语言c++则可以选择pp或者是oop游戏其实是极为适合oop的领域但是,pp的性能比oop要好,一些简单的操作,oop封装一层之后,简单说就会变慢游戏是一种…
&p&&b&1.在C++ 程序中调用被C 编译器编译后的函数,为什么要加extern “C”?&/b&&br&&br&答:首先,extern是C/C++语言中表明函数和全局变量作用范围的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。&br&&br&通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。extern &C&是连接申明(linkage &br&declaration),被extern &br&&C&修饰的变量和函数是按照C语言方式编译和连接的。作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:void&br& foo( int x, int y &br&);该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。&br&&br&所以,可以用一句话概括extern “C”这个声明的真实目的:&u&解决名字匹配问题,实现C++与C的混合编程。&/u&&/p&&p&&b&2.头文件中的ifndef/define/endif有什么作用?&/b&&/p&&p&答:这是C++预编译头文件保护符,保证即使文件被多次包含,头文件也只定义一次。&/p&&p&&b&3. #include&file.h& 与 #include &file.h&的区别?&/b&&br&&br&答:前者是从标准库路径寻找和引用file.h,而后者是从当前工作路径搜寻并引用file.h。&/p&&p&&b&4.评价一下C/C++各自的特点&/b&&/p&&p&答:C语言是一种结构化语言,面向过程,基于算法和数据结构,所考虑的是如何通过一个过程或者函数从输入得到输出;&/p&&p&C++是面向对象,基于类、对象和继承,所考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题,通过获取对象的状态信息得到输出或实现过程控制。&/p&&p&&b&5.const 有什么用途?&/b&&/p&&p&答:在C/C++中,(1)可以定义const常量,(2)修饰函数的返回值和形参;&/p&&p&在C++中,还可以修饰函数的定义体,定义类的const成员函数。被const修饰的东西受到强制保护,可以预防意外的变动,提高了程序的健壮性。&/p&&p&&b&6.const和#define有什么区别?&/b&&/p&&p&答:(1)const和#define都可以定义常量,但是const用途更广。&/p&&p&(2)const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。&br&&br&(3) 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。&/p&&p&&b&7.关于sizeof小结的。&/b&&/p&&p&答:sizeof计算的是在栈中分配的内存大小。&/p&&p&(1) sizeof不计算static变量占得内存;&/p&&p&(2) 32位系统的指针的大小是4个字节,64位系统的指针是8字节,而不用管指针类型;&/p&&p&(3) char型占1个字节,int占4个字节,short int占2个字节&/p&&p&long int占4个字节,float占4字节,double占8字节,string占4字节&/p&&p&一个空类占1个字节,单一继承的空类占1个字节,虚继承涉及到虚指针所以占4个字节&/p&&p&(4) 数组的长度:&/p&&p&若指定了数组长度,则不看元素个数,总字节数=数组长度*sizeof(元素类型)&/p&&p&若没有指定长度,则按实际元素个数类确定&/p&&p&Ps:若是字符数组,则应考虑末尾的空字符。&/p&&p&(5) 结构体对象的长度&/p&&p&在默认情况下,为方便对结构体内元素的访问和管理,当结构体内元素长度小于处理器位数的时候,便以结构体内最长的数据元素的长度为对齐单位,即为其整数倍。若结构体内元素长度大于处理器位数则以处理器位数为单位对齐。&/p&&p&(6) unsigned影响的只是最高位的意义,数据长度不会改变,所以sizeof(unsigned int)=4&/p&&p&(7) 自定义类型的sizeof取值等于它的类型原型取sizeof&/p&&p&(8) 对函数使用sizeof,在编译阶段会被函数的返回值的类型代替&/p&&p&(9) sizeof后如果是类型名则必须加括号,如果是变量名可以不加括号,这是因为sizeof是运算符&/p&&p&(10) 当使用结构类型或者变量时,sizeof返回实际的大小。当使用静态数组时返回数组的全部大小,sizeof不能返回动态数组或者外部数组的尺寸&/p&&p&&b&8.sizeof与strlen的区别?&/b&&/p&&p&答: (1)sizeof的返回值类型为size_t(unsigned int);&/p&&p&(2)sizeof是运算符,而strlen是函数;&/p&&p&(3)sizeof可以用类型做参数,其参数可以是任意类型的或者是变量、函数,而strlen只能用char*做参数,且必须是以’\0’结尾;&/p&&p&(4)数组作sizeof的参数时不会退化为指针,而传递给strlen是就退化为指针;&/p&&p&(5)sizeo是编译时的常量,而strlen要到运行时才会计算出来,且是字符串中字符的个数而不是内存大小;&/p&&p&&b&9.指针和引用的区别?&/b&&/p&&p&答:指针和引用都提供了间接操作对象的功能。&/p&&p&(1) 指针定义时可以不初始化,而引用在定义时就要初始化,和一个对象绑定,而且一经绑定,只要引用存在,就会一直保持和该对象的绑定;&/p&&p&(2) 赋值行为的差异:指针赋值是将指针重新指向另外一个对象,而引用赋值则是修改对象本身;&/p&&p&(3) 指针之间存在类型转换,而引用分const引用和非const应用,非const引用只能和同类型的对象绑定,const引用可以绑定到不同但相关类型的对象或者右值&/p&&p&&b&10.数组和指针的区别?&/b&&/p&&p&答:(1)数组要么在全局数据区被创建,要么在栈上被创建;指针可以随时指向任意类型的内存块;&/p&&p&(2)修改内容上的差别:&/p&&p&char a[] = “hello”;&br&&br&a[0] = ‘X’;&br&&br&char *p = “world”; // 注意p 指向常量字符串&br&&br&p[0] = ‘X’; // 编译器不能发现该错误,运行时错误&/p&&p&(3)用运算符sizeof 可以计算出数组的容量(字节数)。sizeof(p),p 为指针得到的是一个指针变量的字节数,而不是p &br&所指的内存容量。C++/C &br&语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。&/p&&p&&b&11.空指针和悬垂指针的区别?&/b&&/p&&p&答:空指针是指被赋值为NULL的指针;delete指向动态分配对象的指针将会产生悬垂指针。&/p&&p&(1) 空指针可以被多次delete,而悬垂指针再次删除时程序会变得非常不稳定;&/p&&p&(2) 使用空指针和悬垂指针都是非法的,而且有可能造成程序崩溃,如果指针是空指针,尽管同样是崩溃,但和悬垂指针相比是一种可预料的崩溃。&/p&&p&&b&12.C++中有malloc/free,为什么还有new/delete?&/b&&/p&&p&答:malloc/free是C/C++标准库函数,new/delete是C++运算符。他们都可以用于动态申请和释放内存。&/p&&p&对于内置类型数据而言,二者没有多大区别。malloc申请内存的时候要制定分配内存的字节数,而且不会做初始化;new申请的时候有默认的初始化,同时可以指定初始化;&/p&&p&对于类类型的对象而言,用malloc/free无法满足要求的。对象在创建的时候要自动执行构造函数,消亡之前要调用析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制之内,不能把执行构造函数和析构函数的任务强加给它,因此,C++还需要new/delete。&/p&&p&&b&13.什么是智能指针?&/b&&/p&&p&答:当类中有指针成员时,一般有两种方式来管理指针成员:一是采用值型的方式管理,每个类对象都保留一份指针指向的对象的拷贝;另一种更优雅的方式是使用智能指针,从而实现指针指向的对象的共享。&/p&&p&  智能指针的一种通用实现技术是使用引用计数。智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象共享同一指针。&/p&&p&  每次创建类的新对象时,初始化指针并将引用计数置为1;当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数;调用析构函数时,构造函数减少引用计数(如果引用计数减至0,则删除基础对象)。&/p&&p&&b&14.面向对象技术的基本概念是什么,三个基本特征是什么?&/b&&/p&&p&答:基本概念:类、对象、继承; 基本特征:封装、继承、多态。&/p&&p&封装:将低层次的元素组合起来形成新的、更高实体的技术;&br&&br&继承:广义的继承有三种实现形式:实现继承、可视继承、接口继承。&br&&br&多态:允许将子类类型的指针赋值给父类类型的指针&/p&&p&&b&15.C++空类默认有哪些成员函数?&/b&&/p&&p&答:默认构造函数、析构函数、复制构造函数、赋值函数&/p&&p&&b&16.哪一种成员变量可以在一个类的实例之间共享?&/b&&/p&&p&答:static静态成员变量&/p&&p&&b&17.继承层次中,为什么基类析构函数是虚函数?&/b&&/p&&p&答:编译器总是根据类型来调用类成员函数。但是一个派生类的指针可以安全地转化为一个基类的指针。这样删除一个基类的指针的时候,C++不管这个指针指向一个基类对象还是一个派生类的对象,调用的都是基类的析构函数而不是派生类的。如果你依赖于派生类的析构函数的代码来释放资源,而没有重载析构函数,那么会有资源泄漏。&/p&&p&&b&18.为什么构造函数不能为虚函数?&/b&&/p&&p&答:虚函数采用一种虚调用的方法。需调用是一种可以在只有部分信息的情况下工作的机制。如果创建一个对象,则需要知道对象的准确类型,因此构造函数不能为虚函数。&/p&&p&&b&19.如果虚函数是有效的,那为什么不把所有函数设为虚函数?&/b&&/p&&p&答:不行。首先,虚函数是有代价的,由于每个虚函数的对象都要维护一个虚函数表,因此在使用虚函数的时候都会产生一定的系统开销,这是没有必要的。&/p&&p&&b&20.构造函数可以是内联函数&/b&&/p&&p&&b&21.什么是多态?多态有什么作用?&/b&&/p&&p&答:多态就是将基类类型的指针或者引用指向派生类型的对象。多态通过虚函数机制实现。&/p&&p&多态的作用是接口重用。&/p&&p&&b&22.重载和覆盖有什么区别?&/b&&/p&&p&答:虚函数是基类希望派生类重新定义的函数,派生类重新定义基类虚函数的做法叫做覆盖;&/p&&p&重载就在允许在相同作用域中存在多个同名的函数,这些函数的参数表不同。重载的概念不属于面向对象编程,编译器根据函数不同的形参表对同名函数的名称做修饰,然后这些同名函数就成了不同的函数。&/p&&p&重载的确定是在编译时确定,是静态的;虚函数则是在运行时动态确定。&/p&&p&&b&23.公有继承、受保护继承、私有继承&/b&&/p&&p&答:(1)公有继承时,派生类对象可以访问基类中的公有成员,派生类的成员函数可以访问基类中的公有和受保护成员;&/p&&p&(2)私有继承时,基类的成员只能被直接派生类的成员访问,无法再往下继承;&/p&&p&(3)受保护继承时,基类的成员也只被直接派生类的成员访问,无法再往下继承。&/p&&p&&b&24.公有继承时基类受保护的成员,可以通过派生类对象访问但不能修改。&/b&&/p&&p&&b&25.有哪几种情况只能用构造函数初始化列表而不能用赋值初始化?&/b&&/p&&p&答:const成员,引用成员&/p&&p&&b&26.什么是虚指针?&/b&&/p&&p&答:虚指针或虚函数指针是虚函数的实现细节。带有虚函数的每一个对象都有一个虚指针指向该类的虚函数表。&/p&&p&&b&27.C++如何阻止一个类被实例化?一般在什么时候将构造函数声明为private?&/b&&/p&&p&答:(1)将类定义为抽象基类或者将构造函数声明为private;&/p&&p&(2)不允许类外部创建类对象,只能在类内部创建对象&/p&&p&&b&28.main函数执行之前会执行什么?执行之后还能执行代码吗?&/b&&/p&&p&答:(1)全局对象的构造函数会在main函数之前执行;&/p&&p&(2)可以,可以用_onexit 注册一个函数,它会在main 之后执行;&/p&&p&如果你需要加入一段在main退出后执行的代码,可以使用atexit()函数,注册一个函数。&/p&&p&语法:&/p&&p&#include &stdlib.h&&/p&&p&#include &stdio.h&&/p&&p&int atexit(void (*function&)(void));&/p&&p&void fn1( void ), fn2( void ), fn3( void );&/p&&p&int main( void )&/p&&p&{&/p&&p&atexit(fn1);&/p&&p&atexit( fn2 );&/p&&p&printf( &This is executed first.\n& );&/p&&p&}&/p&&p&void fn1()&/p&&p&{&/p&&p&printf( & This is\n& );&/p&&p&}&/p&&p&void fn2()&/p&&p&{&/p&&p&printf( & executed next.& );&/p&&p&}&/p&&p&结果:&/p&&p&This is executed first.&/p&&p&This is executed next.&/p&&p&&b&29.请描述进程和线程的区别?&/b&&/p&&p&答:(1)进程是程序的一次执行,线程是进程中的执行单元;&/p&&p&(2)进程间是独立的,这表现在内存空间、上下文环境上,线程运行在进程中;&/p&&p&(3)一般来讲,进程无法突破进程边界存取其他进程内的存储空间;而同一进程所产生的线程共享内存空间;&/p&&p&(4)同一进程中的两段代码不能同时执行,除非引入多线程。&/p&&p&&b&30.进程间如何通信?&/b&&/p&&p&答:信号、信号量、消息队列、共享内存&/p&&p&&b&31.在网络编程中涉及并发服务器,使用多进程与多线程的区别?&/b&&/p&&p&答:(1)线程执行开销小,但不利于资源管理和保护;进程则相反,进程可跨越机器迁移。&/p&&p&(2)多进程时每个进程都有自己的内存空间,而多线程间共享内存空间;&/p&&p&(3)线程产生的速度快,线程间通信快、切换快;&/p&&p&(4)线程的资源利用率比较好;&/p&&p&(5)线程使用公共变量或者资源时需要同步机制。&/p&&p&&b&32.说一下TCP 3次握手、4次挥手的全过程。&/b&&/p&&p&&b&33.TCP和UDP有什么区别。&/b&&/p&&p&答:&/p&&p&TCP——传输控制协议,提供的是面向连接、可靠的字节流服务。&/p&&p&当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。&/p&&p&UDP——用户数据报协议,是一个简单的面向数据报的传输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快.&/p&&p&TCP协议和UDP协议的一些特性区别如下:&/p&&p&1.TCP协议在传送数据段的时候要给段标号;UDP 协议不需要。&/p&&p&2.TCP协议可靠;UDP协议不可靠。&/p&&p&3.TCP协议是面向连接;UDP协议采用无连接。&/p&&p&4.TCP协议负载较高,采用虚电路;UDP协议低负载。&/p&&p&5.TCP协议的发送方要确认接受方是否收到数据段(3次握手协议)。&/p&&p&6.TCP协议采用窗口技术和流控制。&/p&&p&&b&34.如何编写套接字?&/b&&/p&&p&&b&35.调用函数时要进行参数压栈,一般情况下顺序是从最右边参数往左压栈。&/b&&/p&&p&&b&36.经常要操作的内存分为那几个类别?&/b&&/p&&p&答:(1)栈区:由编译器自动分配和释放,存放函数的参数值、局部变量的值等;&/p&&p&(2)堆:一般由程序员分配和释放,存放动态分配的变量;&/p&&p&(3)全局区(静态区):全局变量和静态变量存放在这一块,初始化的和未初始化的分开放;&/p&&p&(4)文字常量区:常量字符串就放在这里,程序结束自动释放;&/p&&p&(5)程序代码区:参访函数体的二进制代码。&/p&&p&&b&37.请讲述堆和栈的区别。&/b&&/p&&p&答:(1)申请方式不同。栈上有系统自动分配和释放;堆上有程序员自己申请并指明大小;&/p&&p&(2)栈是向低地址扩展的数据结构,大小很有限;堆是向高地址扩展,是不连续的内存区域,空间相对大且灵活;&/p&&p&(3)栈由系统分配和释放速度快;堆由程序员控制,一般较慢,且容易产生碎片;&/p&&p&&b&38.全局变量放在&u&数据段&/u&,内部变量static int count;放在&u&数据段&/u&,内部变量char *p=“AAA”,p的位置在&u&堆栈&/u&上,指向的空间的位置&u&数据段&/u&,内部变量char&br& *p=new char;p的位置&u&堆&/u&,指向的空间的位置&u&数据段&/u&&/b&&/p&&p&&br&&/p&&p&&b&39.字符数组与字符串的比较:&u&最明显的区别是字符串会在末尾自动添加空字符。&/u&&/b&&/p&&p&&b&40.函数指针相关概念(C++学习笔记)&/b&&/p&&p&&b&41.类使用static成员的优点,如何访问?&/b&&/p&&p&答:优点:&/p&&p&(1)static 成员的名字是在类的作用域中,因此可以避免与其他类的成员或全局对象名字冲突;&/p&&p&(2)可以实施封装。static 成员可以是私有成员,而全局对象不可以;&/p&&p&(3) static 成员是与特定类关联的,可清晰地显示程序员的意图。&/p&&p&static 数据成员必须在类定义体的外部定义(正好一次),static 关键字只能用于类定义体内部的声明中,定义不能标示为static. &br&不像普通数据成员,static成员不是通过类构造函数进行初始化,也不能在类的声明中初始化,而是应该在定义时进行初始化.保证对象正好定义一次的最好办法,就是将static&br& 数据成员的定义放在包含类非内联成员函数定义的文件中。&/p&&p&静态数据成员初始化的格式为:&/p&&p&<数据类型><类名>::<静态数据成员名>=<值>&/p&&p&类的静态数据成员有两种访问形式:&/p&&p&<类对象名>.<静态数据成员名> 或 <类类型名>::<静态数据成员名>&/p&&p&&b&42. static数据成员和static成员函数&/b&&/p&&p&答:(1)static数据成员:&br&&br&static数据成员独立于该类的任意对象而存在;每个static数据成员是与类关联的对象,并不与该类的对象相关联。Static数据成员(const&br&&br&static数据成员除外)必须在类定义体的外部定义。不像普通数据成员,static成员不是通过类的构造函数进行初始化,而是应该在定义时进行初始化。&br&&br&(2)static成员函数:&br&&br&Static成员函数没有this形参,它可以直接访问所属类的static成员,不能直接使用非static成员。因为static成员不是任何对象的组成部分,所以static成员不能被声明为const。同时,static成员函数也不能被声明为虚函数。&/p&&p&&b&43.static成员变量定义放在cpp文件中,不能放在初始化列表中。Const static成员可就地初始化。&/b&&/p&&p&&br&&/p&&p&&b&44.如何引用一个已经定义过的全局变量?&/b&&/p&&p&答:可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变量,假定你将那个变量写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。&/p&&p&&b&44.static关键字的作用。&/b&&/p&&p&答:static总是使得变量或对象的存储形式变成静态存储,连接方式变成内部连接,对于局部变量(已经是内部连接了),它仅改变其存储方式;对于全局变量(已经是静态存储了),它仅改变其连接类型。&/p&&p&&b&45.奈奎斯特定理&/b&&/p&&p&&b&46.香农定理&/b&&/p&&p&&b&47.多态类中的虚函数表是 Compile-Time,还是 Run-Time时建立的?&/b&&/p&&p&答案:虚拟函数表是在编译期就建立了,各个虚拟函数这时被组织成了一个虚拟函数的入口地址的数组。而对象的隐藏成员--虚拟函数表指针是在运行期--也就是构造函数被调用时进行初始化的,这是实现多态的关键。&/p&&p&&b&48. 一个父类写了一个 virtual 函数,如果子类覆盖它的函数不加 virtual ,也能实现多态?&/b&&/p&&p&&b&在子类的空间里,有没有父类的这个函数,或者父类的私有变量? (华为笔试题)&/b&&/p&&p&答案:只要基类在定义成员函数时已经声明了 virtue关键字,在派生类实现的时候覆盖该函数时,virtue关键字可加可不加,不影响多态的实现。子类的空间里有父类的所有变量(static除外)。&/p&&p&&b&49. 完成字符串拷贝可以使用 sprintf、strcpy 及 memcpy 函数,请问这些函数有什么区别&/b&&/p&&p&&b&,你喜欢使用哪个,为什么?&/b&&/p&&p&答案:这些函数的区别在于 实现功能以及操作对象不同。&/p&&p&(1)strcpy 函数操作的对象是字符串,完成从源字符串到目的字符串的拷贝功能。&/p&&p&(2)sprintf 函数操作的对象不限于字符串:虽然目的对象是字符串,但是源对象可以是字符串、也可以是任意基本类型的数据。这个函数主要用来实现(字符串或基本数据类型)向字符串的转换功能。如果源对象是字符串,并且指定 %s 格式符,也可实现字符串拷贝功能。&/p&&p&(3)memcpy &br&函数顾名思义就是内存拷贝,实现将一个内存块的内容复制到另一个内存块这一功能。内存块由其首地址以及长度确定。程序中出现的实体对象,不论是什么类型,其最终表现就是在内存中占据一席之地(一个内存区间或块)。因此,memcpy&br&&br&的操作对象不局限于某一类数据类型,或者说可适用于任意数据类型,只要能给出对象的起始地址和内存长度信息、并且对象具有可操作性即可。鉴于memcpy&br& 函数等长拷贝的特点以及数据类型代表的物理意义,memcpy &br&函数通常限于同种类型数据或对象之间的拷贝,其中当然也包括字符串拷贝以及基本数据类型的拷贝。&/p&&p&对于字符串拷贝来说,用上述三个函数都可以实现,但是其实现的效率和使用的方便程度不同:&/p&&p&o strcpy 无疑是最合适的选择:效率高且调用方便。&/p&&p&o sprintf 要额外指定格式符并且进行格式转化,麻烦且效率不高。&/p&&p&o memcpy 虽然高效,但是需要额外提供拷贝的内存长度这一参数,易错且使用不便;并且如果长度指定过大的话(最优长度是源字符串长度 + &br&1),还会带来性能的下降。其实 strcpy 函数一般是在内部调用 memcpy 函数或者用汇编直接实现的,以达到高效的目的。因此,使用 &br&memcpy 和 strcpy 拷贝字符串在性能上应该没有什么大的差别。&/p&&p&对于非字符串类型的数据的复制来说,strcpy 和 snprintf 一般就无能为力了,可是对 memcpy &br&却没有什么影响。但是,对于基本数据类型来说,尽管可以用 memcpy &br&进行拷贝,由于有赋值运算符可以方便且高效地进行同种或兼容类型的数据之间的拷贝,所以这种情况下 memcpy 几乎不被使用 。memcpy &br&的长处是用来实现(通常是内部实现居多)对结构或者数组的拷贝,其目的是或者高效,或者使用方便,甚或两者兼有。&/p&&p&&b&50. 应用程序在运行时的内存包括代码区和数据区,其中数据区又包括哪些部分?&/b&&/p&&p&答:对于一个进程的内存空间而言,可以在逻辑上分成 3个部份:代码区,静态数据区和动态数据区。&/p&&p&动态数据区一般就是“堆栈”。 栈是一种线性结构,堆是一种链式结构。进程的每个线程都有私有的“栈”。&/p&&p&全局变量和静态变量分配在静态数据区,本地变量分配在动态数据区,即堆栈中。程序通过堆栈的基地址和偏移量来访问本地变量。&/p&&p&&b&51. C++函数中值的传递方式有哪几种?&/b&&/p&&p&答:三种传递方式为:值传递、指针传递和引用传递。&/p&&p&&b&52. C++里面是不是所有的动作都是main()引起的?如果不是,请举例.&/b&&/p&&p&比如全局变量的初始化,就不是由main函数引起的&/p&&p&举例: class A{};&/p&&p&A //a的构造函数限执行&/p&&p&int main() {}&/p&&p&&b&53. 下列哪两个是等同的&/b&&/p&&p&&/p&&p&A const int* a = &b;&/p&&p&B const* int a = &b;&/p&&p&C const int* const a = &b;&/p&&p&D int const* const a = &b;&/p&&p&&b&54. 内联函数在编译时是否做参数类型检查?&/b&&/p&&p&答:内联函数要做参数类型检查, 这是内联函数跟宏相比的优势。&/p&&p&&b&55. 全局变量和局部变量有什么区别?实怎么实现的?操作系统和编译器是怎么知道的?&/b&&/p&&p&(1)生命周期不同:&/p&&p&全局变量随主程序创建和创建,随主程序销毁而销毁&/p&&p&局部变量在局部函数内部,甚至局部循环体等内部存在,退出就不存在; 内存中&/p&&p&分配在全局数据区&/p&&p&(2)使用方式不同:通过声明后全局变量程序的各个部分都可以用到;局部变量只能在局部使用,分配在栈区&/p&&p&操作系统和编译器通过内存分配的位置来知道的,全局变量分配在全局数据段并且在程序开始运行的时候被加载。局部变量则分配在堆栈里面 。&/p&&p&&b&56. 有 A 、 B 、 C 、 D 四个人,要在夜里过一座桥。他们通过这座桥分别需要耗时 1 、 2 、 5 、 10 分钟,只有一支手电,并且同时最多只能两个人一起过桥。请问,如何安排,能够在 17 分钟内这四个人都过桥?&/b&&/p&&p&Solution:关键是时间最长的两个人必须同时过桥&/p&&p&The First Time : A(1) 和 B(2) 过桥, A(1) 返回 Cost : 1+2&/p&&p&The Second Time : C(5) 和 D(10) 过桥, B(2) 返回 Cost : 10+2&/p&&p&The Third Time A(1) 和 B(2) 过桥 Cost : 2&/p&&p&Total Time Cost : (1+2)+(10+2)+2=17 minutes&/p&&p&57. &b&static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?&/b&&/p&&p&答:static全局变量与普通全局变量区别:static全局变量只初使化一次,防止在其他文件单元中被引用;&/p&&p&static局部变量和普通局部变量区别:static局部变量只被初始化一次,下一次依据上一次结果值;&/p&&p&static函数与普通函数区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝。&/p&&p&&b&58. 程序的局部变量存在于(堆栈)中,全局变量存在于(静态区 )中,动态申请数据存在于( 堆)中。&/b&&/p&&p&&br&&/p&&p&&b&59. 对于一个频繁使用的短小函数,在C语言中应用什么实现,在C++中应用什么实现?&/b&&/p&&p&c用宏定义,c++用inline&/p&&p&&b&60. 有1,2,....一直到n的无序数组,求排序算法,并且要求时间复杂度为O(n),空间复杂度O(1),使用交换,而且一次只能交换两个数。&/b&&/p&&p&#include&iostream.h&&/p&&p&Using namespace std;&/p&&p&int main(){&/p&&p&int a[] = {10,6,9,5,2,8,4,7,1,3};&/p&&p&int len = sizeof(a) / sizeof(int);&/p&&p&&/p&&p&for(int i = 0; i & )&/p&&p&{&/p&&p&temp = a[a[i] - 1];&/p&&p&a[a[i] - 1] = a[i];&/p&&p&a[i] =&/p&&p&if ( a[i] == i + 1)&/p&&p&i++;&/p&&p&}&/p&&p&for (int j = 0; j & j++)&/p&&p&cout&&a[j]&&&,&;&/p&&p&return 0;&/p&&p&}&/p&
1.在C++ 程序中调用被C 编译器编译后的函数,为什么要加extern “C”? 答:首先,extern是C/C++语言中表明函数和全局变量作用范围的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。 通常,在模块的头文件中对本模块提供给…
&p&这周末读了一遍 deno 的源码,特意过来回答一下。长文预警(5000字,11图)。&/p&&h2&0. 为什么开发 Deno?&/h2&&p&这是我上周做的一张图,介绍了 JavaScript 的发展简史。刚才修改了一下,添加了对 Node.js 和 Deno 发布时间的标注。&/p&&p&Node.js 和 Deno 分别是 Ryan Dahl 在 2009 年和 2018 年,基于当年最新的前端技术开发的&b&非浏览器 JavaScript 运行时&/b&。&/p&&figure&&img src=&https://pic3.zhimg.com/50/v2-747c11e9c0f6c7df743368dcd2c38b58_b.jpg& data-size=&normal& data-rawwidth=&1812& data-rawheight=&631& class=&origin_image zh-lightbox-thumb& width=&1812& data-original=&https://pic3.zhimg.com/50/v2-747c11e9c0f6c7df743368dcd2c38b58_r.jpg&&&figcaption&JavaScript 发展史&/figcaption&&/figure&&p&Ryan Dahl 开发 deno 并不是因为 “just for fun”,也&b&不是为了取代 node&/b&。下面慢慢解释。&/p&&h2&1. 目前 deno 只是一个 demo&/h2&&p&这两天花时间看了 deno 的源码(好在是初级阶段,源码很少,也很容易理解),顺带看了所有的 issue 和 pr。不知道“从官方介绍来看,可以认为它是下一代 Node”是如何脑补出来的。&/p&&p&既然是 Node.js 之父的新作,在讨论中自然离不开 Node.js。而作者很皮的回复到:&/p&&blockquote&The main difference is that Node works and Deno does not work : )&br&&b&最大的区别就是:Node 可以工作,而 Deno 不行 : )&/b&&/blockquote&&p&目前 Deno 只是一个 Demo,甚至连二进制发行版都没有。好在从源码编译比较简单(如果你使用的不是 Windows 系统)。&/p&&p&在 high-level 层面,Deno 提供了一个尽可能简单的 V8 到系统 API 的绑定。为什么使用 Golang 替代 C++ 呢,因为相比 Node 而言,Golang 让我们&b&更加容易的添加新特性&/b&,比如 http2 等。&/p&&p&至于为什么不选择 Rust,作者没有回答。&/p&&p&我们再对比一下两者的启动性能。分别运行:&/p&&div class=&highlight&&&pre&&code class=&language-text&&console.log('Hello world')
&/code&&/pre&&/div&&figure&&img src=&https://pic1.zhimg.com/50/v2-c0baa21ebd8_b.jpg& data-size=&normal& data-rawwidth=&821& data-rawheight=&507& class=&origin_image zh-lightbox-thumb& width=&821& data-original=&https://pic1.zhimg.com/50/v2-c0baa21ebd8_r.jpg&&&figcaption&node vs deno&/figcaption&&/figure&&p&我之前写过一篇文章:&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&Node.js 新计划:使用 V8 snapshot 将启动速度提升 8 倍&/a&,那如果我们使用 --without-snapshot 参数编译 Node.js 呢?&/p&&figure&&img src=&https://pic3.zhimg.com/50/v2-9be64cec710c060e02c5_b.jpg& data-size=&normal& data-rawwidth=&821& data-rawheight=&507& class=&origin_image zh-lightbox-thumb& width=&821& data-original=&https://pic3.zhimg.com/50/v2-9be64cec710c060e02c5_r.jpg&&&figcaption&deno vs node(without-snapshot)&/figcaption&&/figure&&p&依然是相差悬殊,毕竟 deno 需要加载一个 TypeScript 编译器。毕竟是一个 demo 版本,希望以后用力优化。&/p&&p&对于性能提升还有一个思路就是,可以使用 LLVM 作为后端编译器把 TypeScript 代码编译为 WebAssembly 然后在 V8 里面运行,甚至可以直接把源码编译成二进制代码运行。Ryan Dahl 表示 deno 只需要一个编译器,那就是 TS。但是既然 deno 要兼容浏览器,那么 WebAssembly 应该也会被支持。&/p&&p&Deno 可以对 ts 的编译结果进行缓存(~/.deno/cache),所以目前关注的就是启动速度和初次编译速度。&/p&&p&要么就是在发布前先行编译,如此一来 deno 就脱离了开发的初衷了。deno 是一个 ts 的运行时,那么就应该可以直接运行 ts 代码,如果提前把 ts 编译成 js,那么 deno 就回退到 js 运行时了。&/p&&h2&2. 初学者应该学习 Node.js 还是 Deno?&/h2&&p&对于这个问题,Ryan Dahl 的回答干净利落:&/p&&blockquote&Use Node. Deno is a prototype / experiment.&br&&b&使用 Node。Deno 只是一个原型或实验性产品。&/b&&/blockquote&&p&从介绍可以看到,Deno 的目标是&b&不兼容 Node&/b&,而是&b&兼容浏览器&/b&。&/p&&p&所以,Deno 不是要取代 Node.js,也不是下一代 Node.js,也不是要放弃 npm 重建 Node 生态。deno 的目前是要拥抱浏览器生态。&/p&&p&不得不说这个目标真伟大。Ryan Dahl 开发了 Node.js,社区构建出了整个 npm 生态。我在另一个回答 &a href=&https://www.zhihu.com/question//answer/& class=&internal&&justjavac:纯前端开发眼里nodejs到底是什么?&/a& 里面写到“Node.js 是前端工程化的重要支柱之一”。&/p&&p&虽然后来 Ryan Dahl 离开 Node.js 去了 Golang 社区,但是现在 Ryan Dahl 又回来了,为 JavaScript 社区带来了 Golang,开发出了 Deno,然后拥抱浏览器生态。 &/p&&p&我们看看 deno 的关于 Web API 的目标:&/p&&ul&&li&High level&/li&&li& Console √&/li&&li& File/FileList/FileReader/Blob&/li&&li& XMLHttpRequest&/li&&li& WebSocket&/li&&li&Middle level&/li&&li& AudioContext/AudioBuffer&/li&&li& Canvas&/li&&/ul&&p&甚至还会包括 webGL 和 GPU 等的支持。&/p&&h2&3. Deno 的架构&/h2&&p&Parsa Ghadimi 绘制了一张关于 &a href=&//link.zhihu.com/?target=https%3A//user-images.githubusercontent.com/df28--8d77-a7fa422093eb.png& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Deno 的架构图&/a&:&/p&&figure&&img src=&https://pic4.zhimg.com/50/v2-9ed34ec9a24dc2d2e9120b47dab59d91_b.jpg& data-size=&normal& data-rawwidth=&1336& data-rawheight=&474& class=&origin_image zh-lightbox-thumb& width=&1336& data-original=&https://pic4.zhimg.com/50/v2-9ed34ec9a24dc2d2e9120b47dab59d91_r.jpg&&&figcaption&Deno's architecture&/figcaption&&/figure&&p&底层使用了作者开发的 &a href=&//link.zhihu.com/?target=https%3A//github.com/ry/v8worker2& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&v8worker2&/a&,而 event-loop 则基于 pub/sub 模型。关于 v8worker 可以看看这个 PPT:&a href=&//link.zhihu.com/?target=https%3A//docs.google.com/presentation/d/1RgGVgLuP93mPZ0lqHhm7TOpxZBI3TEdAJQZzFqeleAE/edit%23slide%3Did.p& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&docs.google.com/present&/span&&span class=&invisible&&ation/d/1RgGVgLuP93mPZ0lqHhm7TOpxZBI3TEdAJQZzFqeleAE/edit#slide=id.p&/span&&span class=&ellipsis&&&/span&&/a& &/p&&p&我比较好奇的是 deno 使用了 protobuf,而没有使用 &a href=&//link.zhihu.com/?target=https%3A//chromium.googlesource.com/chromium/src/%2B/master/mojo/README.md& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Mojo&/a&。既然目标是要兼容浏览器,却不使用 Mojo,而是要在 protobuf 上重新造轮子,可见 Ryan Dahl 是真正的“轮子哥”啊。但是从 issue 中可以看出,Ryan Dahl 之前是没有听说过 Mojo 的,但是他看完 mojo 之后,依然觉得 protobuf 的选择是正确的。&/p&&p&Mojo 是 Google 开发的新一代 IPC 机制,用以替换旧的 Chrome IPC。目前 Chrome 的最新版本是 67,而 Google 的计划是在 2019 年的 75 版本用 mojo 替换掉所有的旧的 IPC。&/p&&p&Mojo 的思路确实和 protobuf 毕竟像,毕竟都是 Google 家的。旧的 IPC 系统是基于在 2 个进程(线程)之间的命名管道(IPC::Channel)实现的。这个管道是一个队列,进程间的 IPC 消息按照先进先出的顺序依次传递,所以不同的 IPC 消息之间有先后次序的依赖。相比之下,Mojo 则为每一个接口创建了一个独立的消息管道,确保不同接口的 IPC 是独立的。而且为接口的创建独立的消息管道的代价也并不昂贵,只需分配少量的堆内存。&/p&&p&Mojo 的架构设计:&/p&&figure&&img src=&https://pic3.zhimg.com/50/v2-d31b5406bfdfc3fe14b5fd_b.jpg& data-size=&normal& data-rawwidth=&568& data-rawheight=&324& class=&origin_image zh-lightbox-thumb& width=&568& data-original=&https://pic3.zhimg.com/50/v2-d31b5406bfdfc3fe14b5fd_r.jpg&&&figcaption&来自官方文档:https://chromium.googlesource.com/chromium/src/+/master/mojo/README.md#system-overview&/figcaption&&/figure&&p&我们可以看一下 Chrome 引入 Mojo 之后的架构变化。&/p&&p&之前:&/p&&figure&&img src=&https://pic2.zhimg.com/50/v2-20b4affaebf_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&960& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&960& data-original=&https://pic2.zhimg.com/50/v2-20b4affaebf_r.jpg&&&/figure&&p&之后:&/p&&figure&&img src=&https://pic4.zhimg.com/50/v2-41887def2d_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&768& data-rawheight=&456& class=&origin_image zh-lightbox-thumb& width=&768& data-original=&https://pic4.zhimg.com/50/v2-41887def2d_r.jpg&&&/figure&&p&是不是有点微服务的感觉。&/p&&p&熟悉 Java 的 Spring 的可以明显看出这个&b&依赖倒置&/b&。Blink 本来是浏览器最底层的排版引擎,通过 Mojo,Blink 变成了要给中间模块。最近大热的 Flutter 也是基于 Mojo 架构的。&/p&&h2&4. TypeScript VS JavaScript&/h2&&p&deno 的介绍是一个安全的 TypeScript 运行环境。但是我们看源码就会发现,deno 集成进了一个 TypeScript 编译器,而入口文件中 &a href=&//link.zhihu.com/?target=https%3A//github.com/ry/deno/blob/b723ab126f5cc37b27d914e6cbdba9a4f9ce1c71/main.go%23L94-L97& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&ry/deno:main.go&/a&&/p&&div class=&highlight&&&pre&&code class=&language-text&&// It's up to library users to call
// deno.Eval(&deno_main.js&, &denoMain()&)
func Eval(filename string, code string) {
err := worker.Load(filename, code)
exitOnError(err)
} // It's up to library users to call
// deno.Eval(&deno_main.js&, &denoMain()&)
func Eval(filename string, code string) {
err := worker.Load(filename, code)
exitOnError(err)
&/code&&/pre&&/div&&p&使用 V8 运行的 deno_main.js 文件。是 JavaScript 而不是 TypeScript 。&/p&&p&在前面的分析中我们知道这会影响 deno 的初次启动速度。那么对于执行速度呢?从理论上,TypeScript 作为一种静态类型语言,编译完成的 JavaScript 代码会有更快的执行速度。我之前在《前端程序员应该懂点V8 知识》曾经提到过 V8 对于 JavaScript 性能提升有一项是 &b&Type feedback。&/b&&/p&&p&当 V8 执行一个函数时,会基于函数传入的实参(注意是实参,而不是形参,因为 JavaScript 的形参是没有类型的)进行即时编译(JIT):&/p&&figure&&img src=&https://pic2.zhimg.com/50/v2-4cc5a540b84d9abdf3280def863f83c1_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1248& data-rawheight=&669& class=&origin_image zh-lightbox-thumb& width=&1248& data-original=&https://pic2.zhimg.com/50/v2-4cc5a540b84d9abdf3280def863f83c1_r.jpg&&&/figure&&p&但是当后面再次以不同的类型调用函数时,V8 会进行&b&去优化(Deopt)&/b&操作。&/p&&p&(将之前优化完的结果去掉,称为“去优化”)&/p&&figure&&img src=&https://pic4.zhimg.com/50/v2-2b7aa69b7f487f1fdfd0ae_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1167& data-rawheight=&651& class=&origin_image zh-lightbox-thumb& width=&1167& data-original=&https://pic4.zhimg.com/50/v2-2b7aa69b7f487f1fdfd0ae_r.jpg&&&/figure&&p&但是如果我们使用 TypeScript ,所有的参数都是由类型标注的,因此可以防止 V8 引擎内部执行去优化操作。&/p&&h2&5. 对 deno 性能的展望和猜想&/h2&&p&虽然 TypeScript 可以避免 V8 引擎的去优化操作,但是 V8 执行的是 ts 编译后的结果,我们通过字节码或者机器码可以看到,V8 依然生成了 Type Check 的代码,每次调用函数之前,V8 都会对实参的类型进行检查。也就是说,虽然 TypeScript 保证了函数的参数类型,但是编译成 JavaScript 之后,V8 并不能确定函数的参数类型,只能通过每次调用前的检查来保证参数的类型。&/p&&p&其次,当 V8 遇到函数定义时,并不知道参数的类型,而只有函数被调用后,V8 才能判断函数的类型,才对函数进行 Typed 即时编译。这里又有一个矛盾了,typescript 在函数定义时就已经知道了形参的类型,而 V8 只有在函数调用时才根据实参的类型进行优化。&/p&&p&所以,目前 deno 的架构还存在很多问题,毕竟只是一个 demo。未来还有很多方向可以优化。&/p&&p&V8 是一个 JavaScript 运行时,而 deno 如果定义为“安全的 TypeScript 运行时”,至少在目前的架构上,性能是有很大损失的。但是目前还不存在一个 TypeScript 运行时,退而求其次只能在 V8 前面放一个 TypeScript 编译器了。&/p&&p&执行流程是这样的:&/p&&figure&&img src=&https://pic3.zhimg.com/50/v2-8d3b1dadad4_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1253& data-rawheight=&294& class=&origin_image zh-lightbox-thumb& width=&1253& data-original=&https://pic3.zhimg.com/50/v2-8d3b1dadad4_r.jpg&&&/figure&&figure&&img src=&https://pic4.zhimg.com/50/v2-827c7b277da2cde3a6a49ecc3ff853c4_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1418& data-rawheight=&533& class=&origin_image zh-lightbox-thumb& width=&1418& data-original=&https://pic4.zhimg.com/50/v2-827c7b277da2cde3a6a49ecc3ff853c4_r.jpg&&&/figure&&p&虽然我在项目中没有使用过 TypeScript
,但是基本上我在项目里面写的第三方库都会提供一d.ts 文件。目前 TypeScript 最大的用途还是体现在开发和维护过程中。&/p&&p&我们想到的一个方式就是 fork 一份 V8 的源码,然后把编译流程整合进去。TypeScript 在编译为 JavaScript 的过程中也需要一份 AST,然后生成 js 代码。V8 执行 js 代码是再 parse 一份 AST,基于 AST 生成中间代码(ByteCode)。如果 TypeScript 可以直接生成对用的字节码则会提升运行时的性能。&/p&&p&不过 Ryan Dahl 大概不会这么干。但是也未必,毕竟社区已经把 TypeScript 的一个子集编译为 WebAssembly 了。&/p&&p&之前微软的 JScript 和 VBScript 在和 JavaScript 的竞争中败下阵来,而现在 TypeScript 势头正猛。虽然对 ES 规范的兼容束缚了 TypeScript 的发展,但很期待微软可以提供一个 TS 运行时,或者在 Chakra 引擎增加对 TS 运行时的支持。&/p&&p&(哪位大佬可以 fork 一份,然后做个 denoplus 或者 edon、oned 什么的)&/p&&h2&6. 总结&/h2&&p&不论如何,deno 是一个非常伟大的项目,但却不是“下一代 Node.js”。&/p&&p&&br&&/p&&hr&&p&&br&&/p&&p&PS:昨天 Ryan Dahl 在 JS Conf 做了《&a href=&//link.zhihu.com/?target=http%3A//tinyclouds.org/jsconf2018.pdf& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Design Mistakes in Node&/a&》的演讲,目前只有 PPT,还没有 Youtube 视频。而 8 年前的 2009 年,Ryan Dahl 也在 JS Conf 做了一次演讲,这次演讲诞生了 Node.js。&/p&&p&PPT:&a href=&//link.zhihu.com/?target=http%3A//tinyclouds.org/jsconf.pdf& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&tinyclouds.org/jsconf.p&/span&&span class=&invisible&&df&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&YouTube:&a href=&//link.zhihu.com/?target=https%3A//www.youtube.com/watch%3Fv%3Djo_B4LTHi3I& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Introduction to Node.js with Ryan Dahl&/a&&/p&
这周末读了一遍 deno 的源码,特意过来回答一下。长文预警(5000字,11图)。0. 为什么开发 Deno?这是我上周做的一张图,介绍了 JavaScript 的发展简史。刚才修改了一下,添加了对 Node.js 和 Deno 发布时间的标注。Node.js 和 Deno 分别是 Ryan Dahl 在 2…
&p&CMake 可以说是进化版本的autotools. autotools知道的人可能不多, 但configure/make/make install这个命令序列很多人都知道. 它是autotools配置工程的标准步骤,很多不是autotools配置的工程也用这个序列.&/p&&p&CMake主要改进了两点, 一是支持windows等非unix系统, 二是支持多种构建工具, 而不是只支持make. &/p&&p&为了支持windows等非unix系统. 原本autotools的configure部分是一个bash脚本, 改成了cmake命令, 同时cmake还实现了很多windows系统上不常见的工具. 参考如下:&/p&&a href=&//link.zhihu.com/?target=https%3A//cmake.org/cmake/help/v3.11/manual/cmake.1.html%23command-line-tool-mode& data-draft-node=&block& data-draft-type=&link-card& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Command-Line Tool Mode&/a&&p&使用这些工具以及CMake的脚本能力, 可以完全自动检查并配置出一个适合工程的编译环境. 比如说发现系统上有哪些编译器,哪些程序库,哪些API可用. 这是C/C++编译的第一步. &/p&&p&为了支持多种构建工具, cmake的configure这一步会生成一个原生编译系统的脚本. 比如VS IDE的工程.这是第二步.&/p&&p&从这个角度来说, CMake适合做为主构建工具, 它的构建模型非常简单,只有输出目标,有利于其它的IDE工程或Makefile映射.&/p&&p&CMake的描述语言是写在CMakelists.txt中, 注意它是描述语言, 并不是命令式的执行, 并没有计算结果, 它的脚本并不是那么直观.&/p&&h2&快速的说明&/h2&&p&CMake工程非常简单, 就是一堆输出, 包括binary target和pseudo target, 相应的说明在这里: &/p&&a href=&//link.zhihu.com/?target=https%3A//cmake.org/cmake/help/v3.11/manual/cmake-buildsystem.7.html& data-draft-node=&block& data-draft-type=&link-card& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&cmake-buildsystem&/a&&p&大多数情况下, 不需要也不应该干涉输出的生成过程. 但确实可以定义custom_target.&/p&&p&输出之间可以有依赖, 通过target_link_libraries来说明. &/p&&p&简单的情况下,只需要说明工程最终的可执行文件和库就完成了. &/p&&p&复杂的情况请看下面:&/p&&h2&如何指定编译选项?&/h2&&p&C++工程习惯上所有输出文件都使用相同的编译选项, 宏和链接选项. 直接指定这三个名字INCLUDE_DIRECTORIES, COMPILE_DEFINITIONS 和 COMPILE_OPTIONS.&/p&&p&虽然不推荐, 但是也可以特定的输出指定编译选项, 宏和链接选项, 使用target_xxx开头的指令.&/p&&h2&如何引入第三方编译的库?&/h2&&p&最简单你可直接把它加在编译选项上, 很多FindPackage.cmake就是这么做的.&/p&&p&也可以使用相对模块化的import target和interface target. 前者适合有lib/dll的第三方库, 后者适合只有头文件的库.&/p&&h2&如何配置?&/h2&&p&这是继承autoconf的功能, 它基本上完全复制了功能, 减少了使用M4和Bash的实现.&/p&&p&分成两个部分, 一是C++范围内, 通过configure_file定义宏, 让C++读到宏的值. 通过变更编辑选项让C++编译器知道这部分变化.&/p&&p&二是环境检查的部分. CMake知道很多环境信息, 也可以通过FindXXX主动检查, 甚至可以通过编译测试程序来检查是否支持某个API. 这些检查信息可以保存在一个类似环境变量中. 如果需要用户指定一部分配置, 可以通过option命令描述用户选项.&/p&&p&&/p&&p&&/p&
CMake 可以说是进化版本的autotools. autotools知道的人可能不多, 但configure/make/make install这个命令序列很多人都知道. 它是autotools配置工程的标准步骤,很多不是autotools配置的工程也用这个序列.CMake主要改进了两点, 一是支持windows等非unix系统,…
伟大的protobuf、leveldb、re2都是google开源的吧,不过都是C++03。还有著名的v8。&br&要学C++,还是觉得facebook的更赞,看看folly,wangle,都是标准库的水准。代码都是C++11 14的典范。&br&个人看过并山寨了folly的future库,启发很大。看上去模板很唬人,其实最主要就是sfinae。还有需要对右值有深刻理解,promise,future都只是movable的。&br&Leveldb 完整读过,从语言层面,根本不是好的C+代码,但思想熠熠生辉。&br&总得来说,谷歌对c++语言本身是没有什么影响的,当然对推广有贡献。
伟大的protobuf、leveldb、re2都是google开源的吧,不过都是C++03。还有著名的v8。 要学C++,还是觉得facebook的更赞,看看folly,wangle,都是标准库的水准。代码都是C++11 14的典范。 个人看过并山寨了folly的future库,启发很大。看上去模板很唬人,其实…
谢邀。我能想得起来的有这些:&br&&br&以独立进程运行的:&br&1. MySQL/MariaDB/PerconaDB C++&br&2. PostgreSQL C++ &a href=&//link.zhihu.com/?target=http%3A//www.postgresql.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&PostgreSQL: The world's most advanced open source database&/a&&br&3. MongoDB
C++ &a href=&//link.zhihu.com/?target=https%3A//github.com/mongodb/mongo& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&GitHub - mongodb/mongo: The Mongo Database&/a&&br&4. RethinkDB C++ &a href=&//link.zhihu.com/?target=http%3A//rethinkdb.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&RethinkDB: the open-source database for the realtime web&/a&&br&5. Redis C &a href=&//link.zhihu.com/?target=http%3A//redis.io/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Redis&/a&&br&6. Kyoto Tycoon C++ &a href=&//link.zhihu.com/?target=http%3A//fallabs.com/kyototycoon/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&fallabs.com/kyototycoon&/span&&span class=&invisible&&/&/span&&span class=&ellipsis&&&/span&&/a&&br&&br&以库的形式提供的:&br&1. SQLite C &a href=&//link.zhihu.com/?target=https%3A//www.sqlite.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&SQLite Home Page&/a&&br&2. LevelDB C++ &a href=&//link.zhihu.com/?target=https%3A//github.com/google/leveldb& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&GitHub - google/leveldb: LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.&/a&&br&3. RocksDB C++ &a href=&//link.zhihu.com/?target=https%3A//github.com/facebook/rocksdb& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&GitHub - facebook/rocksdb: A library that provides an embeddable, persistent key-value store for fast storage.&/a&&br&4. Tokyo Cabinet C &a href=&//link.zhihu.com/?target=http%3A//fallabs.com/tokyocabinet/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&fallabs.com/tokyocabine&/span&&span class=&invisible&&t/&/span&&span class=&ellipsis&&&/span&&/a&
谢邀。我能想得起来的有这些: 以独立进程运行的: 1. MySQL/MariaDB/PerconaDB C++ 2. PostgreSQL C++
3. MongoDB C++
4. RethinkDB C++
&p&我竟然刷到了一个C语言的问题……而且我竟然无聊到把它回答了……&/p&&p&下面正文:&/p&&p&个人觉得这篇文章&a href=&//link.zhihu.com/?target=http%3A//unixwiz.net/techtips/reading-cdecl.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Reading C type declarations&/a&说的不错,推荐阅读。&/p&&p&另外就是Expert C Programming中有一张图,也告诉你了如何阅读type declaration,同时还有很多文字内容举例告诉你具体如何阅读它。&/p&&p&这其实是C语言的一个不太好的设计哲学,现代编程语言如Scala/Haskell等都将类型通过说明符的方式统一写在后面(或前面),这样能够给类型阅读提供方便。不过C语言的设计哲学是如何使用就如何定义,所以阅读起来需要从declarator中间开始一会向左一会儿向右最后在type specifier结束……&/p&&p&好的,我们看图,希望你可以把它背下来,这样之后对你阅读和书写类型声明很有帮助。(另外再推荐这本Expert C Programming,作为阅读完C Primer Plus之后的阅读材料,然后你可以看再C语言之父的The C Programming Language)&/p&&figure&&img src=&https://pic3.zhimg.com/50/v2-f3a739ff46f5ceb13c54f_b.jpg& data-rawwidth=&584& data-rawheight=&654& class=&origin_image zh-lightbox-thumb& width=&584& data-original=&https://pic3.zhimg.com/50/v2-f3a739ff46f5ceb13c54f_r.jpg&&&/figure&&p&这张图明确说明了你如何阅读一个类型,那么回到你的问题,(*a)[N],这个声明并不完整,我将它改为int (*a)[N];,然后我们一步一步来:&/p&&ol&&li&第1步,找到最左侧的identifier,说identifier is。&/li&&li&第2步,已经分析的符号的右侧有方括号吗?没有。(注意是a的右侧紧挨着的非空白字符,在此例中,是“)”)&/li&&li&第3步,已经分析的符号的右侧有左括号嘛?没有。&/li&&li&第4步,已经分析的符号左侧是左括号吗?不是。&/li&&li&第5步,左侧符号是const, volatile, *之一嘛?是的,说pointer to,然后再次回到步骤4。&/li&&li&第4步,已经分析的符号左侧是左括号吗?是的,这是一个括号将我们已经阅读和处理的declaration进行grouping的括号,找到对应的右括号然后回到步骤2。&/li&&li&第2步,已经分析的符号右侧有方括号吗?有(此时已经分析的符号是(*a),右侧有方括号),从左到右,对于每对方括号,说array of。&/li&&li&第3步,同(3),略&/li&&li&(4), (5),没有&/li&&li&第6步,左侧剩余的类型组成了declaration的标准类型,阅读剩余的类型,说int&/li&&/ol&&p&将以上分析过程中我们说的东西按顺序组合起来:
identifier is pointer to array of int
我们优化一下语法组成一个句子:
The identifier &a& is a pointer to an array of int.
很好,翻译成中文:
标识符a是一个指向一个int数组的指针。&/p&&p&那么,对于类似的*a[N],将其变为int *a[N];以补全declaration,我省略分析过程直接说结果:
The identifier &a& is an array of a pointer to int.
翻译一下就是:
标识符a是一个int指针的数组。&/p&&p&注意这个declaration方式不仅在C语言中有效,你也可以用类似的方法阅读C++的declaration。同时还有一点需要注意,有一些declaration是非法的,引用Expert C Programming:&/p&&figure&&img src=&https://pic4.zhimg.com/50/v2-d6eb04c26f876d17a53edeca_b.jpg& data-rawwidth=&582& data-rawheight=&292& class=&origin_image zh-lightbox-thumb& width=&582& data-original=&https://pic4.zhimg.com/50/v2-d6eb04c26f876d17a53edeca_r.jpg&&&/figure&&p&最后的最后,我想让你现在就去读一下Expert C Programming的第三章,Unscrambling Declarations in C。&/p&&br&&blockquote&C code. C code run. Run code run…please! —Barbara Ling &/blockquote&
我竟然刷到了一个C语言的问题……而且我竟然无聊到把它回答了……下面正文:个人觉得这篇文章说的不错,推荐阅读。另外就是Expert C Programming中有一张图,也告诉你了如何阅读type declaration,同时还有很多文字内容举例告诉…
&p&在一线做C++服务器开发有些年头了,我也写了关于C++服务器开发的一系列心得和总结,看这里:&/p&&a href=&https://www.zhihu.com/question//answer/& data-draft-node=&block& data-draft-type=&link-card& data-image=&https://pic4.zhimg.com/v2-0ad2a037c476ax120.jpg& data-image-width=&1664& data-image-height=&1006& class=&internal&&epoll编程,如何实现高并发服务器开发?&/a&&p&更多的技术文章可以关注我的微信公众号【easyserverdev】,这个号的初心是:让服务器开发技术平民化,帮助新手入门与提高。&/p&
在一线做C++服务器开发有些年头了,我也写了关于C++服务器开发的一系列心得和总结,看这里:更多的技术文章可以关注我的微信公众号【easyserverdev】,这个号的初心是:让服务器开发技术平民化,帮助新手入门与提高。
已有帐号?
无法登录?
社交帐号登录
3050 人关注
366 条内容
2649 人关注
731 条内容
823 人关注
1855 条内容
1707 人关注
419 条内容

我要回帖

更多关于 x?z?p 的文章

 

随机推荐