以下是一些著名互联网企业的部分面试笔试真题以及考察知识点
本文的内容是对一些网址上的知识点介绍做了相应的整理
1.extern的作用
自己理解:应该需要区分extern在C语言中和C++语言中的作用,C语言中extern声明的函数和变量可以被该文件外部模块引用,C++语言中除了该作用还可以声明extern “C”声明一段代码编译连接的方法为C语言的方法。
参考:其实extern的百度词条解释的很清楚,具体的也是跟我上面自己理解差别不是很大。
(a) extern是C/C++语言中声明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量在本模块或其他模块中使用(通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。)
(b) 被extern “C”修饰的变量和函数是按照C语言的方式编译和链接的。(C语言不支持函数重载,所以函数的C++和C的编译方式不同,这一句的作用就是实现C++和C及其他语言混合编程)
extern的使用问题
(1)extern 变量
在一个源文件里定义了一个数组:char a[6];在另外一个文件里用下列语句进行声明:extern char *a; 问:这样可以吗?
答案与分析:
不可以,程序运行时会告诉你非法访问。原因在于,指向类型 char的指针并不等价于类型char的数组。extern char *a声明的是一个指针变量而不是一个字符数组,因此,与实际的定义不同,从而造成运行时非法访问。应将声明改为 extern char a[];
(2)单方面修改extern函数原型
当函数提供方面单方面修改函数原型时,如果使用不知情继续沿用原来的extern声明,这样编译时编译器不会报错。但是在运行过程中,因为少输入或者是多输入参数,往往会造成系统错误,这种情况如何解决?
答案与分析:目前业界针对这种情况的处理没有一个很完美的方案,通常的做法是提供方在自己的xxx_pub.h 中提供对外部接口的声明,然后调用方include该头文件,从而省去extern这一步。避免错误
(3)extern ’C‘
在C++环境下使用C函数的时候,常常会出现编译器无法找到obj模块中的C函数定义,从而导致链接失败的情况,应该如何解决这种情况呢?
答案与分析:C++在编译的时候,为了解决函数的多态问题,会将函数名和参数联合起来生成一个中间的函数名称,而C语言则不会,因此会造成链接时找不到对应函数的情况,此时C函数就需要用extern ’C‘进行链接指定,这告诉编译器,请保持我的名称,不要给我生成用于链接的中间函数名。
(4)extern函数声明
常见extern放在函数前面成为函数声明的一部分,那么,C语言的关键字extern在函数的声明中起什么作用?
答案与分析:如果函数的声明中带有关键字extern,仅仅是暗示这个函数可能在别的源文件里定义,没有其他作用。
(5)extern和static
A、extern表明该变量在别的地方已经定义过了,在这里要使用那个变量
B、static表示静态的变量,分配内存的时候,存储在静态区,不存储在栈上面
static作用范围是内部连接的关系,和extern有点相反,它和对象本身是分开存储的,extern也是分开存储的,但是extern可以被其他的对象用extern引用,而static不可以,只允许对象本身用它。具体的差别,首先static和extern不能同时修饰一个变量;其次,static修饰全局变量声明与定义同时进行;最后,static修饰全局变量的作用域只能是本身的编译单元,也就是说它的“全局”只对本编译单元有效,其他编译单元则看不到它。
一般定义static全局变量时,都把它放在源文件中而不是头文件
(6)extern和const
C++中const修饰的全局常量具有跟static相同的特性,它们只能作用于本编译模块中,但是const可以与extern连用来声明该常量可以作用于其他编译模块中
2.strstr()函数的作用
strstr()函数的原型一般为extern char * strstr(const char *src , const char *dest) , 其作用就是判断字符串dest是否是src的子串,如果是,则返回目标字符串在源字符串中第一次出现的位置(地址,返回的是个指针)。否则,返回NULL
3.windows线程优先级问题( 进程和线程的区别和联系 )
参考网址【3】 进程和线程关系及区别
(a)进程,是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念。每个进程都有个自己的地址空间,即进程空间或(虚空间)。进程至少有5种基本状态,它们是:初始态、执行态、等待状态,就绪状态,终止状态。
(b)线程,在网络或多用户环境下,一个服务器通常需要接收大量且不确定数量用户的并发请求,为每一个请求都创建一个进程显然是行不通的,——无论是从系统资源开销方面或是响应用户请求的效率方面来看。因此,操作系统中线程的概念便被引进了。线程,是进程的一部分,一个没有线程的进程可以被看作是单线程的。线程有时又被称为轻权进程或轻量级进程,也是 CPU 调度的一个基本单位。
通常一个进程可以包含若干个线程,它们可以利用进程所拥有的资源。进程是系统进行资源分配和调度的一个独立单位,线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本不拥有系统资源,只拥有一些在运行中必不可少的资源(如程序计数器,一组寄存器和栈),线程可与同属于一个进程的其他线程共享进程所拥有的全部资源。
线程和进程区别归纳:
- 地址空间和其他资源:进程间互相独立,同一个进程的各线程共享。
- 通信:进程间通信IPC,线程间可以直接读写进程序数据段(如全局变量)来进行通信-需要进行同步和互斥的辅助。
- 调度和切换:线程上下文切换比进程上下文切换快速,高效。
- 多线程的OS中,进程不是一个可执行的实体。
4.多方法交换x与y的值
(1)不借助第三方变量交换x和y的值 (x=x+y,y=x-y;x=x-y)
(2)用函数实现,参数是引用 void swap( int &x,int &y )
(3)用函数实现,参数是指针 void swap( int *x,int *y)
5.指针的自加与引用
假设指针指向的是一个整型数组(int a[]={1,2,3,4,5}; int *p = a;),那么,指针p的地址是数组a的首地址,而指针p++,自加之后,p所指的是a[1]的地址。
指针的引用相当于传递的是:指针的指针,这样,指针的数值可以进行改变。
6.前置++与后置++
对于迭代器和其他模板对象使用前缀形式 (++i) 的自增, 自减运算符.,理由是 前置自增 (++i) 通常要比后置自增 (i++) 效率更高。
int a = 0;
++ a; //前置++
a++; //后置++
《C专家编程》中有如下描述(P276,人民邮电出版社):
++a表示取a的地址,增加它的内容,然后把值放在寄存器中;
a++表示取a的地址,把它的值装入寄存器,然后增加内存中的a的值;
有篇博客讲的挺清楚的, 前置++和后置++的区别 - randyjiawenjie的专栏 - 博客频道 - CSDN.NET 前置++和后置++的区别
7.inline的作用
inline函数不像正常函数在调用时存在压栈和call的操作,它会把程序代码直接嵌入到调用代码段中,也就是说使用inline函数会增大二进制程序的体积,但是会使执行速度加快。
同时,编译期间可以对参数进行强类型的检查,这是inline优于宏的一个方面。
详细的解释见 关于C/C++中的inline
8.二维数组的表示
(1)数组的形式 int a[3][5];
(2)指针的形式 int *p = malloc(sizeof(int )*3*5);
9.ifndef的作用
条件编译的语法,一般情况下,源程序中所有的行都参加编译。但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是“条件编译”。有时,希望当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。
关于在头文件中的详细应用,看 参考网址[5] #ifndef/#define/#endif
10.KMP算法
字符串匹配的高级算法(具体的讲解和实现见blog 字符串匹配(KMP算法 含代码))
11.函数调用方式
- __cdecl 堆栈由调用者清除 参数从右至左的顺序压入堆栈内函数名自动加前导下划线
- __stdcall 堆栈由被调用者清除 参数从右至左的顺序压入堆栈内函数名自动加前导下划线,后面紧跟着一个@,其后是参数的尺寸
- __fastcall 堆栈由被调用者清除 部分参数保存在寄存器中,然后其他的压入堆栈内函数名自动加前导下划线,后面紧跟着一个@,其后是参数的尺寸
- thiscall(非关键字) 参数从右向左压入栈。如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定,this指针在所有参数亚茹栈后被压入栈。参数个数不定的,由调用者清理堆栈,否则由函数自己清理堆栈。
12.重载函数
函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数。不能利用返回类型进行重载!类中函数const和非const可以进行重载,其实原理是利用this指针的类型是const和非const进行重载,其实原理就是参数类型不同,const指针or const引用调用的为const版本的函数~更多函数重载的知识。
13.构造函数和析构函数
用new建立一个C++类的对象,并进行delete
MyClass* pMyClass = new MyClass;
pMyClass->~MyClass();
delete pMyClass;
在new的时候,做了三件事
(1)调用::operator new分配所需内存
(2)调用构造函数
(3)返回指向新分配并构造的对象的指针
在delete的时候,做了两件事
(1)调用析构函数
(2)调用::operator delete释放内存
C++中的new operator new 与placement new
在子类和父类中,构造和析构函数的调用顺序
析构函数的调用顺序是先基类在派生类,构造函数的调用顺序是先派生类再基类
构造函数和析构函数 - `leisure - 博客园 http://www.cnblogs.com/chenyang920/p/5267138.html
虚拟析构函数的使用场景是指向父类的指针实则为子类指针,调用delete的时候使用虚拟析构函数,防止部分内存泄露。
构造函数不能声明为虚拟函数,因为对象的虚拟函数表的指针其实是在构造函数内编译器添加完成的代码,所以在构造函数执行之前无法访问到虚拟函数表的。
14.合并两个有序链表
类似归并排序,两个指针归并即可。 两个有序单链表的合并
15.100亿条记录的文本文件,取出重复数最多的前10条
这个问题关于海量数据的处理(具体的实现。。。)
360 php 面试题 - 开源中国社区 http://www.oschina.net/question/163919_61165?sort=time&#answers
大数据处理--hash的使用 - bluesky的日志 - 网易博客 http://zhangyongbluesky.blog.163.com/blog/static/183194162012525952318/
【程序员面试宝典】有1千万条短信,找出重复出现最多的前10条_Leora_新浪博客 http://blog.sina.com.cn/s/blog_7124c26901014zcl.html
16.设计一个双向链表,并且提供一个可根据值删除元素的函数
STL(Standard Template Library 标准模板库)中的list底层及为双链表实现。
17.二叉树的多种遍历算法实现
有个blog 写的非常详细,先是介绍了数与二叉树,然后写了非常详细的代码
先中后序遍历,广度优先遍历,深度优先遍历
[数据结构与算法]二叉树的多种遍历方式 - SAP师太 - 博客园 http://www.cnblogs.com/jiangzhengjun/p/4289830.html
18.有读和写的两个线程和一个队列,读线程从队列中读数据,写线程往队列中写数据
生产者和消费者模型:
使用信号灯和互斥量。
1
2
3
4
5
6
|