线程是指进程内的一个执行单元,也是进程内的可调度实体.与进程的区别:(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行(3)拥有资源:进程是拥有资源的独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源. (4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
人工测试:个人复查、抽查和会审机器测试:黑盒测试和白盒测试
Heap是堆,stack是栈。Stack的空间由操作系统自动分配/释放,Heap上的空间手动分配/释放。Stack空间有限,Heap是很大的自由存储区C中的malloc函数分配的内存空间即在堆上,C++中对应的是new操作符。程序在编译期对变量和函数分配内存都在栈上进行,且程序运行过程中函数调用时参数的传递也在栈上进行
5.客户端如何访问.Net组件实现Web Service?
小页(4K)两级分页模式,大页(4M)一级
一个递增一,一个递增二,他们指向同一个接点时就是环出现的地方 ??
通过调用门,从ring3到ring0,中断从ring3到ring0,进入vm86等等
用内存映射或全局原子(互斥变量)、查找窗口句柄.. FindWindow,互斥,写标志到文件或注册表,共享内存。.
键盘钩子SetWindowsHookEx
存储过程(Stored Procedure)是一组为了完成特定功能的SQL 语句集,经编译后存储在数据库。中用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。
存储过程用于实现频繁使用的查询、业务规则、被其他过程使用的公共例行程序
存储过程在创建时即在服务器上进行编译,所以执行起来比单个 SQL 语句快
1,进程:子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。2,线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。区别:两者都可以提高程序的并发度,提高程序运行效率和响应时间。线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。
(1)冒泡排序; (2)选择排序; (3)插入排序; (4)快速排序; (5)堆排序; (6)归并排序;
struct mybitfields { unsigned short a : 4; unsigned short b : 5; unsigned short c : 7; }test
void main(void) { int i; test.a=2; test.b=3; test.c=0;
i=*((short *)&test); printf("%d/n",i); }
unsigned int i=3; cout<<i * -1;
int a; int b; int c;
void F1() { b=a*2; a=b; }
void F2() { c=a+1; a=c; }
main() { a=5; //Start F1,F2 in parallel F1(); F2(); printf("a=%d/n",a); }
(1)Byte转换为RGB时,保留高5、6bits; (2)RGB转换为Byte时,第2、3位置零。
(1)增加一个元素; (2)获得头元素; (3)弹出头元素(获得值并删除)。
1。In C++, what does "explicit" mean? what does "protected" mean?
c++中的explicit关键字用来修饰类的构造函数,表明该构造函数是显式的,在某些情况下,我们要求类的使用者必须显示调用类的构造函数时就需要使用explicit,反之默认类型转换可能会造成无法预期的问题。
protected控制的是一个函数对一个类的成员(包括成员变量及成员方法)的访问权限。protected成员只有该类的成员函数及其派生类的成员函数可以访问
1. 在C++中有没有纯虚构造函数?
构造函数不能是虚的。只能有虚的析构函数
在C++类的成员变量被声明为static(称为静态成员变量),意味着它为该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,也就是说不管创建多少对象,static修饰的变量只占有一块内存。其修改值为该类的其它所有实例所见;而类的静态成员函数也只能访问静态成员(变量或函数)。
static是加了访问控制的全局变量,不被继承。
4。如何实现一个非阻塞的socket?
5。setsockopt, ioctl都可以对socket的属性进行设置,他们有什么不同? (linux)
6。解释一下进程和线程的区别? (重复,参见微软亚洲技术中心笔试)7。解释一下多播(组播)和广播的含义?
组播:主机之间“一对一组”的通讯模式,也就是加入了同一个组的主机可以接受到此组内的所有数据,网络中的交换机和路由器只向有需求者复制并转发其所需数据。主机可以向路由器请求加入或退出某个组,网络中的路由器和交换机有选择的复制并传输数据,即只将组内数据传输给那些加入组的主机。
广播:主机之间“一对所有”的通讯模式,网络对其中每一台主机发出的信号都进行无条件复制并转发,所有主机都可以接收到所有信息(不管你是否需要).
8。多播采用的协议是什么?
9。在c++中纯虚析构函数的作用是什么?请举例说明。
10。编程,请实现一个c语言中类似atoi的函数功能(输入可能包含非数字和空格)
一、选择题:15分 共10题 1. 在排序方法中,关键码比较次数与记录地初始排列无关的是 . A. Shell排序 B. 归并排序 C. 直接插入排序 D. 选择排序
2. 以下多线程对int型变量x的操作,哪几个需要进行同步: A. x=y; B. x++; C. ++x; D. x=1;
3. 代码 void func() { static int val; … } 中,变量val的内存地址位于: A. 已初始化数据段 B.未初始化数据段 C.堆 D.栈
4. 同一进程下的线程可以共享以下 A. stack B. data section C. register set D. thread ID
5. TCP和IP分别对应了 OSI中的哪几层? A. Application layer B. Data link layer C. Presentation layer D. Physical layer E. Transport layer F. Session layer G. Network layer
6. short a[100],sizeof(a)返回? A 2 B 4 C 100 D 200 E 400
7. 以下哪种不是基于组件的开发技术_____。 A XPCOM B XP C COM D CORBA
8. 以下代码打印的结果是(假设运行在i386系列计算机上): struct st_t { int status; short* pdata; char errstr[32]; };
st_t st[16]; char* p = (char*)(st[2].errstr + 32); printf("%d", (p - (char*)(st)));
A 32 B 114 C 120 D 1112
9. STL中的哪种结构是连续形式的存储 A map B set C list D vector
10. 一个栈的入栈序列是A,B,C,D,E,则栈的不可能的输出序列是( ) A、EDCBA; B、DECBA; C、DCEAB; D、ABCDE
参考答案:D /ABC/ A/ BC /EG /D /B/ C/ D/ C
二、简答题:20分,共2题
1. (5分)重复多次fclose一个打开过一次的FILE *fp指针会有什么结果,并请解释。 考察点:导致文件描述符结构中指针指向的内存被重复释放,进而导致一些不可预期的异常。
2. (15分)下面一段代码,想在调用f2(1)时打印err1,调用f2(2)时打印err4,但是代码中有一些问题,请做尽可能少的修改使之正确。 1 static int f1(const char *errstr, unsigned int flag) { 2 int copy, index, len; 3 const static char **__err = {“err1”, “err2”, “err3”, “err4”}; 4 5 if(flag & 0x10000) 6 copy = 1; 7 index = (flag & 0x300000) >> 20; 8 9 if(copy) { 10 len = flag & 0xF; 11 errstr = malloc(len); 12 if(errstr = NULL) 13 return -1; 14 strncpy(errstr, __err[index], sizeof(errstr)); 15 } else 16 errstr = __err + index; 17 } 18 19 void f2(int c) { 20 char *err; 22 swtch(c) { 23 case 1: 24 if(f1(err, 0x110004) != -1) 25 printf(err); 26 case 2: 27 if(f2(err, 0x30000D) != -1) 28 printf(err); 29 } 30 }
三、编程题:30分 共1题 注意:要求提供完整代码,如果可以编译运行酌情加分。
1. 求符合指定规则的数。 给定函数d(n) = n + n的各位之和,n为正整数,如 d(78) = 78+7+8=93。 这样这个函数可以看成一个生成器,如93可以看成由78生成。 定义数A:数A找不到一个数B可以由d(B)=A,即A不能由其他数生成。现在要写程序,找出1至10000里的所有符合数A定义的数。 输出: 1 3 …
四、设计题:35分 共1题 注意:请尽可能详细描述你的数据结构、系统架构、设计思路等。建议多写一些伪代码或者流程说明。
1. 假设一个mp3搜索引擎收录了2^24首歌曲,并记录了可收听这些歌曲的2^30条URL,但每首歌的URL不超过2^10个。系统会定期检查这些URL,如果一个URL不可用则不出现在搜索结果中。现在歌曲名和URL分别通过整型的SONG_ID和URL_ID唯一确定。对该系统有如下需求: 1) 通过SONG_ID搜索一首歌的URL_ID,给出URL_ID计数和列表 2) 给定一个SONG_ID,为其添加一个新的URL_ID 3) 添加一个新的SONG_ID 4) 给定一个URL_ID,将其置为不可用
限制条件:内存占用不超过1G,单个文件大小不超过2G,一个目录下的文件数不超过128个。
为获得最佳性能,请说明设计的数据结构、搜索算法,以及资源消耗。如果系统数据量扩大,该如何多机分布处理?
Q:When speaking of software products, how do you define the term“quality”.问:当说到软件产品的时候,你如何定义术语“质量”
Meeting customer requirementsQ:What is the role of software debelopers and quality assuranle engineers in ensuring the quality of the product? How are other functional areas important to developing a quality product?问:在确定产品的质量方面,什么是软件开发工程师和质量保证工程师要做的?其他的功能对如何发展产品质量有什么重要?
软件测试是贯穿整个软件开发生命周期、对软件产品(包括阶段性产品)进行验证和确认的活动过程,其目的是尽快尽早地发现在软件产品中所存在的各种问题——与用户需求、预先定义的不一致性。Quality assuranle engineers: use a set of activities designed to ensure the development process to meet the requirements.Q:What is cyclomatic complexity?问:(这是一个复杂度模型吧)
一种代码复杂度的衡量标准,中文名称叫做圈复杂度。圈复杂度“用来衡量一个模块判定结构的复杂程度,数量上表现为独立现行路径条数,即合理的预防错误所需测试的最少路径条数,圈复杂度大说明程序代码可能质量低且难于测试和维护,根据经验,程序的可能错误和高的圈复杂度有着很大关系”。Q:What are black-box texing and white-box texting?问:什么是黑盒测试和白盒测试?
Q:The following function divides a by b and out put to c,returns -1 as error. Int divide (int a,int b,int c) List you test cases in a black-box testing.问:对 Int divide (int a,int b,int c)函数写出黑盒测试用例
先等价,后边界值
先等价,后边界值
a b c 预期 结果 g j 0 -1 2 2 10 2 3 2 3 4 2 2 5 6 6 6
IQ,EQ题
然后是英文的数据结构,c/c++,数据库,3D建模,软件工程,软件测试
题目几乎都是英文的
后面还要填表格,一张中文的,一张英文的
一般有三个步骤首先会笔试。笔试题分为EQ题和专业题,EQ题没什么好说的,大家自由发挥就行,不过其中有一些逻辑推理题要注意。专业题目全英文,考的内容很多,数据结构,C/C++语法,COM,3D建模,软件测试基础知识,如黑盒,白盒测试等,题不难,每方面有三到五题左右。笔试成绩达到一定分数过后才能进入第二轮人事部面试。这部分面试一般是五人一组在一房间里,面试人员会问很多问题,然后每个人做答,问题很随机,当时好像问了“最尊敬的人是谁,最难忘的事是什么,有何职业规划,如何处理与同事之间的关系矛盾等等”。这轮过后就到技术部面试,会问一些基础知识,如数据结构常用的有哪些,如何用C/C++写出其中的一种,面象对像有哪些特性,XML是什么等等,可能他会用英语问你这些问题,各位练下听力口语哈(当时俺被问的很郁闷)
COM:
(COM),是微软公司为了计算机工业的软件生产更加符合人类的行为方式开发的一种新的软件开发技术。在COM构架下,人们可以开发出各种各样的功能专一的组件,然后将它们按照需要组合起来,构成复杂的应用系统。由此带来的好处是多方面的:可以将系统中的组件用新的替换掉,以便随时进行系统的升级和定制;可以在多个应用系统中重复利用同一个组件;可以方便的将应用系统扩展到网络环境下;COM与语言,平台无关的特性使所有的程序员均可充分发挥自己的才智与专长编写组件模块;等等。COM是开发软件组件的一种方法。组件实际上是一些小的二进制可执行程序,它们可以给应用程序,操作系统以及其他组件提供服务。
开发自定义的COM组件就如同开发动态的,面向对象的API。多个COM对象可以连接起来形成应用程序或组件系统。并且组件可以在运行时刻,在不被重新链接或编译应用程序的情况下被卸下或替换掉。Microsoft的许多技术,如ActiveX, DirectX以及OLE等都是基于COM而建立起来的。并且Microsoft的开发人员也大量使用COM组件来定制他们的应用程序及操作系统。COM所含的概念并不止是在Microsoft Windows操作系统下才有效。COM并不是一个大的API,它实际上象结构化编程及面向对象编程方法那样,也是一种编程方法。在任何一种操作系统中,开发人员均可以遵循“COM方法”。 有的组件必须满足两个条件:第一,组件必须动态链接;第二,它们必须隐藏(或封装)其内部实现细节。动态链接对于组件而言是一个至关重要的要求,而消息隐藏则是动态链接的一个必要条件。对于COM来讲,接口是一个包含一个函数指针数组的内存结构。每一个数组元素包含的是一个由组件所实现的函数地址。
1.写出判断ABCD四个表达式的是否正确, 若正确, 写出经过表达式中 a的值(3分)
int a = 4;
(A)a += (a++); (B) a += (++a) ;(C) (a++) += a;(D) (++a) += (a++);
a = ?
答:C错误,左侧不是一个有效变量,不能赋值,可改为(++a) += a;
改后答案依次为9,10,10,11
2.某32位系统下, C++程序,请计算sizeof 的值(5分).
char *p = str ;
int n = 10;
请计算
sizeof (str ) = ?(1)
sizeof ( p ) = ?(2)
sizeof ( n ) = ?(3)
void Foo ( char str[100]){
请计算
sizeof( str ) = ?(4)
void *p = malloc( 100 );
请计算
sizeof ( p ) = ?(5)
答:(1)17 (2)4 (3) 4 (4)4 (5)4
3. 回答下面的问题. (4分)
(1).头文件中的 ifndef/define/endif 干什么用?预处理
答:防止头文件被重复引用
(2). #include 和 #include “filename.h” 有什么区别?
答:前者用来包含开发环境提供的库头文件,后者用来包含自己编写的头文件。
答:函数和变量被C++编译后在符号库中的名字与C语言的不同,被extern "C"修饰的变量和函数是按照C语言方式编译和连接的。由于编译后的名字不同,C++程序不能直接调用C 函数。C++提供了一个C 连接交换指定符号extern“C”来解决这个问题。
(4). switch()中不允许的数据类型是?
答:实型
4. 回答下面的问题(6分)
(1).Void GetMemory(char **p, int num){
*p = (char *)malloc(num);
void Test(void){
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
请问运行Test 函数会有什么样的结果?
答:输出“hello”
(2). void Test(void){
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str);
if(str != NULL){
strcpy(str, “world”);
printf(str);
请问运行Test 函数会有什么样的结果?
答:输出“world”
(3). char *GetMemory(void){
char p[] = "hello world";
return p;
void Test(void){
char *str = NULL;
str = GetMemory();
printf(str);
请问运行Test 函数会有什么样的结果?
答:无效的指针,输出不确定
5. 编写strcat函数(6分)
已知strcat函数的原型是char *strcat (char *strDest, const char *strSrc);
其中strDest 是目的字符串,strSrc 是源字符串。
(1)不调用C++/C 的字符串库函数,请编写函数 strcat
答:
VC源码:
char * __cdecl strcat (char * dst, const char * src)
char * cp = dst;
while( *cp )
cp++; /* find end of dst */
while( *cp++ = *src++ ) ; /* Copy src to end of dst */
return( dst ); /* return dst */
(2)strcat能把strSrc 的内容连接到strDest,为什么还要char * 类型的返回值?
答:方便赋值给其他变量
答:不是,其它数据类型转换到CString可以使用CString的成员函数Format来转换
7.C++中为什么用模板类。
答:(1)可用来创建动态增长和减小的数据结构
(2)它是类型无关的,因此具有很高的可复用性。
(3)它在编译时而不是运行时检查数据类型,保证了类型安全
(4)它是平台无关的,可移植性
(5)可用于基本数据类型
答:同步多个线程对一个数据类的同时访问
答:物理字体结构,用来设置字体的高宽大小
10.程序什么时候应该使用线程,什么时候单线程效率高。
答:1.耗时的操作使用线程,提高应用程序响应
2.并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求。
3.多CPU系统中,使用线程提高CPU利用率
4.改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。
其他情况都使用单线程。
答:见下一题
答:线程通常被定义为一个进程中代码的不同执行路线。从实现方式上划分,线程有两种类型:“用户级线程”和“内核级线程”。 用户线程指不需要内核支持而在用户程序中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。这种线程甚至在象 DOS 这样的操作系统中也可实现,但线程的调度需要用户程序完成,这有些类似 Windows 3.x 的协作式多任务。另外一种则需要内核的参与,由内核完成线程的调度。其依赖于操作系统核心,由内核的内部需求进行创建和撤销,这两种模型各有其好处和缺点。用户线程不需要额外的内核开支,并且用户态线程的实现方式可以被定制或修改以适应特殊应用的要求,但是当一个线程因 I/O 而处于等待状态时,整个进程就会被调度程序切换为等待状态,其他线程得不到运行的机会;而内核线程则没有各个限制,有利于发挥多处理器的并发优势,但却占用了更多的系统开支。 Windows NT和OS/2支持内核线程。Linux 支持内核级的多线程
13.C++中什么数据分配在栈或堆中,New分配数据是在近堆还是远堆中?
答:栈: 存放局部变量,函数调用参数,函数返回值,函数返回地址。由系统管理
堆: 程序运行时动态申请,new 和 malloc申请的内存就在堆上
14.使用线程是如何防止出现大的波峰。
答:意思是如何防止同时产生大量的线程,方法是使用线程池,线程池具有可以同时提高调度效率和限制资源使用的好处,线程池中的线程达到最大数时,其他线程就会排队等候。
15函数模板与类模板有什么区别?
答:函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化必须由程序员在程序中显式地指定。
16一般数据库若出现日志满了,会出现什么情况,是否还能使用?
答:只能执行查询等读操作,不能执行更改,备份等写操作,原因是任何写操作都要记录日志。也就是说基本上处于不能使用的状态。
17 SQL Server是否支持行级锁,有什么好处?
答:支持,设立封锁机制主要是为了对并发操作进行控制,对干扰进行封锁,保证数据的一致性和准确性,行级封锁确保在用户取得被更新的行到该行进行更新这段时间内不被其它用户所修改。因而行级锁即可保证数据的一致性又能提高数据操作的并发性。
18如果数据库满了会出现什么情况,是否还能使用?
答:见16
19 关于内存对齐的问题以及sizeof()的输出
答:编译器自动对齐的原因:为了提高程序的性能,数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;然而,对齐的内存访问仅需要一次访问。
20 int i=10, j=10, k=3; k*=i+j; k最后的值是?
答:60,此题考察优先级,实际写成: k*=(i+j);,赋值运算符优先级最低
21.对数据库的一张表进行操作,同时要对另一张表进行操作,如何实现?
答:将操作多个表的操作放入到事务中进行处理
答:在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
答:Internet控制报文协议,处于网络层(IP层)
24.触发器怎么工作的?
答:触发器主要是通过事件进行触发而被执行的,当对某一表进行诸如UPDATE、 INSERT、 DELETE 这些操作时,数据库就会自动执行触发器所定义的SQL 语句,从而确保对数据的处理必须符合由这些SQL 语句所定义的规则。
答:服务器端:socket()建立套接字,绑定(bind)并监听(listen),用accept()等待客户端连接。
客户端:socket()建立套接字,连接(connect)服务器,连接上后使用send()和recv(),在套接字上写读数据,直至数据交换完毕,closesocket()关闭套接字。
服务器端:accept()发现有客户端连接,建立一个新的套接字,自身重新开始等待连接。该新产生的套接字使用send()和recv()写读数据,直至数据交换完毕,closesocket()关闭套接字。
26.动态连接库的两种方式?
答:调用一个DLL中的函数有两种方法:
1.载入时动态链接(load-time dynamic linking),模块非常明确调用某个导出函数,使得他们就像本地函数一样。这需要链接时链接那些函数所在DLL的导入库,导入库向系统提供了载入DLL时所需的信息及DLL函数定位。
2.运行时动态链接(run-time dynamic linking),运行时可以通过LoadLibrary或LoadLibraryEx函数载入DLL。DLL载入后,模块可以通过调用GetProcAddress获取DLL函数的出口地址,然后就可以通过返回的函数指针调用DLL函数了。如此即可避免导入库文件了
答:Internet上产生的许多新的应用,特别是高带宽的多媒体应用,带来了带宽的急剧消耗和网络拥挤问题。组播是一种允许一个或多个发送者(组播源)发送单一的数据包到多个接收者(一次的,同时的)的网络技术。组播可以大大的节省网络带宽,因为无论有多少个目标地址,在整个网络的任何一条链路上只传送单一的数据包。所以说组播技术的核心就是针对如何节约网络资源的前提下保证服务质量。
托管代码
使用基于公共语言运行库的语言编译器开发的代码称为托管代码;托管代码具有许多优点,例如:跨语言集成、跨语言异常处理、增强的安全性、版本控制和部署支持、简化的组件交互模型、调试和分析服务等。
重载
每个类型成员都有一个唯一的签名。方法签名由方法名称和一个参数列表(方法的参数的顺序和类型)组成。只要签名不同,就可以在一种类型内定义具有相同名称的多种方法。当定义两种或多种具有相同名称的方法时,就称作重载。
预处理器(Preprocessor)1. 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL 我在这想看到几件事情: 1). #define 语法的基本知识(例如:不能以分号结束,括号的使用,等等) 2). 懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。 3). 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。 4). 如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要。
2. 写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。#define MIN(A,B) ((A) <= (B)?(A) : (B)) 这个测试是为下面的目的而设的: 1). 标识#define在宏中应用的基本知识。这是很重要的,因为直到嵌入(inline)操作符变为标准C的一部分,宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。 2). 三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码,了解这个用法是很重要的。 3). 懂得在宏中小心地把参数用括号括起来 4). 我也用这个问题开始讨论宏的副作用,例如:当你写下面的代码时会发生什么事? least = MIN(*p++, b);
3. 预处理器标识#error的目的是什么?如果你不知道答案,请看参考文献1。这问题对区分一个正常的伙计和一个书呆子是很有用的。只有书呆子才会读C语言课本的附录去找出象这种问题的答案。当然如果你不是在找一个书呆子,那么应试者最好希望自己不要知道答案。#error 停止编译并显示错误信息死循环(Infinite loops)4. 嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?这个问题用几个解决方案。我首选的方案是: while(1) { } 一些程序员更喜欢如下方案: for(;;) { } 这个实现方式让我为难,因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案,我将用这个作为一个机会去探究他们这样做的基本原理。如果他们的基本答案是:“我被教着这样做,但从没有想到过为什么。”这会给我留下一个坏印象。 第三个方案是用 goto Loop: ... goto Loop; 应试者如给出上面的方案,这说明或者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。
数据声明(Data declarations) 5. 用变量a给出下面的定义 a) 一个整型数(An integer) b) 一个指向整型数的指针(A pointer to an integer) c) 一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an integer) d) 一个有10个整型数的数组(An array of 10 integers) e) 一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers) f) 一个指向有10个整型数数组的指针(A pointer to an array of 10 integers) g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer) h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argument and return an integer )答案是: a) int a; // An integer b) int *a; // A pointer to an integer c) int **a; // A pointer to a pointer to an integer d) int a[10]; // An array of 10 integers e) int *a[10]; // An array of 10 pointers to integers f) int (*a)[10]; // A pointer to an array of 10 integers g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer
人们经常声称这里有几个问题是那种要翻一下书才能回答的问题,我同意这种说法。当我写这篇文章时,为了确定语法的正确性,我的确查了一下书。 但是当我被面试的时候,我期望被问到这个问题(或者相近的问题)。因为在被面试的这段时间里,我确定我知道这个问题的答案。应试者如果不知道 所有的答案(或至少大部分答案),那么也就没有为这次面试做准备,如果该面试者没有为这次面试做准备,那么他又能为什么出准备呢?
Static6. 关键字static的作用是什么?这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用: 1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。 2). 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。 3). 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。 大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。这是一个应试者的严重的缺点,因为他显然不懂得本地化数据和代码范围的好处和重要性。
Const 7.关键字const是什么含意? 我只要一听到被面试者说:“const意味着常数”,我就知道我正在和一个业余者打交道。去年Dan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出const意味着“只读”就可以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。(如果你想知道更详细的答案,仔细读一下Saks的文章吧。)如果应试者能正确回答这个问题,我将问他一个附加的问题:下面的声明都是什么意思?const int a; //a是一个常整型数int const a; //a是一个常整型数const int *a; //一个指向常整型数的指针,整型数是不可修改的,但指针可以int * const a; //一个指向整型数的常指针,指针指向的整型数是可以修改的,指针不可修改int const * a const;// 一个指向常整型数的常指针,整型数不可修改,指针也是不可修改的
(主要看const靠近*还是int,不可修改的就是它)前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由: 1). 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。) 2). 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。 3). 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。Volatile
8. 关键字volatile有什么含意 并给出三个不同的例子。一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子: 1). 并行设备的硬件寄存器(如:状态寄存器) 2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 3). 多线程应用中被几个任务共享的变量 回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。 假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。 1). 一个参数既可以是const还可以是volatile吗?解释为什么。 2). 一个指针可以是volatile 吗?解释为什么。 3). 下面的函数有什么错误: int square(volatile int *ptr) { return *ptr * *ptr; } 下面是答案: 1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。 2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。 3). 这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码: int square(volatile int *ptr) { int a,b; a = *ptr; b = *ptr; return a * b; } 由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下: long square(volatile int *ptr) { int a; a = *ptr; return a * a; }位操作(Bit manipulation)9. 嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变。对这个问题有三种基本的反应 1). 不知道如何下手。该被面者从没做过任何嵌入式系统的工作。 2). 用bit fields。Bit fields是被扔到C语言死角的东西,它保证你的代码在不同编译器之间是不可移植的,同时也保证了的你的代码是不可重用的。我最近不幸看到Infineon为其较复杂的通信芯片写的驱动程序,它用到了bit fields因此完全对我无用,因为我的编译器用其它的方式来实现bit fields的。从道德讲:永远不要让一个非嵌入式的家伙粘实际硬件的边。 3). 用 #defines 和 bit masks 操作。这是一个有极高可移植性的方法,是应该被用到的方法。最佳的解决方案如下: #define BIT3 (0x1<<3) static int a; void set_bit3(void) { a |= BIT3; } void clear_bit3(void) { a &= ~BIT3; } 一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数,这也是可以接受的。我希望看到几个要点:说明常数、|=和&=~操作。
访问固定的内存位置(Accessing fixed memory locations) 10. 嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下: int *ptr; ptr = (int *)0x67a9; *ptr = 0xaa55;一个较晦涩的方法是: *(int * const)(0x67a9) = 0xaa55;即使你的品味更接近第二种方案,但我建议你在面试时使用第一种方案。中断(Interrupts) 11. 中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是,产生了一个新的关键字__interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。__interrupt double compute_area (double radius) { double area = PI * radius * radius; printf(" Area = %f", area); return area; }这个函数有太多的错误了,以至让人不知从何说起了: 1). ISR 不能返回一个值。如果你不懂这个,那么你不会被雇用的。 2). ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第一项。 3). 在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。 4). 与第三点一脉相承,printf()经常有重入和性能上的问题。如果你丢掉了第三和第四点,我不会太为难你的。不用说,如果你能得到后两点,那么你的被雇用前景越来越光明了。代码例子(Code examples)12 . 下面的代码输出是什么,为什么?void foo(void) { unsigned int a = 6; int b = -20; (a+b > 6) puts("> 6") : puts("<= 6"); }这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是“>6”。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。 因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。13. 评价下面的代码片断:unsigned int zero = 0; unsigned int compzero = 0xFFFF; /*1's complement of zero */对于一个int型不是16位的处理器为说,上面的代码是不正确的。应编写如下:unsigned int compzero = ~0;这一问题真正能揭露出应试者是否懂得处理器字长的重要性。在我的经验里,好的嵌入式程序员非常准确地明白硬件的细节和它的局限,然而PC机程序往往把硬件作为一个无法避免的烦恼。 到了这个阶段,应试者或者完全垂头丧气了或者信心满满志在必得。如果显然应试者不是很好,那么这个测试就在这里结束了。但如果显然应试者做得不错,那么我就扔出下面的追加问题,这些问题是比较难的,我想仅仅非常优秀的应试者能做得不错。提出这些问题,我希望更多看到应试者应付问题的方法,而不是答案。不管如何,你就当是这个娱乐吧…
动态内存分配(Dynamic memory allocation)14. 尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从堆(heap)中动态分配内存的过程的。那么嵌入式系统中,动态分配内存可能发生的问题是什么?这里,我期望应试者能提到内存碎片,碎片收集的问题,变量的持行时间等等。这个主题已经在ESP杂志中被广泛地讨论过了(主要是 P.J. Plauger, 他的解释远远超过我这里能提到的任何解释),所有回过头看一下这些杂志吧!让应试者进入一种虚假的安全感觉后,我拿出这么一个小节目:下面的代码片段的输出是什么,为什么?char *ptr; if ((ptr = (char *)malloc(0)) == NULL) puts("Got a null pointer"); else puts("Got a valid pointer"); 这是一个有趣的问题。最近在我的一个同事不经意把0值传给了函数malloc,得到了一个合法的指针之后,我才想到这个问题。这就是上面的代码,该代码的输出是“Got a valid pointer”。我用这个来开始讨论这样的一问题,看看被面试者是否想到库例程这样做是正确。得到正确的答案固然重要,但解决问题的方法和你做决定的基本原理更重要些。Typedef 15. Typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如,思考一下下面的例子: #define dPS struct s * typedef struct s * tPS; 以上两种情况的意图都是要定义dPS 和 tPS 作为一个指向结构s指针。哪种方法更好呢?(如果有的话)为什么? 这是一个非常微妙的问题,任何人答对这个问题(正当的原因)是应当被恭喜的。答案是:typedef更好。思考下面的例子: dPS p1,p2; tPS p3,p4;第一个扩展为 struct s * p1, p2;上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许不是你想要的。第二个例子正确地定义了p3 和p4 两个指针。晦涩的语法16. C语言同意一些令人震惊的结构,下面的结构是合法的吗,如果是它做些什么? int a = 5, b = 7, c; c = a+++b;这个问题将做为这个测验的一个愉快的结尾。不管你相不相信,上面的例子是完全合乎语法的。问题是编译器如何处理它?水平不高的编译作者实际上会争论这个问题,根据最处理原则,编译器应当能处理尽可能所有合法的用法。因此,上面的代码被处理成: c = a++ + b; 因此, 这段代码持行后a = 6, b = 7, c = 12。 如果你知道答案,或猜出正确答案,做得好。如果你不知道答案,我也不把这个当作问题。我发现这个问题的最大好处是:这是一个关于代码编写风格,代码的可读性,代码的可修改性的好的话题
今天群硕笔试,考了好多内容,其中Java占很大部分!
本试卷中最有难度的编程题:给定一个数组,这个数组中既有正数又有负数,找出这个数组中的子数组,此子数组的和最大!
#include<stdio.h>void main(){int a[15]={2,3,-4,5,6,-5,-1,14,9,-10,1,-71,75,4,-9};int b[15]; //计算和int num=a[14];int c[15]; //最大尾数标志数组int n=14;int i,j=0;for(i=14;i>=0;i--) //从后计算和放入b中{ c[i]=n;b[i]=num;if(num<0){n=i-1;num=0;}num=num+a[i-1];}printf("/n");for(i=0;i<15;i++)printf("%d, ",b[i]);printf("/n");//找到最大字符子串num=b[0];n=c[0];for(i=0;i<15;i++)if(b[i]>num){num=b[i];j=i;n=c[i];}for(i=j;i<=n;i++)printf("%d, ",a[i]);printf("sum=%d",num);}
最不知道怎么答的题:在TCP/IP中最经常使用的编程方法?
最简单的题:final, finally, finalize的区别(因为经常考)
final—修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为 abstract的,又被声明为final的。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重载。
finally—再异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的 catch 子句就会执行,然后控制就会进入 finally 块(如果有的话)。
finalize—方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。
最容易疏忽的题:main(){C c;}在前面已经定义了C的类,这个地方容易迷糊的就是,没有new,照样执行构造函数,没有free,照样执行析构函数。
最迷糊的题:一个无向图是否能够存到树中?为什么?
最然我感到有差距的题:EJB中一定包括()interface,()interface,()class
不用任何变量交换a,b两个变量
用递归求最大公约数
1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现 一次。每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空 间,能否设计一个算法实现?
template <typename T, int index> struct SumArr { static int GetValue(T* arr) { return arr[index] + SumArr<T, index-1>::GetValue(arr); } }; template <typename T> struct SumArr<T, 0> { static int GetValue(T* arr) { return arr[0]; } }; int main() { int arry[11] = {1,2,3,4,5,6,7,8,9,10,2}; printf("%d", SumArr<int, 10>::GetValue(arry) - 11 * 5); }
1_1 什么叫做多态性? 在C++中是如何实现多态的?
解:多态是指同样的消息被不同类型的对象接收时导致完全不同的行为,是对类的特定成员函数的再抽象。C++支持的多态有多种类型,重载(包括函数重载和运算符重载)和虚函数是其中主要的方式。
1_2 什么叫做抽象类? 抽象类有何作用? 抽象类的派生类是否一定要给出纯虚函数的实现?
解:带有纯虚函数的类是抽象类。抽象类的主要作用是通过它为一个类族建立一个公共的接口,使它们能够更有效地发挥多态特性。抽象类声明了一组派生类共同操作接口的通用语义,而接口的完整实现,即纯虚函数的函数体,要由派生类自己给出。但抽象类的派生类并非一定要给出纯虚函数的实现,如果派生类没有给出纯虚函数的实现,
这个派生类仍然是一个抽象类。
晚上参加了autodesk笔试,留下了一丝的残念,mfc好久没有用几乎忘光了,com学院没有开这门课,还得靠自学T_T晚上听了报告,发现autodesk公司真的很不错.在cadc部门能接触到autocad最核心的代码,这是在另外很多外企所碰不到的.autolove部分那张脸上充满笑容忘着远方的小女孩的照片很让人感动,很钦佩autodesk公司所做的公益活动.得复习一下专业知识了,以后还将有很多的招聘会.
附笔试题目:
1.What is virtual function ?what is vtable used for?虚函数主要用于实现多态用,基类的某个函数前加个Virtual 用来告诉编译系统,遇到这个处理过程时,要等到执行时再确定到底调用哪个类的处理过程;
如果没有多态和虚拟继承,在C++中,struct和class的存取效率完全相同!简单的说就是,存取class的data member和非virtual function效率和struct完全相同!不管该data member是定义在基类还是派生类的。如果不是为了和C兼容,C++中就不会有struct关键字。
1. dynamic binding
2. default private
3. for safe deleting base classes
4. sometype foo const;
5. use some macro, create a message table
6-12. i'll keep it blank...
13. 丁最大,男的
Autodesk笔试题目
C/C++ Programming1,列出两个情况是必须使用成员初始化列表,而不在构造函数里面赋值2,#define DOUBLE(x) x+xi = 5 * DOUBLE(10)这个时候i是什么结果?正确的DOUBLE应该怎样写?3,static_cast和dynamic_cast有什么区别?4,namespace解决了什么问题?5,auto_ptr是什么东西,有什么用?Algorithm and GP1, 写出三个你熟悉的排序法,以时间复杂度的大小排序2, 写出一个使用递归来反转一个单向链表的函数3, 写一个程序测试系统是Big_Endian的还是Little_Endian的(以前万老师发帖讨论过了)C++ Programming1, C++有管理多种内存,分别写出他们是什么,他们的特性和性能如何?2, 写出一个基于char*的string类,包括构造析构函数和赋值运算符,取字串长度等基本操作Graphic(两题都不会,全忘了)……IQ1, 称面粉(经典题)2, 在平面上有一系列间距为2的无限长的平行线,在上面扔单位长度的线段,和平行线相交的概率是多少?3, A君和B君见到B君的三个熟人X,Y,ZA君问B君:“他们的多大”B君说:“他们的年龄之和是我们的年龄之和,他们的年龄的乘积是2450”A说:“我还是不知道”B说:“他们都比我们的朋友C要小”A说:“那我知道了”问C的年龄是多少?
MFC(不太会做,记不清楚了)1, 怎么为窗口自定义消息
2, SendMessage和PostMessage的区别
sendmessage是消息发送给指定的callback函数后立即执行。。当前的调用挂起。。postmessage只是把消息发送到消息队列。。然后返回。。。所以在消息循环中。。用的是postmessage而不是sendmessage。。
3, CRuntimeClass是由什么用的?他是怎样得到的?
4, 怎样通过一个句柄得到CWnd的指针
fromhandle函数可以得到指定cwnd的m_hwnd。。
早上起的早了一点,刚好看到版上置顶的帖子第一个是博朗,反正也没有别的事,抄了一份简历过去。
三态:运行态,就绪态,等待态
硬件中断
有限自动向量机
有限自动机(Finite Automata):有限自动机=有限控制器+字符输入带
如果有限状态机每次转换后的状态是唯一的则称之为确定有限状态机(DFA);如果转换后的后继状态不是唯一的则称之为不确定有限自动机(NFA);
数据库
链表操作(出入新元素,很简单)
数据结构中的树,
加密问题
PING的协议(ICMP?)
Ping命令是在互连网络中进行故障检测的工具,通俗地说就是它向指定的机器发送一个消息,然后通知是否成功地传送了该消息. ping利用了ICMP中的请求回显和应答回显的功能. ping 程序是用来探测主机到主机之间是否可通信,如果不能ping到某台主机,表明不能和这台主机建立连接。ping 使用的是ICMP协议,它发送icmp回送请求消息给目的主机。ICMP协议规定:目的主机必须返回ICMP回送应答消息给源主机。如果源主机在一定时间内收到应答,则认为主机可达。
ICMP协议通过IP协议发送的,IP协议是一种无连接的,不可靠的数据包协议。在Unix/Linux,序列号从0开始计数,依次递增。而Windows ping程序的ICMP序列号是没有规律。
UML知识(全军覆没,没接触过这玩意)
UML的组成
在UML中,一个用例模型由若干个用例图描述,用例图主要 元素是用例和执行者。用例(use case) 从本质上讲,一个用例是用户与计算机之间的一次典型交互作用。在UML中,用例表示为一个椭圆。执行者(Actor) 执行者是指用户在系统中所扮演的角色。其图形化的表示是一个小人。
在UML中,类 和对象模型分别由类图和对象图表示. UML规定类的属性的语法为: 可见性 属性名 : 类型 = 缺省值 {约束特性} 图1"客户"类中,"客户名"属性描述为"- 客户名 : 字符串 = 缺省客户名"。
常用的可见性有Public、Private和Protected三种,在U ML中分别表示为"+"、"-"和"#"。
关联类通过一根虚线与关联连接。
聚集和组成 聚集(Aggregation)是一种特殊形式的关联。聚集表示类之间的关系是 整体与部分的关系。一辆轿车包含四个车轮、一个方向盘、一个发动机和一个底盘,这是 聚集的一个例子。在需求分析中,"包含"、"组成"、"分为……部分"等经常设计成聚集关 系。聚集可以进一步划分成共享聚集(Shared Aggregation)和组成。
已知后序,中序,求前序
堆的结构。
答的惨不忍睹啊,数了数,大概就10个能对的,其他是一点吃不准。
考完软件,立刻考逻辑,就是一大堆的数据,40个题目,40min要做完。可怜我还没有计算器,做的我累死了。到最后还有10几个没勾,我也懒得乱勾了。交卷。
有几点点要bs一下这个公司,
2.用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至全部输出。写出C程序。循环链表,用取余操作做
3.不能做switch()的参数类型是:switch的参数不能为实型。
4. static有什么用途?(请至少说明两种)1.限制变量的作用域2.设置变量的存储域
7. 引用与指针有什么区别?1) 引用必须被初始化,指针不必。2) 引用初始化以后不能被改变,指针可以改变所指的对象。2) 不存在指向空值的引用,但是存在指向空值的指针。
9. 全局变量和局部变量在内存中是否有区别?如果有,是什么区别?全局变量储存在静态数据库,局部变量在堆栈
10. 什么是平衡二叉树?左右子树都是平衡二叉树 且左右子树的深度差值的绝对值不大于1
11. 堆栈溢出一般是由什么原因导致的?没有回收垃圾资源
14. 写出float x 与“零值”比较的if语句。if(x>0.000001&&x<-0.000001)
16. Internet采用哪种网络协议?该协议的主要层次结构?tcp/ip 应用层/传输层/网络层/数据链路层/物理层
17. Internet物理地址和IP地址转换采用什么协议?ARP (Address Resolution Protocol)(地址解析协议)
1. 用宏定义写出swap(x,y)#define swap(x, y)x = x + y;y = x - y;x = x - y;
答案:方法1:如果数就是1-N-1,那么求出a[N]的和,然后减去1-N-1就行了。(确定数字1-N)
S = N * (N-1) / 2;int i;int s = 0;for(i=0;i<N;++i){s += a[i];}int res = s - S;
方法2.a[]中的某元素a[i]看做是pi[]数组的下标,元素a[i]存储到对应数组下标pi[a[i]]的地址中
#include<stdio.h> #define N 10 void main() { int a[N]={1,2,3,4,5,6,7,7,8,9}; int pi[N]={0}; int key=0; for(int i=0;i<N;i++) { if(pi[a[i]]==0) pi[a[i]]=a[i]; else { key=a[i]; break; } } printf("多余的数字是%d/n",key); }
3 一语句实现x是否为2的若干次幂的判断位运算
int i = 512; cout << boolalpha << ((i & (i - 1)) ? false : true) << endl;
什么是预编译何时需要预编译:1、总是使用不经常改动的大型代码体。 2、程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况下,可以将所有包含文件预编译为一个预编译头。
char * const p;char const * pconst char *p上述三个有什么区别?char * const p; //常量指针,p的值不可以修改char const * p;//指向常量的指针,指向的常量值不可以改const char *p; //同char const *pchar str1[] = "abc";char str2[] = "abc";const char str3[] = "abc";const char str4[] = "abc";const char *str5 = "abc";const char *str6 = "abc";char *str7 = "abc";char *str8 = "abc";cout << ( str1 == str2 ) << endl;cout << ( str3 == str4 ) << endl;cout << ( str5 == str6 ) << endl;cout << ( str7 == str8 ) << endl;结果是:0 0 1 1解答:str1,str2,str3,str4是数组变量,它们有各自的内存空间;而str5,str6,str7,str8是指针,它们指向相同的常量区域。一个32位的机器,该机器的指针是多少位指针是多少位只要看地址总线的位数就行了。80386以后的机子都是32的数据总线。所以指针的位数就是4个字节了。main(){ int a[5]={1,2,3,4,5}; int *ptr=(int *)(&a+1); printf("%d,%d",*(a+1),*(ptr-1));}输出:2,5*(a+1)就是a[1],*(ptr-1)就是a[4],执行结果是2,5;&a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int)int *ptr=(int *)(&a+1); 则ptr实际是&(a[5]),也就是a+5原因如下:&a是数组指针,其类型为 int (*)[5];而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同a是长度为5的int数组指针,所以要加 5*sizeof(int)所以ptr实际是a[5]但是prt与(&a+1)类型是不一样的(这点很重要),所以prt-1只会减去sizeof(int*);a,&a的地址是一样的,但意思不一样,a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址,a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5].1.请问以下代码有什么问题:int main(){ char a; char *str=&a; strcpy(str,"hello"); printf(str); return 0;}没有为str分配内存空间,将会发生异常。问题出在将一个字符串复制进一个字符变量指针所指地址。虽然可以正确输出结果,但因为越界进行内在读写而导致程序崩溃。char* s="AAA";printf("%s",s);s[0]='B';printf("%s",s);有什么错?"AAA"是字符串常量。s是指针,指向这个字符串常量,所以声明s的时候就有问题。cosnt char* s="AAA";然后又因为是常量,所以对是s[0]的赋值操作是不合法的。
1、写一个“标准”宏,这个宏输入两个参数并返回较小的一个。#define Min(X, Y) ((X)>(Y)?(Y):(X)) 注意结尾没有;
2、嵌入式系统中经常要用到无限循环,你怎么用C编写死循环。 while(1){}或者for(;;)
3、关键字static的作用是什么? 定义静态变量
4、关键字const有什么含意?表示常量不可以修改的变量。
5、关键字volatile有什么含意?并举出三个不同的例子?提示编译器对象的值可能在编译器未监测到的情况下改变。
5:int a[3];a[0]=0; a[1]=1; a[2]=2;int *p, *q;p=a;q=&a[2];则a[q-p]=a[2]=2 解释:指针一次移动一个int但计数为1今天早上的面试题9道,比较难,向牛人请教,国内的一牛公司,坐落在北京北四环某大厦:1、线形表a、b为两个有序升序的线形表,编写一程序,使两个有序线形表合并成一个有序升序线形表h;答案在严锐敏《数据结构第二版》第二章例题,数据结构当中,这个叫做:两路归并排序Linklist *unio(Linklist *p,Linklist *q){linklist *R,*pa,*qa,*ra;pa=p;qa=q;R=ra=p;while(pa->next!=NULL&&qa->next!=NULL){if(pa->data>qa->data){ra->next=qa;qa=qa->next; 取两线性表的大值放入ra,并将取值的表指针加1}else{ra->next=pa;pa=pa->next;}}if(pa->next!=NULL)ra->next=pa;if(qa->next!=NULL)ra->next==qa;return R;}
2、运用四色定理,为N个局域举行配色,颜色为1、2、3、4四种,另有数组adj[][N],如adj[i][j]=1则表示i区域与j区域相邻,数组color[N],如color[i]=1,表示i区域的颜色为1号颜色。四色填充
3、用递归算法判断数组a[N]是否为一个递增数组。递归的方法,记录当前最大的,并且判断当前的是否比这个还大,大则继续,否则返回false结束:bool fun( int a[], int n ){if( n= =1 )return true;if( n= =2 )return a[n-1] >= a[n-2];return fun( a,n-1) && ( a[n-1] >= a[n-2] );}
4、编写算法,从10亿个浮点数当中,选出其中最大的10000个。(google,百度,腾讯)一种是用外部排序,在《数据结构》书上有《计算方法导论》在找到第n大的数的算法上加工?
另外一种是用一个有序容器保存10000个数,然后其他的数字依次和容器中的最小数字比较,如果大于容器已有的,就插入容器并删除原先最小的那个,而容器仍旧保持有序。
5、编写一unix程序,防止僵尸进程的出现.
有一个数组a[1000]存放0--1000;要求每隔二个数删掉一个数,到末尾时循环至开头继续进行,求最后一个被删掉的数的原始下标位置。以7个数为例:{0,1,2,3,4,5,6,7} 0-->1-->2(删除)-->3-->4-->5(删除)-->6-->7-->0(删除),如此循环直到最后一个数被删除。方法1:数组#include <iostream>using namespace std;#define null 1000int main(){int arr[1000];for (int i=0;i<1000;++i)arr[i]=i;int j=0;int count=0;while(count<999){while(arr[j%1000]==null)j=(++j)%1000;j=(++j)%1000;while(arr[j%1000]==null)j=(++j)%1000;j=(++j)%1000;while(arr[j%1000]==null)j=(++j)%1000;arr[j]=null;++count;}while(arr[j]==null)j=(++j)%1000;cout<<j<<endl;return 0;}方法2:链表#include<iostream>using namespace std;#define null 0struct node{int data;node* next;};int main(){node* head=new node;head->data=0;head->next=null;node* p=head;for(int i=1;i<1000;i++){node* tmp=new node;tmp->data=i;tmp->next=null;head->next=tmp;head=head->next;}head->next=p;while(p!=p->next){p->next->next=p->next->next->next;p=p->next->next;}cout<<p->data;return 0;}方法3:通用算法#include <stdio.h>#define MAXLINE 1000 //元素个数/*MAXLINE 元素个数a[] 元素数组R[] 指针场suffix 下标index 返回最后的下标序号values 返回最后的下标对应的值start 从第几个开始K 间隔*/int find_n(int a[],int R[],int K,int& index,int& values,int s=0) {int suffix;int front_node,current_node;suffix=0;if(s==0) {current_node=0;front_node=MAXLINE-1;}else {current_node=s;front_node=s-1;}while(R[front_node]!=front_node) {printf("%d/n",a[current_node]);R[front_node]=R[current_node];if(K==1) {current_node=R[front_node];continue;}for(int i=0;i<K;i++){front_node=R[front_node];}current_node=R[front_node];}index=front_node;values=a[front_node];return 0;}int main(void) {int a[MAXLINE],R[MAXLINE],suffix,index,values,start,i,K;suffix=index=values=start=0;K=2;for(i=0;i<MAXLINE;i++) {a[i]=i;R[i]=i+1;}R[i-1]=0;find_n(a,R,K,index,values,2);printf("the value is %d,%d/n",index,values);return 0;}试题: void test2() { char string[10], str1[10]; int i; for(i=0; i<10; i++) { str1[i] = 'a'; } strcpy( string, str1 ); } 解答:对试题2,如果面试者指出字符数组str1不能在数组内结束可以给3分;如果面试者指出strcpy(string, str1)调用使得从str1内存起复制到string内存起所复制的字节数具有不确定性可以给7分,在此基础上指出库函数strcpy工作方式的给10分;str1不能在数组内结束:因为str1的存储为:{a,a,a,a,a,a,a,a,a,a},没有'/0'(字符串结束符),所以不能结束strcpy( char *s1,char *s2)他的工作原理是,扫描s2指向的内存,逐个字符付到s1所指向的内存,直到碰到'/0',因为str1结尾没有'/0',所以具有不确定性,不知道他后面还会付什么东东。正确应如下void test2() { char string[10], str1[10]; int i; for(i=0; i<9; i++) { str1[i] = 'a'+i; //把abcdefghi赋值给字符数组} str[i]='/0';//加上结束符strcpy( string, str1 ); }第二个code题是实现strcmpint StrCmp(const char *str1, const char *str2)做是做对了,没有抄搞,比较乱int StrCmp(const char *str1, const char *str2){assert(str1 && srt2);while (*str1 && *str2 && *str1 == *str2) {str1++, str2++;}if (*str1 && *str2)return (*str1-*str2);elseif (*str1 && *str2==0)return 1;elseif (*str1 = = 0 && *str2)return -1;elsereturn 0;}int StrCmp(const char *str1, const char *str2){//省略判断空指针(自己保证)while(*str1 && *str1++ = = *str2++);return *str1-*str2; }第三个code题是实现子串定位int FindSubStr(const char *MainStr, const char *SubStr)做是做对了,没有抄搞,比较乱int MyStrstr(const char* MainStr, const char* SubStr){const char *p;const char *q;const char * u = MainStr;//assert((MainStr!=NULL)&&( SubStr!=NULL));//用断言对输入进行判断while(*MainStr) //内部进行递增{p = MainStr;q = SubStr;while(*q && *p && *p++ == *q++);if(!*q ){return MainStr - u +1 ;//MainStr指向当前起始位,u指向}MainStr ++;}return -1;}分析:int arr[] = {6,7,8,9,10};int *ptr = arr;*(ptr++)+=123;printf(“ %d %d ”, *ptr, *(++ptr));输出:8 8过程:对于*(ptr++)+=123;先做加法6+123,然后++,指针指向7;对于printf(“ %d %d ”, *ptr, *(++ptr));从后往前执行,指针先++,指向8,然后输出8,紧接着再输出8
给定字符串A和B,输出A和B中的最大公共子串。比如A="aocdfe" B="pmcdfa" 则输出"cdf"*///Author: azhen#include<stdio.h>#include<stdlib.h>#include<string.h>char *commanstring(char shortstring[], char longstring[]){int i, j;char *substring=malloc(256);if(strstr(longstring, shortstring)!=NULL) //如果……,那么返回shortstringreturn shortstring; for(i=strlen(shortstring)-1;i>0; i--) //否则,开始循环计算{for(j=0; j<=strlen(shortstring)-i; j++){memcpy(substring, &shortstring[j], i);substring[i]='/0';if(strstr(longstring, substring)!=NULL)return substring;}}return NULL;}main(){char *str1=malloc(256);char *str2=malloc(256);char *comman=NULL;gets(str1);gets(str2);if(strlen(str1)>strlen(str2)) //将短的字符串放前面comman=commanstring(str2, str1);elsecomman=commanstring(str1, str2);printf("the longest comman string is: %s/n", comman);}11.写一个函数比较两个字符串str1和str2的大小,若相等返回0,若str1大于str2返回1,若str1小于str2返回-1int strcmp ( const char * src,const char * dst){int ret = 0 ;while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst){++src;++dst;}if ( ret < 0 )ret = -1 ;else if ( ret > 0 )ret = 1 ;return( ret );}
3,求1000!的未尾有几个0(用素数相乘的方法来做,如72=2*2*2*3*3);求出1->1000里,能被5整除的数的个数n1,能被25整除的数的个数n2,能被125整除的数的个数n3,能被625整除的数的个数n4.1000!末尾的零的个数=n1+n2+n3+n4;#include<stdio.h>#define NUM 1000int find5(int num){int ret=0;while(num%5==0){num/=5;ret++;}return ret;}int main(){int result=0;int i;for(i=5;i<=NUM;i+=5){result+=find5(i);}printf(" the total zero number is %d/n",result);return 0;}1. 有双向循环链表结点定义为: struct node { int data; struct node *front,*next; }; 有两个双向循环链表A,B,知道其头指针为:pHeadA,pHeadB,请写一函数将两链表中data值相同的结点删除 BOOL DeteleNode(Node *pHeader, DataType Value){if (pHeader == NULL) return;BOOL bRet = FALSE;Node *pNode = pHead;while (pNode != NULL){if (pNode->data == Value){if (pNode->front == NULL){pHeader = pNode->next;pHeader->front = NULL;}else{if (pNode->next != NULL){pNode->next->front = pNode->front;}pNode->front->next = pNode->next;}Node *pNextNode = pNode->next;delete pNode;pNode = pNextNode;bRet = TRUE; //不要break或return, 删除所有}else{pNode = pNode->next;}}return bRet;}void DE(Node *pHeadA, Node *pHeadB){if (pHeadA == NULL || pHeadB == NULL){return;}Node *pNode = pHeadA;while (pNode != NULL){if (DeteleNode(pHeadB, pNode->data)){if (pNode->front == NULL){pHeadA = pNode->next;pHeadA->front = NULL;}else{pNode->front->next = pNode->next;if (pNode->next != NULL){pNode->next->front = pNode->front;}}Node *pNextNode = pNode->next;delete pNode;pNode = pNextNode;}else{pNode = pNode->next;}}}2. 编程实现:找出两个字符串中最大公共子字符串,如"abccade","dgcadde"的最大子串为"cad" int GetCommon(char *s1, char *s2, char **r1, char **r2){int len1 = strlen(s1);int len2 = strlen(s2);int maxlen = 0;for(int i = 0; i < len1; i++){ for(int j = 0; j < len2; j++) { if(s1[i] == s2[j]) { int as = i, bs = j, count = 1; while(as + 1 < len1 && bs + 1 < len2 && s1[++as] == s2[++bs]) count++; if(count > maxlen) { maxlen = count; *r1 = s1 + i; *r2 = s2 + j; } } }}3. 编程实现:把十进制数(long型)分别以二进制和十六进制形式输出,不能使用printf系列库函数char* test3(long num) {char* buffer = (char*)malloc(11);buffer[0] = '0';buffer[1] = 'x';buffer[10] = '/0';char* temp = buffer + 2;for (int i=0; i < 8; i++) {temp[i] = (char)(num<<4*i>>28);temp[i] = temp[i] >= 0 ? temp[i] : temp[i] + 16;temp[i] = temp[i] < 10 ? temp[i] + 48 : temp[i] + 55;}return buffer;}斐波拉契数列递归实现的方法如下:int Funct( int n ){if(n==0) return 1;if(n==1) return 1;retrurn Funct(n-1) + Funct(n-2);}请问,如何不使用递归,来实现上述函数?请教各位高手!解答:int Funct( int n ) // n 为非负整数{int a=0;int b=1;int c;if(n==0) c=1;else if(n==1) c=1;else for(int i=2;i<=n;i++) //应该n从2开始算起{c=a+b;a=b;b=c;}return c;}解答:现在大多数系统都是将低字位放在前面,而结构体中位域的申明一般是先声明高位。100 的二进制是 001 100 100低位在前 高位在后 001----s3100----s2100----s1所以结果应该是 1如果先申明的在低位则:001----s1100----s2100----s3结果是 41、原题跟little-endian,big-endian没有关系2、原题跟位域的存储空间分配有关,到底是从低字节分配还是从高字节分配,从Dev C++和VC7.1上看,都是从低字节开始分配,并且连续分配,中间不空,不像谭的书那样会留空位3、原题跟编译器有关,编译器在未用堆栈空间的默认值分配上有所不同,Dev C++未用空间分配为01110111b,VC7.1下为11001100b,所以在Dev C++下的结果为5,在VC7.1下为1。注:PC一般采用little-endian,即高高低低,但在网络传输上,一般采用big-endian,即高低低高,华为是做网络的,所以可能考虑big-endian模式,这样输出结果可能为4判断一个字符串是不是回文int IsReverseStr(char *aStr){int i,j;int found=1;if(aStr==NULL)return -1;j=strlen(aStr);for(i=0;i<j/2;i++)if(*(aStr+i)!=*(aStr+j-i-1)){found=0;break;}return found;}Josephu 问题为:设编号为1,2,… n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。数组实现:#include <stdio.h>#include <malloc.h>int Josephu(int n, int m){ int flag, i, j = 0; int *arr = (int *)malloc(n * sizeof(int)); for (i = 0; i < n; ++i) arr[i] = 1; for (i = 1; i < n; ++i) { flag = 0; while (flag < m) { if (j == n) j = 0; if (arr[j]) ++flag; ++j; } arr[j - 1] = 0; printf("第%4d个出局的人是:%4d号/n", i, j); } free(arr); return j;
为什么说用PHP开发大型系统令人不爽
笔者在过去的四年里一直致力于PHP应用的开发。PHP确实十分容易编写。但是PHP也有一些十分严重的缺陷。
下面笔者会给出自己的理由,为什么PHP不适合于比小型业余网站更大的网站。
1. 对递归的不良支持
递归是一种函数调用自身的机制。这是一种强大的特性可以把某些复杂的东西变得很简单。有一个使用递归的例子是快速排序(quicksort)。不幸的是,PHP并不擅长递归。Zeev,一个PHP开发人员,说道:“PHP 4.0(Zend)对密集数据使用了栈方式,而不是使用堆方式。也就是说它能容忍的递归函数的数量限制和其他语言比起来明显少。”见bug 1901。这是一个很不好的借口。每一个编程语言都应该提供良好的递归支持。
2. 许多PHP模块都不是线程安全的
在几年前,Apache发布了Web服务器的2.0版。这个版本支持多线程模式,在这个模式下,软件一个一部分可以同时运行多个。PHP的发明者说PHP的核心是线程安全的,但是非核心模块不一定是。但是十次有九次,你想要在PHP脚本中使用这种模块,但这又使你的脚本不能合适Apache的多线程模式。这也是为什么PHP小组不推荐在Apache 2 的多线程模式下运行PHP。不良的多线程模式支持使PHP常被认为是Apache 2依然不流行的原因之一。
请阅读这篇讨论: Slashdot: Sites Rejecting Apache 2?.
3. PHP 由于商业原因而不健全
通过使用缓存,PHP的性能可以陡增500%[见基准测试]。那么为什么缓存没有被构建在PHP中呢?因为Zend——PHP的制造者,它在销售自己的Zend Accelerator,所以当然,他们不想抛弃自己的商业产品这块肥肉。
但是有另一个可选择的: APC. (Zend后来推出Zend Optimizer,免费的加速器——译者)
4. 没有命名空间
设想某个人制作了一个PHP模块用来阅读文件。模块中一个函数叫做read。然后另一个人的模块可以读取网页的,同样包含一个函数read。然后我们就无法同时使用这两个模块了,因为PHP不知道你要用哪个函数。
但是有一个很简单的解决方法,那就是命名空间。曾经有人建议PHP5加入这个特性,但不幸得是他没有这么做。现在,没有命名空间,每个函数都必须加上模块名作为前缀,来避免名称冲突。这导致了函数名恐怖得长,例如xsl_xsltprocessor_transform_to_xml让代码难于书写和理解。
5. 不标准的日期格式字符
很多程序员对 日期格式字符 都很熟悉,它是从UNIX和C语言中来的。其他一些编程语言采用了这个标准,但是很奇怪的,PHP有它自己的一套完全不兼容的日期格式字符。在C中,“%j”表示一年中的当天,在PHP中他表示一个月中的当天。然而使事情更混乱的是:Smarty (一个很流行的PHP模版引擎)的 strftime 函数和 date_format 函数,却使用了C/UNIX的格式化字符。
6. 混乱的许可证
你也许认为PHP是免费的,所有的在手册中提到的PHP模块也是免费的。错了!例如,如果你想在PHP中生成PDF文件,你会在手册中发现两个模块:PDF 和 ClibPDF。但是这两个都是有商业许可证的。所以,你所使用的每个模块,你都要确保你同意他的许可证。
7. 不一致的函数命名规则
有些函数名称是有多个单词组成的。一般有三种单词组合的习惯:
直接拼接:getnumberoffiles
用下划线分开:get_number_of_files
骆驼法则:getNumberOfFiles
大部分语言选择其中一中。但是PHP都用到了。
例如,你想要把一些特殊字符转换成HTML实体,你会使用函数htmlentities(直接拼接单词)。如果你要使用相反的功能,你要用到它的小弟弟html_entity_decode。由于某些特殊的原因,这个函数名是由下划线分隔单词。怎么能这样呢?你知道有一个函数叫strpad。或者他是str_pad?每次你都要查看一下到底这个符号是什么或者直接等他出现一个错误。函数是不分大小写的,所以对于PHP来说rawurldecode和RawUrlDecode之间没有什么区别。这也很糟糕,因为两个都使用到了同时他们看上去还不一样,混淆了阅读者。
8. 魔法引用的地狱
9. 缺少标准框架
一个成长中的网站没有一个整体框架,最终会变成维护的噩梦。一个框架可以让很多工作变得简单。现在最流行的框架模型时MVC-模型,在其中表现层、业务逻辑和数据库访问都分离开了。
很多PHP网站不使用MVC-模型。他们甚至没有一个框架。甚至现在有一些PHP框架同时你都可以自己写一个,关于PHP的文章和手册没有提高框架的一个字。同时JSP-开发人员使用像Struts的框架、ASP开发人员使用.Net,看起来好像这些概念都广泛被PHP开发人员所了解。这就说明了PHP实际上到底是多专业。
总结
什么问题?
对于非常小的项目,它可以是一个十分符合人意的编程语言。但是对于较大的和更为复杂的项目,PHP就显出他的薄弱了。当你不断地摸索之后,你会发现笔者提到的某些问题的解决方案。所以,当解决方案已知之后,为什么不能修正他呢?另外为什么这些修补不在手册中提到呢?
一个开源的语言十分流行是一件好事。但不幸得是,它不是一个伟大的语言。笔者希望所有的问题能有一天得到解决(也许在PHP6?),然后我们就将拥有一个开源语言,他既开源,又好用。
C语言最长平台算法
已知一个已经从小到大排列好的数组,说这个数组中的一个平台(Plateau),就是连续的一串相同的元素,并且这一串元素不能再延伸.例如,在1,2,2,3,3,3,4,5,5,6中1,2.2,3.3.3,4,5.5,6都是平台.编写程序把这个数组中最长的平台找出来.
#include <stdio.h>int longest_plateau(int x[],int n){int length=1;int i;for(i=1;i<n;i++){if(x[i]==x[i-length])length++;}return length;}void main(){int a[]={1,2,2,3,3,3,4,5,5,6};int tmp;tmp=longest_plateau(a,9);printf("%d/n",tmp);}
本文的写作目的并不在于提供C/C++程序员求职面试指导,而旨在从技术上分析面试题的内涵。文中的大多数面试题来自各大论坛,部分试题解答也参考了网友的意见。 许多面试题看似简单,却需要深厚的基本功才能给出完美的解答。企业要求面试者写一个最简单的strcpy函数都可看出面试者在技术上究竟达到了怎样的程度,我们能真正写好一个strcpy函数吗?我们都觉得自己能,可是我们写出的strcpy很可能只能拿到10分中的2分。读者可从本文看到strcpy 函数从2分到10分解答的例子,看看自己属于什么样的层次。此外,还有一些面试题考查面试者敏捷的思维能力。 分析这些面试题,本身包含很强的趣味性;而作为一名研发人员,通过对这些面试题的深入剖析则可进一步增强自身的内功。 2.找错题 试题1:
void test1(){ char string[10]; char* str1 = "0123456789"; strcpy( string, str1 );}
试题1字符串str1需要11个字节才能存放下(包括末尾的’’),而string只有10个字节的空间,strcpy会导致数组越界;试题2:
void test2(){ char string[10], str1[10]; int i; for(i=0; i<10; i++) { str1[i] = 'a'; } strcpy( string, str1 );}
对试题2,如果面试者指出字符数组str1不能在数组内结束可以给3分;如果面试者指出strcpy(string, str1)调用使得从str1内存起复制到string内存起所复制的字节数具有不确定性可以给7分,在此基础上指出库函数strcpy工作方式的给10 分; 试题3:
void test3(char* str1){ char string[10]; if( strlen( str1 ) <= 10 ) { strcpy( string, str1 ); }}
对试题3,if(strlen(str1) <= 10)应改为if(strlen(str1) < 10),因为strlen的结果未统计’’所占用的1个字节。 剖析: 考查对基本功的掌握:(1)字符串以’’结尾;(2)对数组越界把握的敏感度; (3)库函数strcpy的工作方式,如果编写一个标准strcpy函数的总分值为10,下面给出几个不同得分的答案:
char * strcpy( char *strDest, const char *strSrc ) { assert( (strDest != NULL) && (strSrc != NULL) ); char *address = strDest; while( (*strDest++ = * strSrc++) != ‘’ ); return address;} 从2分到10分的几个答案我们可以清楚的看到,小小的strcpy竟然暗藏着这么多玄机,真不是盖的!需要多么扎实的基本功才能写一个完美的strcpy啊! (4)对strlen的掌握,它没有包括字符串末尾的''。 读者看了不同分值的strcpy版本,应该也可以写出一个10分的strlen函数了,完美的版本为: int strlen( const char *str ) //输入参数const
{ assert( strt != NULL ); //断言字符串地址非0 int len; while( (*str++) != '' ) { len++; } return len;}
试题4:
void GetMemory( char *p ){ p = (char *) malloc( 100 );}void Test( void ) { char *str = NULL; GetMemory( str ); //!!! strcpy( str, "hello world" ); printf( str );}
试题4传入中GetMemory( char *p )函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入形参的值,执行完
char *str = NULL;GetMemory( str );后的str仍然为NULL;试题5:
char *GetMemory( void ){ char p[] = "hello world"; return p; }void Test( void ){ char *str = NULL; str = GetMemory(); printf( str ); }
试题5中
char p[] = "hello world"; return p;的p[]数组为函数内的局部自动变量,在函数返回后,内存已经被释放。这是许多程序员常犯的错误,其根源在于不理解变量的生存期。
试题6:
void GetMemory( char **p, int num ){ *p = (char *) malloc( num );}void Test( void ){ char *str = NULL; GetMemory( &str, 100 ); strcpy( str, "hello" ); printf( str ); }
试题6的GetMemory避免了试题4的问题,传入GetMemory的参数为字符串指针的指针,但是在GetMemory中执行申请内存及赋值语句*p = (char *) malloc( num );后未判断内存是否申请成功,应加上:
if ( *p == NULL ){ ...//进行申请内存失败处理}
试题6的Test函数中也未对malloc的内存进行释放。试题7:
void Test( void ){ char *str = (char *) malloc( 100 ); strcpy( str, "hello" ); free( str ); ... //省略的其它语句}
试题7存在与试题6同样的问题,在执行char *str = (char *) malloc(100);后未进行内存是否申请成功的判断;另外,在free(str)后未置str为空,导致可能变成一个“野”指针,应加上:str = NULL;
剖析:试题4~7考查面试者对内存操作的理解程度,基本功扎实的面试者一般都能正确的回答其中50~60的错误。但是要完全解答正确,却也绝非易事。对内存操作的考查主要集中在:(1)指针的理解;(2)变量的生存期及作用范围;(3)良好的动态内存申请和释放习惯。再看看下面的一段程序有什么错误:
swap( int* p1,int* p2 ){ int *p; *p = *p1; *p1 = *p2; *p2 = *p;}
在swap函数中,p是一个“野”指针,有可能指向系统区,导致程序运行的崩溃。在VC++中DEBUG运行时提示错误“Access Violation”。该程序应该改为:
swap( int* p1,int* p2 ){ int p; p = *p1; *p1 = *p2; *p2 = p;} 3.内功题 试题1:分别给出BOOL,int,float,指针变量 与“零值”比较的 if 语句(假设变量名为var) 解答: BOOL型变量:if(!var) int型变量: if(var==0) float型变量: const float EPSINON = 0.00001; if ((x >= - EPSINON) && (x <= EPSINON) 指针变量: if(var==NULL) 剖析: 考查对0值判断的“内功”,BOOL型变量的0判断完全可以写成if(var==0),而int型变量也可以写成if(!var),指针变量的判断也可以写成if(!var),上述写法虽然程序都能正确运行,但是未能清晰地表达程序的意思。 一般的,如果想让if判断一个变量的“真”、“假”,应直接使用if(var)、if(!var),表明其为“逻辑”判断;如果用if判断一个数值型变量(short、int、long等),应该用if(var==0),表明是与0进行“数值”上的比较;而判断指针则适宜用if(var==NULL),这是一种很好的编程习惯。 浮点型变量并不精确,所以不可将float变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。如果写成if (x == 0.0),则判为错,得0分。
试题2:以下为Windows NT下的32位C++程序,请计算sizeof的值
void Func ( char str[100] ){ sizeof( str ) = ?}void *p = malloc( 100 );sizeof ( p ) = ?
解答:
sizeof( str ) = 4sizeof ( p ) = 4
剖析:Func ( char str[100] )函数中数组名作为函数形参时,在函数体内,数组名失去了本身的内涵,仅仅只是一个指针;在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。数组名的本质如下:
(1)数组名指代一种数据结构,这种数据结构就是数组; 例如:char str[10]; cout << sizeof(str) << endl;
输出结果为10,str指代数据结构char[10]。(2)数组名可以转换为指向其指代实体的指针,而且是一个指针常量,不能作自增、自减等操作,不能被修改;
char str[10];
str++; //编译出错,提示str不是左值
(3)数组名作为函数形参时,沦为普通指针。 Windows NT 32位平台下,指针的长度(占用内存的大小)为4字节,故sizeof( str ) 、sizeof ( p ) 都为4。
试题3:写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。另外,当你写下面的代码时会发生什么事?least = MIN(*p++, b);
解答:
#define MIN(A,B) ((A) <= (B) ? (A) : (B))
MIN(*p++, b)会产生宏的副作用
剖析: 这个面试题主要考查面试者对宏定义的使用,宏定义可以实现类似于函数的功能,但是它终归不是函数,而宏定义中括弧中的“参数”也不是真的参数,在宏展开的时候对“参数”进行的是一对一的替换。程序员对宏定义的使用要非常小心,特别要注意两个问题:(1)谨慎地将宏定义中的“参数”和整个宏用用括弧括起来。所以,严格地讲,下述解答:
#define MIN(A,B) (A) <= (B) ? (A) : (B)#define MIN(A,B) (A <= B ? A : B )
都应判0分;(2)防止宏的副作用。宏定义#define MIN(A,B) ((A) <= (B) ? (A) : (B))对MIN(*p++, b)的作用结果是:((*p++) <= (b) ? (*p++) : (*p++))这个表达式会产生副作用,指针p会作三次++自增操作。除此之外,另一个应该判0分的解答是:
#define MIN(A,B) ((A) <= (B) ? (A) : (B));
这个解答在宏定义的后面加“;”,显示编写者对宏的概念模糊不清,只能被无情地判0分并被面试官淘汰。
试题4:为什么标准头文件都有类似以下的结构?
#ifndef __INCvxWorksh#define __INCvxWorksh #ifdef __cplusplusextern "C" {#endif /*...*/ #ifdef __cplusplus}#endif #endif /* __INCvxWorksh */
解答:头文件中的编译宏
#ifndef __INCvxWorksh
#define __INCvxWorksh#endif的作用是防止被重复引用。
作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在symbol库中的名字与C语言的不同。例如,假设某个函数的原型为:
void foo(int x, int y);
该函数被C编译器编译后在symbol库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。_foo_int_int这样的名字包含了函数名和函数参数数量及类型信息,C++就是考这种机制来实现函数重载的。 为了实现C和C++的混合编程,C++提供了C连接交换指定符号extern "C"来解决名字匹配问题,函数声明前加上extern "C"后,则编译器就会按照C语言的方式将该函数编译为_foo,这样C语言中就可以调用C++的函数了。
试题5:编写一个函数,作用是把一个char组成的字符串循环右移n个。比如原来是“abcdefghi”如果n=2,移位后应该是“hiabcdefgh” 函数头是这样的:
//pStr是指向以''结尾的字符串的指针//steps是要求移动的nvoid LoopMove ( char * pStr, int steps ){ //请填充...}
解答:
正确解答1:
void LoopMove ( char *pStr, int steps ){ int n = strlen( pStr ) - steps; char tmp[MAX_LEN]; strcpy ( tmp, pStr + n ); //将要移动的部分放入tmp的前部 strcpy ( tmp + steps, pStr); //将原字符串放入tmp的后部 *( tmp + strlen ( pStr ) ) = ''; //保持tmp和原字符串长度一致 strcpy( pStr, tmp );}
正确解答2:
void LoopMove ( char *pStr, int steps ){ int n = strlen( pStr ) - steps; char tmp[MAX_LEN]; memcpy( tmp, pStr + n, steps ); memcpy(pStr + steps, pStr, n ); memcpy(pStr, tmp, steps ); }剖析:这个试题主要考查面试者对标准库函数的熟练程度,在需要的时候引用库函数可以很大程度上简化程序编写的工作量。最频繁被使用的库函数包括: (1) strcpy(2) memcpy(3) memset
试题6:已知WAV文件格式如下表,打开一个WAV文件,以适当的数据结构组织WAV文件头并解析WAV格式的各项信息。 WAVE文件格式说明表
偏移地址
字节数
数据类型
内 容
文件头
00H
Char
"RIFF"标志
04H
int32
文件长度
08H
Char
"WAVE"标志
0CH
Char
"fmt"标志
10H
过渡字节(不定)
14H
int16
格式类别
16H
int16
通道数
18H
int16
采样率(每秒样本数),表示每个通道的播放速度
1CH
int32
波形音频数据传送速率
20H
int16
数据块的调整数(按字节算的)
22H
每样本的数据位数
24H
Char
数据标记符"data"
28H
int32
语音数据的长度
解答: 将WAV文件格式定义为结构体WAVEFORMAT:
typedef struct tagWaveFormat{ char cRiffFlag[4]; UIN32 nFileLen; char cWaveFlag[4]; char cFmtFlag[4]; char cTransition[4]; UIN16 nFormatTag ; UIN16 nChannels; UIN16 nSamplesPerSec; UIN32 nAvgBytesperSec; UIN16 nBlockAlign; UIN16 nBitNumPerSample; char cDataFlag[4]; UIN16 nAudioLength; } WAVEFORMAT; 假设WAV文件内容读出后存放在指针buffer开始的内存单元内,则分析文件格式的代码很简单,为:
WAVEFORMAT waveFormat;memcpy( &waveFormat, buffer,sizeof( WAVEFORMAT ) );
直接通过访问waveFormat的成员,就可以获得特定WAV文件的各项格式信息。 剖析:试题6考查面试者组织数据结构的能力,有经验的程序设计者将属于一个整体的数据成员组织为一个结构体,利用指针类型转换,可以将memcpy、memset等函数直接用于结构体地址,进行结构体的整体操作。 透过这个题可以看出面试者的程序设计经验是否丰富。
试题7:编写类String的构造函数、析构函数和赋值函数,已知类String的原型为:
class String{ public: String(const char *str = NULL); // 普通构造函数 String(const String &other); // 拷贝构造函数 ~ String(void); // 析构函数 String & operate =(const String &other); // 赋值函数 private: char *m_data; // 用于保存字符串 };解答:
//普通构造函数String::String(const char *str) { if(str==NULL) //加分点:对m_data加NULL 判断 { m_data = new char[1]; // 得分点:对空字符串自动申请存放结束标志''的空 *m_data = ''; } else { int length = strlen(str); m_data = new char[length+1]; // 若能加 NULL 判断则更好 strcpy(m_data, str); }}// String的析构函数String::~String(void) { delete [] m_data; // 或delete m_data; }//拷贝构造函数String::String(const String &other) // 得分点:输入参数为const型{ int length = strlen(other.m_data); m_data = new char[length+1]; //加分点:对m_data加NULL 判断 strcpy(m_data, other.m_data); }//赋值函数String & String::operate =(const String &other) // 得分点:输入参数为const型{ if(this == &other) //得分点:检查自赋值 return *this; delete [] m_data; //得分点:释放原有的内存资源 int length = strlen( other.m_data ); m_data = new char[length+1]; //加分点:对m_data加NULL 判断 strcpy( m_data, other.m_data ); return *this; //得分点:返回本对象的引用} 剖析: 能够准确无误地编写出String类的构造函数、拷贝构造函数、赋值函数和析构函数的面试者至少已经具备了C++基本功的60%以上! 在这个类中包括了指针类成员变量m_data,当类中包括指针类成员变量时,一定要重载其拷贝构造函数、赋值函数和析构函数,这既是对C++程序员的基本要求,也是《Effective C++》中特别强调的条款。 仔细学习这个类,特别注意加注释的得分点和加分点的意义,这样就具备了60%以上的C++基本功! 试题8:请说出static和const关键字尽可能多的作用 解答:
static关键字至少有下列n个作用: (1)函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值; (2)在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问; (3)在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内; (4)在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝; (5)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。 const关键字至少有下列n个作用: (1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了; (2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const; (3)在一个函数声明中const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值; (4)对于类的成员函数,若指定其为const类型则表明其是一个常函数,不能修改类的成员变量; (5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。 例如:const classA operator*(const classA& a1,const classA& a2);
operator*的返回结果必须是一个const对象。如果不是,这样的变态代码也不会编译出错:
classA a, b, c;(a * b) = c; // 对a*b的结果赋值
操作(a * b) = c显然不符合编程者的初衷,也没有任何意义。 剖析: 惊讶吗?小小的static和const居然有这么多功能,我们能回答几个?如果只能回答1~2个,那还真得闭关再好好修炼修炼。 这个题可以考查面试者对程序设计知识的掌握程度是初级、中级还是比较深入,没有一定的知识广度和深度,不可能对这个问题给出全面的解答。大多数人只能回答出static和const关键字的部分功能。 4.技巧题
试题1:请写一个C函数,若处理器是Big_endian的则返回0;若是Little_endian的则返回1 解答:
int checkCPU(){ { union w { int a; char b;} c; c.a = 1; return (c.b == 1); }}剖析: 嵌入式系统开发者应该对Little-endian和Big-endian模式非常了解。采用Little-endian模式的CPU对操作数的存放方式是从低字节到高字节,而Big-endian模式对操作数的存放方式是从高字节到低字节。例如,16bit宽的数0x1234在Little- endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
内存地址
存放内容
0x4000
0x34
0x4001
0x12
而在Big-endian模式CPU内存中的存放方式则为:
内存地址
存放内容
0x4000
0x12
0x4001
0x34
32bit宽的数0x12345678在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
内存地址
存放内容
0x4000
0x78
0x4001
0x56
0x4002
0x34
0x4003
0x12
而在Big-endian模式CPU内存中的存放方式则为:
内存地址
存放内容
0x4000
0x12
0x4001
0x34
0x4002
0x56
0x4003
0x78
联合体union的存放顺序是所有成员都从低地址开始存放,面试者的解答利用该特性,轻松地获得了CPU对内存采用Little-endian还是Big-endian模式读写。如果谁能当场给出这个解答,那简直就是一个天才的程序员。试题2:写一个函数返回1+2+3+…+n的值(假定结果不会超过长整型变量的范围) 解答:
int Sum( int n ){ return ( (long)1 + n) * n / 2; //或return (1l + n) * n / 2;} 剖析: 对于这个题,只能说,也许最简单的答案就是最好的答案。下面的解答,或者基于下面的解答思路去优化,不管怎么“折腾”,其效率也不可能与直接return ( 1 l + n ) * n / 2相比!
int Sum( int n ){ long sum = 0; for( int i=1; i<=n; i++ ) {sum += i;} return sum;} 所以程序员们需要敏感地将数学等知识用在程序设计中。
6.C++中引用与指针的区别; 答:1 引用实际上是所引用的对象或变量的别名,而指针是包含所指向对象或变量的地址的变量。 2 引用在定义时必须初始化,而指针在定义时不初始化。 3 不可以有努NULL的引用,而可以有指向NULL的指针。 4 引用在初始化后不可以改变引用关系,而指针可以随时指向其他对象(非const指针)。
7.C++中virtual与inline的含义分别是什么? 答:在基类成员函数的声明前加上virtual关键字,意味着将该成员函数声明为虚函数。 inline与函数的定义体放在一起,使该函数称为内联。inline是一种用于实现的关键字,而不是用于声明的关键字。虚函数的特点;如果希望派生类能够重新定义基类的方法,则在基类中将该方法定义为虚方法,这样可以启用动态联编。内联函数的特点;使用内联函数的目的是为了提高函数的运行效率。内联函数体的代码不能过长,因为内联函数省去调用函数的时间是以代码膨胀为代价的。内联函数不能包含循环语句,因为执行循环语句要比调用函数的开销大。一个函数能否即是虚函数又是内联函数?可以,建议不使用?
8.以下关键字的含义与用法:extern,extern “C”,static,explicit,register,#undef,#ifndef
9.什么是函数重载与覆盖?为什么C不支持函数重载?为什么C++能支持函数重载?10.VC中,编译工具条内的Debug与Release选项是什么含义?
Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。Debug带有大量的调试代码,运行时需要相应的运行库,发布模式程序紧凑不含有调试代码和信息,直接可以运行(如果不需要运行库)
11.编写my_memcpy函数,实现与库函数memcpy类似的功能,不能使用任何库函数; void* mymemcpy(void* pvTo, const char* pvFrom, size_t size) { assert((dest != NULL) && (src != NULL)); byte* psTo = (byte*)pvTo; byte* psFrom = (byte*)pvFrom; while (size-- > 0) {*psTo++ = *psFrom++;} return pvTo; }
12.编写my_strcpy函数,实现与库函数strcpy类似的功能,不能使用任何库函数; 答:char* my_strcpy(char* strdest, const char* strsrc) { assert((strdest != NULL) && (strsrc != NULL)) char* address = strdest; while((*strdest++ = *strsrc++) != NULL) return address; }
13.编写gbk_strlen函数,计算含有汉字的字符串的长度,汉字作为一个字符处理;已知:汉字编码为双字节,其中首字节<0,尾字节在0~63以外;(如果一个字节是-128~127)
14.函数assert的用法?答:断言assert是仅在debug版本起作用的宏,用于检查“不应该“发生的情况。程序员可以把assert看成一个在任何系统状态下都可以安全使用的无害测试手段。
15.为什么在头文件的最前面都会看到这样的代码:#ifndef _STDIO_H_#define _STDIO_H_
头文件中的#ifndef一般格式是这样的#ifndef <标识> ,#define <标识>;<标识>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:stdio.h
#ifndef _STDIO_H_ #define _STDIO_H_
16.为什么数组名作为参数,会改变数组的内容,而其它类型如int却不会改变变量的值?答:当数组名作为参数时,传递的实际上是地址。而其他类型如int作为参数时,由于函数参数值实质上是实参的一份拷贝,被调函数内部对形参的改变并不影响实参的值。
选择、填空、简答、程序都有。基本上是C和网络方面的DD,还有几道概率和推理
10道选择,大多数是C的,50分,然后两题填空,20分,第二题不是编程,是个数学题。第三部分写两个函数,3 0分,第一题是把一个unsigned long的数转成一个IP地址输出,应该很容易的,结果自己想复杂了,浪费了不少时间,最后还没做对,晕。第二题是两个长度为N的数字字符串相加,结果保存在一个长度为N+1的字符串里,思路倒是很清楚,后来发现好像在处理进位和前一位的和的时候还有进位的问题,但是懒得改了,就这样吧。最后一部分是附加题,10题选择,20分,内容主要是和IP网络有关的,笔试中有英译汉。请翻译一下ipv6的路由发现机制。是将arp和irdp和icmp重定向的融合等等。
1 H.323协商。(笔试题) 2 ipsec为什么是三层的。l2tp为什么是二层的? 答:ipsec是需要三层IP路由的。l2tp是打穿的。 反问:那l2tp不需要ip吗? 无语。 3 ospf中包的ttl值是多少?(回忆不清了。可能是吧。但没听说过有介绍啊。) 4 为什么要划分区域? 答:用来防止LSA在整个区域内泛洪。减少对CPU和内存的损耗。 反问:那area 0的一条路由条目发生了变化。area 1要不要知道呢? 答:要。 反问:既然要的话,那不还是要泛洪吗?那划分区域的话就没有什么意义了嘛。 答:可以通过缺省路由的方式或建立stub区域等方法。 反问:正面回答。 无语。 5 MPLS VPN的标签一共有几层。内网的标签放在哪里。 答:骨干里传递一层。到Mp-ibgp邻居一层。跨域一层。好象TE还可以加一层标签。内网的标签放在lfib表里。 对方没怎么做声。但估计答得不好。 (我有一点不明,MPLS标签有分内网和外网吗?) 6 MPLS中RD和RT的作用分别是什么? 答:RD的作用是允许VPN用户地址的重叠。RT可以用来区分不同的VPN用户。控制路由条目的出口入口策略。 反问:既然RT可以区分不同的VPN用户。那RD有什么用。地址重叠那是你的规划没做好。 答:RD是肯定要的。 反问:为什么?不是有RT可以区分用户吗? 无语。 7 RR防止环路的机制。 答:两个属性originate id。包含了始发这条路由的路由器的route-id,因此RR不会将此路由又重新发回给源。 一个是cluster-id。包含了RR的route-id。 8 BGP控制out-bound用local-pre,控制进来的用med.(笔试题) 9 ospf是工作在哪个协议上的?(可能是我记不清了?) 10 ospf的LSA类型。 答:(这个我不打字了。大家应该都知道吧。) 11 简述OSPF的基本工作机制。 答:(昨晚补了下卷一)一。向邻接路由器发出hello包。根据hello包中携带的area id ,hello time,dead interval,stub标记。如果都相同的话。建立起邻居关系。 二 向邻居发送链路状态更新包. (根据ospf 类型而定。如果是broadcast和nbma的话,由DR发出)三 收到邻居路由器发来的更新包后,以自己为根,根据 spf算法建立一条无环路的路径。四在整个区域内泛洪。五整个区域内的database同步。六数据库稳定后,hello包变为keepalive报文,30min发送一次。 (回答肯定不是很好。请高手指正) 12 ppp的lcp和ncp协商过程。 答:(说得不好。基本无语) 13 笔试中还有一道PSTN的信令控制有哪三种?(笔试题) 14sloari 8.0查看进程的命令是什么?linux 7.3查看IP的命令是什么?(笔试题) 15 IP是5.32.0.0,掩码255.224.0.0。请问最大的有效地址是多少。(笔试题) 16 下列哪一项不属于于7号信令标准?(选择。我乱蒙了一个) 17 lx/???的有效距离是多少?我选的10km 18 IP 包头几个字节?加上数据部分几个字节19 QOS有一点点。 随便蒙吧,反正这方面对方问得不是很细。把你知道的说出来就可以了。 20 CQ能不能有一种流量统治第二种流量,(由于是英文,dominate)? (笔试题) 21 FTP下载一个文件完成。有几个TCP连接??四次 (笔试题)
snmp,arp,ospf协议,c++的异常处理,局部静态变量 ,全局变量的存放问题,
一道是测试时的那个覆盖问题,一道是int型溢出问题(没考虑到!),其他的基本满分^_^程序题为把一个un int转4进制村数组,考验编程的严密性,还有一道是比较发散的思路题
前面50分10个选择题,前七个是C程序,后三个数学题。都计较简单,呵呵。中间是两个填空题,各填三空,题一为比较两个输入字符串的大小,简单。题二是填写程序注释,对内存进行操作方面的,如检查内存溢出,内存泄漏,避免产生野指针之类的。后面是两道综合题,题一写C程序函数,将一个整数转换为4进制的字符串;题二要求提供解决一个代理服务器由于应答无响应而导致的资源得不到释放的解决方案。题一简单,题二偶就模仿TCP虚电路连接的算法,写了一下自己的思路和主要步骤,感觉应该不会偏得很远!
做完这些题后还剩十分钟,后面还有10道选择题为通讯知识题,为附加题,都是关于网络,偏数据链路层和网络层的知识,
刚刚考完华为3com的软件笔试,从9:00-10:00,共一小时。 前面50分10个选择题,前七个是C程序,后三个数学题。 中间是两个填空题,各填三空,题一为比较两个输入字符串的大小,简单。题二是填写程序注释,对内存进行操作方面的,如free(p)什么作用。 后面是两道综合题,题一写C程序函数,将一个整数转换为4进制的字符串;题二要求提供解决一个代理服务器由于应答无响应而导致的资源得不到释放的解决方案。 最后20分共10道选择题为通讯知识题,关于路由器,网络方面的知识,如果看过的话不难。
第一面技术面,还有点挑战,简单介绍自己后,技术gg就开始正式发起技术进攻了,首先问了,指针函数和函数指针的区别,欧最讨厌这种绕口令式的问题了,不就一个是指针,然后该指针指向一个函数,另一个是一个函数,返回一个指针啊!简单一句话就是指与被指的关系。呵呵,不过当时紧张,绕了一小会!然后技术gg又丢出一个问题,一个单向链表怎样实现,快速查找。偶立马想到了数组的二分查找,就告诉他,给单链表增加一个数据项表示它的序号,然后用类数组二分查找算法开始查找。技术gg立马之处偶的错误之处,要是改链表要插入删除的话,改序号很麻烦,而且该单向链表有个条件要排好序的,唉!这可为难我了!:(过了一小会,技术gg笑着说给你降低点难度,假设单链表是排好序的,偶还是没有放弃二分查找,偶就回答,常规查找是指针一位一位的移,我可以一次多移几位,然后缩小查找范围,技术gg问偶这样做有什么好处,偶说可以减少比较次数,他想了会说,嗯,这个方法不错,偶正打算得意的笑,他又问还有其他方法吗?偶晕,还不肯放过!!偶回答,应该可以把单链表转化成排序二叉树吧,这样查找,插入,删除,就都很easy了!技术gg听了,略加思索,说,嗯这个方法不错!然后就要偶等二面,唉,终于pass啦!
二面是hr面,基本都是常规问题,什么别人对我的评价啊,自己的职业规划啊,可不可以提前上班啊,云云!二面完后填了个表,都到11点了,被通知不早了,明天早上继续三面。
今天早上8点就爬起来了,8点20多一点到大活,准备最后一面。到了一看,门还没开呢,只有一个面试的mm等在那里!他们也真准时,8:28终于看到工作人员了!说是8:30面试,可是三面的面试官,迟迟未出现,终于等啊等啊,等了一个小时,才听到了偶的名字,偶跟着面试官刚到指定位置,面试官的手机响了,偶狂晕,中途杀出个电话来!此时偶的肚子已经在打鼓了,唉!十几分钟后电话终于讲完了,面试开始了,介绍了下自己的项目,由于不对口,所以他也没有仔细问,然后就是自己的优点,还有就是问偶面华为没?
c题目比较多,一道网络选择,几道操作系统题目选择,还有两道大题,关于双向链表和哈希算法的。内存拷贝memcopy问题,插入排序问题
笔试20题通讯多选,10题计算机多选。 通讯考的都是基础知识,但是不是通讯专业的一般都要靠摸的,计算机考的比较有深度,主要是c和c++;类的继承关系(初始化顺序);二*树的先序、中序、后序,知道其中两个,推导剩下的一个;i++和++i问题。例如: int i=1; int j; j=(++i)+(++i)+(++i)+(++i);//j=15
int i=1; int j; j=(++i)+(++i)+(i++)+(i++)+(++i);//j=16 moto面试中英语口语很重要,技术面试一开始就是用英语的,后来不行了,只好用中文。第二面的那个人力资源的主管就是全英文了,一路面下来不吐一个汉字。二面的时候主要就是聊天了,问问你的爱好,和同学关系什么的,主要是看口语,以及你个人的性格。moto待遇6000+770。干满三年一次性发6000*20%的住房公积金
//计算字符串长度的函数 int strlen_mod(char* str) { int count = 0; while(str[count] != '/0') { ++count;} return count; } //将字符串反转输出的函数 void print_reverse(char* str) { size_t size = strlen(str); int size2= strlen_mod(str); printf("The length of %s is %d ### %d/n", str, size, size2); int i; char temp = ' '; for(i=0; i < size/2; i++) { printf("%d/n", i); temp = str[i]; str[i] = str[size - 1 - i]; str[size - 1 - i]= temp; } printf("Reverse string: %s/n", str); }
What will print out? 输出结果是?main() { char *p1=“name”; char *p2; p2=(char*)malloc(20); memset (p2, 0, 20); // while(*p2++ = *p1++); printf(“%sn”,p2); } Answer:empty string. What will be printed as the result of the operation below:main() { int x=20,y=35; x=y++ + x++; y= ++y + ++x; printf(“%d%dn”,x,y); } Answer : 5794
What will be printed as the result of the operation below:main() { int x=5; printf(“%d,%d,%dn”,x,x< <2,x>>2); }Answer: 5,20,1 What will be printed as the result of the operation below:#define swap(a,b) a=a+b;b=a-b;a=a-b; void main(){ int x=5, y=10; swap (x,y); printf(“%d %dn”,x,y); swap2(x,y); printf(“%d %dn”,x,y); } int swap2(int a, int b) { int temp; temp=a; b=a; a=temp; return 0; } Answer: 10, 510, 5
What will be printed as the result of the operation below:main(){ char *ptr = ” Cisco Systems”; *ptr++; printf(“%sn”,ptr); ptr++; printf(“%sn”,ptr); } Answer:Cisco Systemsisco systems What will be printed as the result of the operation below:main(){ char s1[]=“Cisco”; char s2[]= “systems”; printf(“%s”,s1); } Answer: Cisco What will be printed as the result of the operation below:main(){ char *p1; char *p2; p1=(char *)malloc(25); p2=(char *)malloc(25); strcpy(p1,”Cisco”); strcpy(p2,“systems”); strcat(p1,p2); printf(“%s”,p1); } Answer: Ciscosystems The following variable is available in file1.c, who can access it?:static int average;Answer: all the functions in the file1.c can access the variable. WHat will be the result of the following code?#define TRUE 0 // some code while(TRUE) { // some code } Answer: This will not go into the loop as TRUE is defined as 0. What will be printed as the result of the operation below:int x; int modifyvalue() { return(x+=10); } int changevalue(int x) { return(x+=1); } void main(){ int x=10; x++; changevalue(x); x++; modifyvalue(); printf("First output:%dn",x); x++; changevalue(x); printf("Second output:%dn",x); modifyvalue(); printf("Third output:%dn",x); } Answer: 12 , 13 , 13 What will be printed as the result of the operation below:main(){ int x=10, y=15; x = x++; y = ++y; printf(“%d %dn”,x,y); } Answer: 11, 16 What will be printed as the result of the operation below:main(){ int a=0; if(a==0) printf(“Cisco Systemsn”); printf(“Cisco Systemsn”); } Answer: Two lines with “Cisco Systems” will be printed.
*182 A 在C语言中,一维数组的定义方式为:,类型说说明符 数组名__。 A) [常量表达式] B) [整形表达式] c)[ 整型常量]或[整型表达式] D)[整型常量] *183 C 以下能对一维数组a进行正确初始化的语句是__。 A) int a[10]=(0,0,0,0,0) B)int a[10]={} C) int a[]={0};D) int a[10]={10*1}; *184 C 以下对二维数组a的正确说明是__。 A) int a[3][]; B) floatf a(3,4); c) double a[1][4]; D) float a(3)(4); *185 C 若有说明:int a[3][4]; 则对a数组元素的正确引用是__。 A) a[2][4] B) a[1,3] C) a[1+1][0] D) a(2)(1); *186 D 若有说明:int a[3][4];则对a数组元素的非法引用是__。 A) a[0][2*1] B) a[1][3] C)a[4-2][0] D)a[0][4]" " *187 B 以下能对二维数组a进行正确初始化的语句是__。 A) int a[2][]={{1,0,1},{5,2,3}}; B) int a[][3」={{1,2,3},{4,5,6}}; C) int a [2][4]={{1,2,3},{4,5},{6}}; D) int a[][3={{1,0,1},{},{1,1}}; *188 C 以下不能对二维数组a进行正确初始化的语句是__。 A) int a[2][3]={0}; B) int a[][3」={{1,2,3},{4,5,6}}; C) int a[2][4]={{1,2,3},{4,5}{6}}; D) int a[][3]={{1,0,1},{0},{1,1}}; *189 D 若有说明: int a[3]「4]={0};则下面正确的叙述是 A)只有元素a[0][0]可得到初值0 B)此说明语句不正确:。 C)数组a中各元素都可得到初值,但其值不一定为0。 D)数组a中每个元素均可得到初值0 *190 D 若有说明:int a[][4]={0,0};则下面不正确的叙述是__。 A)数组a的每个元素都可得到初值0 B)二维数组a的第一维大小为1 C)因为二维数组0中第二维大小的值除以初值个数的商为1,故数组a行 数为1 D)只有元素a[0]「0」和a[0]「1」可得初值0,其余元素均得不到初值0 *191 B 若有说明:int a[3]「4];则数组a各元素 A)可在程序的运行阶段得到初值0 B)可在程序的编译阶段得到初值0 C)不能得到确定的初值D)可在程序的编译或运行阶段得初值0 *192 C 以下各组选项中,均能正确定义二维实型数组a的选项是 A)float a[3][4]; B)float a(3,4); float a[][4]; float a[3][4]; float a[3][]={{1},{0}}; float a[][]={{0},{0}}; C)float a[3][4]; D)float a[3][4]; static float a[][4]={{0},{0}}; float a[3][]; auto float a[][4]={{0},{0},{0}}; float a[][4] *193 A 下面程序(每行程序前面的数字表示行号) 1 main() 2 { 3 int a[3]={3*0}; 4 int i; 5 for(i=0;i<3;i++) scanf("%d",&a[ i]); 6 for(i=1;i<3;i++) a[0]=a[0]+a[ i] ; 7 printf("%d/n",a[0]); }A)没有错误B)第3行有错误 C)第5行有错误 D)第7行有错误 *194 C 下面程序一一一(每行程序前面的数字表示行号)。 1 main() 2 { 3 float a[10]={0.0}; 4 int i 5 for(i=0;i<3;i++) scanf("%d",&a[ i]); 6 for(i=0;i<10;i++) a[0]=a[0]+a[ i]; 7 printf("%d/n",a[0]); 8 } A)没有错误 B)第3行有错误 C)第5行有错误 D)第7行有错误 *195 D 下面程序有错的行是 1 main() 2{ 3 int a[3]={1}; 4 int i; 5 scanf("%d",&a); 6 for(i=1;i<3;i++) a[0]=a[0]+a[ i]; 7 printf("a[0]=%d/n",a[0]); 8 } A)3 B)6 C)7 D)5 *196 D 下面程序(每行程序前面的数字表示行号) 1 main() 2 { 3 int a[3]={0}; 4 int i; 5 for(i=0;i<3;i++)scanf("%d",&a[ i]); 6 for(i=1;i<4;i++)a[0]=a[0]+a[ i]; 7 printf("%d/n",a[0]); 8 } A)没有错误 B)第3行有错误 C)第5行有错误 D)第6行有错误 *197 D 若二维数组a有m列,则计算任一元素a[ i][j]在数组中位置的公式为 (假设a[0][0]位于数组的第一个位置上。) A)i*m+j B)j*p+i。C)i*m+j-1 D)i*m+j+1 *198 B 对以下说明语句的正确理解是 int a[10]={6,7,8,9,10}; A)将5个初值依次赋给a[1]至a[5] B)将5个初值依次赋给a[0]至a[4] C)将5个初值依次赋给a[6]至a[10] D)因为数组长度与初值的个数不相同,所以此语句不正确 *199 B 以下不正确的定义语句是__. A) double x[5]={2.0,4.0,6.0,8.0,10.0}; B) int y「5」={0,1,3,5,7,9}; C) char c1[]={’1’,’2’,’3’,’4’,’5’}; 4 。二入广 / "’ (: D) char c2[]=}{'/x10','/xa','/x8'}; *200 B 若有说明:int a[」「3」={1,2,3,4,5,6,7};则a数组第一维的大小是__. A) 2 B) 3 C) 4 D)无确定值 *201 B 若二维数组a有m列,则在a[ i][j]前的元素个数为__. A)j*m+j B)i*m+j C)i*m+j D)i*m+j+1 *202 A 定义如下变量和数组: int k; int a[3][3]={1,2,3,4,5,6,7,8,9}; 则下面语句的输出结果是 。" for(k=0;k<3;k++) printf ("%d",a[k][2-k]); A) 3 5 7B)3 6 9 C) 1 5 9 D) 1 4 7 *203 B 若有以下程序段: ...... int a[]={4,0,2,3,1};i,j,t; for(i=1;i<5;i++) {t=a[ i];j=i-1; while(j>=0&&t>a[j]) {a[j+1]=a[j];j--;} ...... 则该程序段的功能是 __. A)对数组a进行插入排序(升序) B)对数组a进行插入排序(降序) C)对数组a进行选择排序(升序) D)对数组a进行选择排序(降序) *204 D 以下正确的定义语句是__. A) int a[1」[4」={1,2,3,4,5}; B) float x[3][]={{1},{2},{3}}; C) long b[2][3]={{1},{1,2},{1,2,3}}; D) double y[][3]={0}; *205 C 下面程序的运行结果是__.main() {int a[6」「6」,i,j; for(i=1;i<6;i++) for(j=1;j<6,j++) a[ i][j]=(i/j)*(j/i); for(i=1;i<6;i++) {for(j=1;j<6;j十十) printf("%2d",a[ i][j]); printf("/n"_);} } A)11111 B)00001 C)10000 D)10001 11111 00010 01000 01010 11111 00100 00100 00100 11111 01000 00010 01010 11111 10000 00001 10001 *206 C 下面程序的运行结果是 __. main() {int a[6],i; for(i=1;i<6;i十十) {a[ i]=9*(i-2+4*(i>3))%5; printf("%2d",a[ i]); } } A)—40404B)—40403 C)一40443D)一40440 *207 D 下面是对s的初始化,其中不正确的是__. A) char s[5」={"abc"} B)char s[5]={'a','b','c'}; C) char s[5]="" D) char s[5]="abcdef"; *208 B 下面程序段的运行结果是 __. char c[5]={'a','b','/0','c','/0'}; printf("%s",c);} A)’a’’b’ B) ab C) ab c D) ab (其中 表示空格) *209 D 对两个数组a和6进行如下初始化 char a[]="ABCDEF"; char b[]={’A’,’B’,’C’,’D’,’E’,’F’};卜 则以下叙述正确的是 __. A) a与b数组完全相同 B) a与b长度相同 C) a和b中都存放字符串 D) a数组比b数组长度长 *210 B 有两个字符数组a、b,则以下正确的输入格式是 __. A) gets (a,b); B) scanf ("%s%s",a,b); C) scanf ("%s%s",&a,&b); D) gets ("a"), gets ("b"); *211 D 有字符数组a[80]和b[80],则正确的输出形式是__. A) puts (a,b); B) printf ("%s,%s,a[],b[]); C) putchar(a,b); D) puts (a), puts (b); *212 D 下面程序段的运行结果是__. char a[7]="abcdef"; char b[4]="ABC"; strcpy(a,b); printf ("%c",a[5]); J。 " 了 A)一 B)/O C) e D)f(其中一表示空格) *213 D 有下面的程序段 char a[3],b[]="china"; a=b; printf("%s",a); 则__. A)运行后•将输出Chm、"、B)运行后将输出Ch’一 C)运行后将输出Chi D)编译出错 *214 B 下面程序段的运行结果是__. char c[]="/t/v///0will/n"; printf("%d",strlen(c)); A)14 B) 3 C) 9 D)字符串中有非法字符,输出值不确定 *215 D 判断字符串a和b是否相等,应当使用__. A) if (a==b) B) if (a=b) C) if (strcpy(a,b)), D) if (strcmp(a,b)) *216 D 判断字符串s1是否大于字符串s2应当使用__. A) if (sl>s2) B) if (strcmp(s1,s2)) C) if (strcmp(s2,sl)>0) D) if (strcmp(s1,s2)>0) *217 A 下面程序段是输出两个字符串中对应字符相等的字符。请选择填空。
char x[]="programming";
char y[]="Fortran";
int i=0;
while (x[ i]!='/0'&& y[ i]!='/0')
{if (x[i ]==y[ i]) printf ("%c", 1 );
else i++;}
【1】A)x[i++] B)y[++i] C)x[ i] D)y[ i] *218 D 下面描述正确的是__. A)两个字符串所包含的字符个数相同时,才能比较字符串 B)字符个数多的字符串比字符个数少的字符串大 C)字符串"STOP "与"STOp"相等 D)字符串"hat"小于字符串"he"
*219 C 下述对C语言字符数组的描述中错误的是 A)字符数组可以存放字符串 B)字符数组的字符串可以整体输入、输出 C)可以在赋值语句中通过赋值运算符"="对字符数组整体赋值 D)不可以用关系运算符对字符数组中的字符串进行比较 *220 B 有已排好序的字符串a,下面的程序是将字符串s中的每个字符按a中元素的规律插入到a中。请选择填空。 #indude main() {char a[20」="cehiknqtw"; char s[]="fbla"; int i,k,j; for(k=0;s[k]!='/0';k十+) {j=0; while(s[k]>=a[j]&&a[j]!='/0')j++; for(i=str1en(a);i>=j;i--) 【2】; a[j」=S[k」; } puts(a); } 【2】 A) a[ i]=a[i+1] B) a[i+1]=a[ i]; C) a[ i]=a[i-1] D) a[i-1]=a[ i]; *221 A 下面程序的功能是将字符串5中所有的字符c删除。请选择填空。 #include main() {char s[80]; int i,j; gets(s); for(i=j=0;s[ i]!='/0';i++) if(s[ i]!='c')【1】 puts(s); 【1】A)s[j++]=s[ i] B)s[++j]=s[ i]; C) s[j]=s[ i];j++; D) s[j]=s[ i]; *222 B 下面程序的功能是从键盘输入一行字符,统计其中有多少个单词,单词之间 用空格分隔。请选择填空。 #indude main() {char s[80」,c1,c2=''; int i=0,num=0; gets(s); while(s[ i]!='/0') {c1=s[ i]; 1f(i==0) c2=''; else c2=s[i-1]; if(【1】) num++; i++; ) printf("There are %d words./n",num); } 【1】A)c1='' && c2=='' B)cl!='' && c2=='' C)c1=='' && c2!='' D)cl!='' && c2!='' *223 A 下面程序的运行结果是 #indude main() {char ch[7]={"12ab56"}; int i,s=0; for(i=0;ch[ i]>='0'&&ch[ i]<='9';i+=2) s=10*s+ch[ i]-'0'; printf("%d/n",s); } A)1 B)1256 C) 12ab56 D)1 2 5 6 *224 A 当运行以下程序时,从键盘输入:aa bb cc dd (表示回车),则下面程序的运行结果是 # include main() {char a1[5],a2[5」,a3[5],a4[5]; scanf("%s%s",a1,a2); gets(a3); gets(a4); puts(al); puts(a2); puts(a3); puts(a4); }
A) aa B) aa ()aa D) aa bb
bb bb bb cc
cc cc dd dd
cc dd dd ee
*225 D 当运行以下程序时,从键盘输入:ab c dd (表示回车),则下面程序的运行结果是 #include #difine N 6 main() { char c[N]; int i=0; for (;i for(i=0; i<N;&NBSP;&NBSP;I++)&NBSP;&NBSP;PUTCHAR(C[ 、 A)abcdef B)a C)b D)ab b c c c d d e f *226 A 当运行以下程序时,从键盘输入:AhaMA Aha( 则下面程序的运行结果是 #include "stdio.h" main() {char s[80],c='a'; int i=0; scanf("%s",s); while(s[ i]!='/0') {if(s[ i]==c) s[ i]=s[ i]-32; else if(s[ i]==c-32) s[ i]=s[ i]+32; i++; } puts(s); ) A)ahAMa B)AhAMa C) AhAMa ahA D) ahAMa ahA *227 D 下面程序的运行结果是一一一。 #include #inc1ude main() {char a[80」="AB",b[80]="LMNP"; int i=0; strcat(a,b); whi1e(a[i++]!='/0')b[ i]=a[ i]; puts(b); } A)LB B)ABLMNP C)AB D)LBLMNP *228 B 下面程序的运行结果是 #include main() { char str[]="SSSWLIA",c; int k; for(k=2;(c=str[k])!='/0';k++) {switch(c){case 'I':++k;break; case 'L':continue; default:putchar(c);continue; } putchar('*'); } } A)SSW* B)SW* C) SW*A D)SW *229 B 下面程序的运行结果是 #include main() {char a[]="morning",t; int i,j=0; for(i=1;i<7;i++) if(a[j]t=a[j];a[j]=a[7]; a[7]=a[j];puts(a); } A) mogninr B) mo C) morning D) mornin
实现思想:
把两个无序的数组首先排序,然后再按照链表结构把它们分别构造好,然后再把两个有序链表合并。
int const array1_size = 5;//数组1的长度int const array2_size = 7;//数组2的长度//链表结构体typedef struct ListNode{ int data; ListNode * next;}ListNode;
//合并两个有序链表返回不带头结点的头指针ListNode * MergeList(ListNode *p,ListNode *q){ ListNode *h,*r; h = new ListNode; h->next = NULL; r = h; while(p !=NULL && q != NULL) { if(p->data <= q->data) { r->next = p; r =p; p = p->next; } else { r->next = q; r =q; q = q->next;
} } if(p != NULL) r->next = p; else r->next = q; p = h; h = h->next; delete p; return h;}
//构造一个链表(没有头结点的)ListNode * GenerateList(int array[],int length){ ListNode * h,*temp,*old_head ; h = new ListNode; h->next = NULL; temp = h; for(int i = 0; i< length;i++) { ListNode *p = new ListNode; p->data = array[i]; temp->next = p; temp = p; } temp->next = NULL; old_head = h; h = h->next; delete old_head; return h;}//打印链表void Print_List(ListNode *h){ ListNode *p; p = h; for(;p!=NULL;p=p->next) printf("%d ",p->data);
}//引入冒泡排序算法
void Swap(int *a,int *b){ int temp; temp = *a; *a = *b; *b = temp;}void Bubble_Sort(int *array,int length){ int pass,j; for(pass =1;pass<=length-1;pass++) for(j=0;j<=length-2;j++) if(array[j]>array[j+1]) Swap(&array[j],&array[j+1]);}
/*********************OK,所有准备工作已经做好,开始main()函数**********/
//输入字符表示结束int _tmain(int argc, _TCHAR* argv[]){ char end; int List1[array1_size]={9,5,6,10,45}; int List2[array2_size]={3,1,4,6,7,9,0}; Bubble_Sort(List1,array1_size); Bubble_Sort(List2,array2_size); ListNode * m_list1,*m_list2,*m_list; m_list1 = GenerateList(List1,array1_size); m_list2 = GenerateList(List2,array2_size); m_list = MergeList(m_list1,m_list2); Print_List(m_list); scanf("%c",&end); return 0;}
class A{public: A(){ doSth(); } virtual void doSth(){printf("I am A");}};class B:public A{public: virtual void doSth(){ printf("I am B");}};B b;
执行结果是什么?为什么?答:执行结果是I am A因为b对象构造时调用基类A的构造函数A(),得此结果。
3、在STL的应用中 map这种key-value的应用很多,如果key的类型是GUID,该如何处理?答:谁知道怎么处理补上吧。4、一个内存变量a=5,有5个线程需要对其进行操作,其中3个对a进行加1操作,2个对a进行减1操作,为了保证能够得到正常结果6,需要使用什么方法?(列出越多越好)答:即要求列出线程同步方法,具体答案可见下面一题。5、描述并比较以下对象:事件,信标,临界区,互斥对象。答:这些对象都是用于线程同步的对象。临界区:一种保证在某一时刻只有一个线程能访问数据的简便办法。它只可以在同一进程内部使用。主要API函数有,产生临界区:InitializeCriticalSection,删除临界区:DeleteCriticalSection,进入临界区:EnterCriticalSection,退出临界区:LeaveCriticalSection。互斥对象:互斥对象跟临界区相似,但它不仅仅能够在同一应用程序不同线程中实现资源的安全共享,而且可以在不同应用程序的线程之间实现对资源的安全共享,当然下面两者也有这个特点。主要API函数有,创建互斥量: CreateMutex,打开一个存在的互斥量: OpenMutex,释放互斥量的使用权:ReleaseMutex,关闭互斥量:CloseHandle。信标:使用信号量(信标)最重要用途是:信号允许多个线程同时使用共享资源,它指出了同时访问共享资源的线程最大数目。它的API函数和使用方法都与互斥对象相似,如创建信号灯:CreateSemaphore,传入的参数可以指定信号灯的初始值。事件:用来通知其他进程/线程某件操作已经完成。API函数有创建,打开事件对象等,特殊点的是可以用函数SetEvent人工设置事件为有无信号状态,因此创建事件对象时可以有两种方式,一种为自动重置,一种为人工重置。只有人工重置方式创建的事件对象才能正确使用函数SetEvent。鉴于本套题考的是VC,有必要说明的是在MFC中对于各种同步对象都提供了相对应的类CCtiticalSection,CMutex,CSemaphore ,CEvent,另外为使用等待功能封装了两个类:CSingleLock和CMultiLock。这些类方便了使用这些同步对象。6、cdecl、stdcall、fastcall是什么?哪种可以实现个数不定的入口参数,为什么?答:三者都是函数调用的约定。cdecl:c declare(C调用约定)的缩写,是C和C++程序的缺省调用方式,规则是,按从右至左的顺序压参数入栈,由调用者把参数弹出栈,对于传送参数的内存栈是由调用者来维护的,正因为如此,只有这种调用方式可实现个数不定的入口参数(可变参数)。stdcall:是Pascal程序的缺省调用方式,规则是,按从右至左的顺序压参数入栈,被调用的函数在返回前清理传送参数的内存栈。上两者的主要区别是前者由调用者清理栈,后者由被调用的函清理栈。当然函数名的修饰部分也是不同的。fastcall:采用寄存器传递参数,特点就是快了。二、程序设计(以下题目请写出实现代码)1、有一段文本,统计其中的单词数。例如:As a technology , "HailStorm" is so new that it is still only known by its code name.注意:单词间的间隔不一定是一个空格。答:可执行程序代码如下,假设该文本已存入text这个数组里。
#include <iostream.h>#define QUEEN 8 //皇后数量int queen[QUEEN] ; //下标代表所在列号,值代表所在行号, //如queen[1]=2表示第1列第2行有个皇后bool row_YN[QUEEN] ; //棋局的每一行是否有棋,有则为1,无为0 ;bool passive_YN[2*QUEEN-1] ; //斜率为1的斜线方向上是否有棋,共有2*QUEEN-1个斜线bool negative_YN[2*QUEEN-1] ; //斜率为负1的斜线方向上是否有棋//用全局变量,因全局数组元素值自动为0int main(){ int row = 0 ;//游标,当前移动的棋子(以列计) bool flag = false ; //当前棋子位置是否合法 queen[0] = -1 ; //第0列棋子准备,因一开始移动的就是第0列棋子 int count = 0 ; //一共有多少种解法的计数器 ; while(row>=0 ) //跳出条件是回溯到无法回溯时 { queen[row]++ ; //row列上的皇后走到下一行试试 if(queen[row] >= QUEEN) //当前列全部走完 { queen[row] = -1 ; //当前列棋子置于准备状态 row-- ; //回溯到上一列的棋子 if(row>=0) //回溯时要清理如下行,斜线的标志位 { row_YN[queen[row]] = false ; passive_YN[queen[row] + row] = false ; negative_YN[QUEEN-1 + row - queen[row]] = false ; } } else { //先判断棋子所在行没有棋子 if(row_YN[queen[row]] == false) { flag = true ; //以下检查当前棋子是否与之前的棋子斜线相交 if( passive_YN[queen[row] + row] == true || negative_YN[QUEEN-1 + row - queen[row]] == true) flag = false ; else flag = true ; if(flag) // flag为真表示位置合法 { if(row == QUEEN-1) //列到达最后,即最后一个皇后也找到位置,输出解 { count++ ; //解法的数目加一 ; cout<<"***第"<<count<<"种解法***"<<endl ; for(int i=0;i<QUEEN;i++) cout<<"第"<<i<<"列皇后在第"<<queen[i]<<"行"<<endl; } row_YN[queen[row]] = true ;// 当前行设为有棋子 passive_YN[queen[row] + row] = true ;//当前行正斜率方向有棋子 negative_YN[QUEEN-1 + row - queen[row]] = true ; //当前行负斜率方向上也有棋子 row++ ; if(row >= QUEEN) { // 找到解后再次回溯找另外的解,这同上面无解回溯是一样的 row-- ; row_YN[queen[row]] = false ; passive_YN[queen[row] + row] = false ; negative_YN[QUEEN-1 + row - queen[row]] = false ;//原理同回溯 } flag = false ; } } } } cout<<QUEEN<<"皇后问题一共有"<<count<<"种解法"<<endl ; return 0 ;}
3、输入二个64位的十进制数,计算相乘之后的乘积。答:以下代码为网上别人贴出的,输入任意位数十进制数(包括小数,负数)都可以得出正确结果。思路是:将大数当作字符串进行处理,也就是将大数用10进制字符数组进行表示,然后模拟人们手工进行“竖式计算”的过程编写乘法。
#include <iostream.h>#define MAX 100int str_num(char str[]) //计算字符串的长度,等效于strlen(str);{ int i=0,num_str=0; while(str[i]!=0) {num_str++; i++; } return(num_str);}void place(int num_str,char str[]) //将字符串高低颠倒。{ int temp=0,i=0,j=0; for(i=0,j=num_str-1;i<j;i++,j--) {temp=str[j]; str[j]=str[i]; str[i]=temp; }}void transition(unsigned int a[],char str1[]) //数字字符转化为数字。{ int i=0; while(str1[i]!=0) {a[i]=str1[i]-'0'; i++; }}void multiply_int(unsigned int a[],unsigned int b[],unsigned int c[]) //大数相乘算法,入口为整形数组。{ int i=0,j=0; for(i=0;i<MAX;i++) for(j=0;j<MAX;j++) { c[i+j]+=a[i]*b[j]; c[i+j+1]+=c[i+j]/10; c[i+j]%=10; }}void output(int sign,unsigned int c[],int quan) //数据输出。{ int sign_temp=0,i=0; cout<<"The result is: "; if(sign==1) cout<<"-"; for(i=MAX-1;i>-1;i--) { if(sign_temp==0) {if(c[i]!=0) sign_temp=1; } if(sign_temp==1) { if(i==quan-1) cout<<"."; cout<<c[i]; c[i]=0; } } cout<<endl;}void multiply_string(char str1[],char str2[],unsigned int c[]) //大数相乘,入口为字符串。{ unsigned int a[MAX]={0},b[MAX]={0}; int sign=0; transition(a,str1); transition(b,str2); multiply_int(a,b,c);}int sign_comp(char str1[],char str2[]) //符号判断,如果为负数将作相应处理。{ int i=0,sign_num=0; if(str1[0]==45) {sign_num=!sign_num; for(i=0;i<MAX-1;i++) str1[i]=str1[i+1]; } if(str2[0]==45) {sign_num=!sign_num; for(i=0;i<MAX-1;i++) str2[i]=str2[i+1]; } return (sign_num);}int format(char str[]) //将输入的字符串进行格式化。以得到字符的一些标志信息和相应格式的新数据串。{ int point=0,quan=0,i=0,j,k=0,sign_point=0,num_str=0; num_str=str_num(str); while(str[i]!=0) { if(str[i]<'0'||str[i]>'9') if(str[i]!='.') {cout<<"data error"<<endl; return(-1); } else {point++; sign_point=i; } if(point>1) {cout<<"data error"<<endl; return(-1); } i++; } if(point==1) { for(j=sign_point;j<num_str;j++) str[j]=str[j+1]; num_str--; quan=num_str-sign_point; } place(num_str,str); return(quan);}void clear(char str[]) //清空函数。{ int i; for(i=0;i<MAX;i++) { str[i]=0; }}void main(void) //主函数。{ char str1[MAX]={0},str2[MAX]={0}; int quan1=0,quan2=0,sign=0; unsigned int c[MAX*2+1]={0}; do { cout<<"Please input the first number:"; cin>>str1; cout<<"Please input the second number:"; cin>>str2; sign=sign_comp(str1,str2); quan1=format(str1); quan2=format(str2); if(quan1==-1||quan2==-1) { clear(str1); clear(str2); } }while(quan1==-1||quan2==-1||str1[0]==0||str2[0]==0); multiply_string(str1,str2,c); output(sign,c,quan1+quan2);}所有题目到此结束,说实话后面两题的算法我就是看别人的代码(呵呵,再次实话,后两题代码也不是我写的,只是对已有代码做了些修改,使结构更清晰,便于阅读)
intel的面试题:不用任何局部和全局变量实现int strlen(char *a)
int strlen(char *a)
if(0 == *a)
return 0; else
return 1 + strlen(a +1);
传说中的baidu笔试题(另一个版本)
一、选择题:15分 共10题 1. 已知一个线性表(38,25,74,63,52,48),采用的散列函数为Hash($Key)=$Key mod 7,将元素散列到表长为7的哈希表中存储。请选择后面两种冲突解决方法分别应用在该散列表上进行等概率成功查找的平均查找长度,拉链法 ,线性探测法 . A. 1.0 B. 1.5 C. 1.7 D. 2.0 E. 2.3 F. 7/6 G. 4/3 H. 3/2
3. 下面哪个shell语句不能打印出用户主目录的路径? A. echo “$HOME” B. echo ~ C. echo `$HOME` D. echo $HOME
4. 最坏情况下,合并两个大小为n的已排序数组所需要的比较次数 A.2n B.2n-1 C.2n+1 D.2n-2
5. 一个B类网的子网掩码是255.255.240.0,这个子网能拥有的最大主机数是: A. 240 B. 255 C.4094 D. 65534
6. 以下代码执行后,val的值是___: unsigned long val = 0; char a = 0x48; char b = 0x52; val = b << 8 | a; A 20992 B 21064 C 72 D 0
7. 内存的速度远远高于磁盘速度,所以为了解决这个矛盾,可以采用: A 并行技术 B 虚存技术 C 缓冲技术 D 通道技术
8. 以下代码打印的结果是(假设运行在i386系列计算机上): struct st_t { int status; short* pdata; char errstr[32]; };
st_t st[16]; char* p = (char*)(st[2].errstr + 32); printf("%d", (p - (char*)(st)));
A 32 B 114 C 120 D 1112
9. 同一进程下的线程可以共享以下 A. stack B. data section C. register set D. thread ID
10. 以下哪种操作最适合先进行排序处理? A 找最大、最小值 B 计算算术平均值 C 找中间值 D 找出现次数最多的值
二、简答题:20分,共2题
2. (14分)函数A将字符串str1转成小写,并打印出转化前后的字符串。另外,改错时不能改变函数的接口和主要思路。改错时,请指出行号。 1 #include <stdio.h> 2 #include <stdlib.h> 5 char* str1 = "ABDFLjlero我们都是saf"; 7 char* ToLower(char s[]) 8 { 9 static size_t i=sizeof(s); 10 11 for (i; i>=0; i--) { 12 if (s[i]>"A" && s[i]<"Z") { 13 s[i] += 26; 14 } 15 } 16 return s;
}19 int A() 20 { 21 printf("old str[%s] after lower[%s]n", str1, ToLower(str1)); 22 }
三、编程题:30分 共1题 注意:要求提供完整代码,如果可以编译运行酌情加分。
1. 两个已排序的整型数组,求交集,最快算法 输入:两个已排序的整型数组(int a[m], b[n]) 输出:两个数组的交集
四、设计题:35分 共1题 注意:请尽可能详细描述你的数据结构、系统架构、设计思路等。建议多写一些伪代码或者流程说明。 1. 考虑一个字符串替换的过程,在一个文本文件中含有一些文本内容和一些需要替换的变量,变量的格式为“$Var$”,原来的“$”使用“$$”进行转义,原来的“$$”表示为“$$$”。我们将含有变量的文件称为模板(文件名为t),文本文件的平均长度为100K。另外,还有一系列的变量文件,里面为变量名和变量值的对应关系(文件名为1.v , 2.v… n.v),每个变量文件包含的变量数在百万数量级,且变量排列次序不定。现要求将,模板里的变量分别用变量文件里的变量替换,并将生成的文件写成(1.r, 2.r… n.r)。 要求:从算法和实现上和实现技术上的细节对程序进行优化,尽量使程序高效。程序运行环境为2G内存,4CPU。阐明主要思路,给出伪码和说明,可以着重指出你使用的优化技术。 例子:模板文件为 This is an $FF$ $$. I like $FF$ and $FA$。 变量文件为 1.v FF : banana FA : apple 2.v FA: 苹果 FF : 香蕉 则生成文件为 1.r This is an banana $$. I like banana and apple。 2.r This is an香蕉 $$. I like 香蕉and苹果。
百度11月4日网上笔试题及答案(仅供参考)1用C语言实现一个revert函数,它的功能是将输入的字符串在原串上倒序后返回。2 编程:用C语言实现函数void * memmove(void *dest,const void *src,size_t n)。memmove函数的功能是拷贝src所指的内存内容前n个字节到dest所指的地址上。3 英文拼写纠错:在用户输入英文单词时,经常发生错误,我们需要对其进行纠错。假设已经有一个包含了正确英文单词的词典,请你设计一个拼写纠错的程序。(1)请描述你解决这个问题的思路;(2)请给出主要的处理流程,算法,以及算法的复杂度;(3)请描述可能的改进(改进的方向如效果,性能等等,这是一个开放问题)。4 寻找热门查询:搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。假设目前有一千万个记录,这些查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就是越热门。请你统计最热门的10个查询串,要求使用的内存不能超过1G。(1)请描述你解决这个问题的思路;(2)请给出主要的处理流程,算法,以及算法的复杂度。5 集合合并:给定一个字符串的集合,格式如:{aaa bbb ccc}, {bbb ddd},{eee fff},{ggg},{ddd hhh}要求将其中交集不为空的集合合并,要求合并完成后的集合之间无交集,例如上例应输出{aaa bbb ccc ddd hhh},{eee fff}, {ggg}(1)请描述你解决这个问题的思路;(2)请给出主要的处理流程,算法,以及算法的复杂度(3)请描述可能的改进(改进的方向如效果,性能等等,这是一个开放问题)。////////////////////////////////11 题char *revert(char * str){int n=strlen(str);int i=0;char c;for(i=0;i {c=str;str=str[n-i];str[n-i]=c;}return str;}///////////////////////////////////2 题void * memmove(void *dest,const void *src,size_t n){assert((dest!=0)&&(src!=0));char * temp=(char * )dest;char * ss=(char * )src;int i=0;for(;i<N;I++) {*temp++=*ss++;}return temp;}/////////////////////////////////////////////////3 题(1)思路 : 字典以字母键树组织,在用户输入同时匹配(2)流程:每输入一个字母: 沿字典树向下一层,a)若可以顺利下行,则继续至结束,给出结果;b)若该处不能匹配,纠错处理,给出拼写建议,继续至a);算法:1.在字典中查找单词字典采用27叉树组织,每个节点对应一个字母,查找就是一个字母一个字母匹配.算法时间就是单词的长度k.2.纠错算法情况:当输入的最后一个字母不能匹配时就提示出错,简化出错处理,动态提示可能 处理方法:(a)当前字母前缺少了一个字母:搜索树上两层到当前的匹配作为建议;(b)当前字母拼写错误:当前字母的键盘相邻作为提示;(只是简单的描述,可 以有更多的)根据分析字典特征和用户单词已输入部分选择(a),(b)处理复杂性分析:影响算法的效率主要是字典的实现与纠错处理(a)字典的实现已有成熟的算法,改进不大,也不会成为瓶颈;(b)纠错策略要简单有效 ,如前述情况,是线性复杂度;(3)改进策略选择最是重要,可以采用统计学习的方法改进。//////////////////////////////////////////////4 题(1)思路:用哈希做(2)首先逐次读入查询串,算哈希值,保存在内存数组中,同时统计频度(注意值与日志项对应关系)选出前十的频度,取出对应的日志串,简单不过了。哈希的设计是关键。 //////////////////////////////////////////////////5 题(1)思路:先将集合按照大小排列后,优先考虑小的集合是否与大的集合有交集。有就合并,如果小集合与所有其他集合都没有交集,则独立。独立的集合在下一轮的比较中不用考虑。这样就可以尽量减少字符串的比较次数。当所有集合都独立的时候,就终止。(2)处理流程:1.将集合按照大小排序,组成集合合并待处理列表2.选择最小的集合,找出与之有交集的集合,如果有,合并之;如果无,则与其它集合是独立集合,从待处理列表 中删除。3.重复直到待处理列表为空算法:1。将集合按照大小从小到大排序,组成待处理的集合列表。2。取出待处理集合列表中最小的集合,对于集合的每个元素,依次在其他集合中搜索是否有此元素存在:1>若存在,则将此小集合与大集合合并,并根据大小插入对应的位置 。转3。2>若不存在,则在该集合中取下一个元素。如果无下一个元素,即所有元素都不存在于其他集合。则表明此集合独立,从待处理集合列表中删除。并加入结果集合列表。转3。3。如果待处理集合列表不为空,转2。如果待处理集合列表为空,成功退出,则结果集合列表就是最终的输出。算法复杂度分析:假设集合的个数为n,最大的集合元素为m排序的时间复杂度可以达到n*log(n)然后对于元素在其他集合中查找,最坏情况下为(n-1)*m查找一个集合是否与其他集合有交集的最坏情况是m*m*(n-1)合并的时间复杂度不会超过查找集合有交集的最坏情况。所以最终最坏时间复杂度为O(m*m*n*n)需要说明的是:此算法的平均时间复杂度会很低,因为无论是查找还是合并,都是处于最坏情况的概率很小,而且排序后优先用最小集合作为判断是否独立的对象,优先与最大的集合进行比较,这些都最大的回避了最坏情况。(3)可能的改进:首先可以实现将每个集合里面的字符串按照字典序进行排列,这样就可以将查找以及合并的效率增高。另外,可能采取恰当的数据结构也可以将查找以及合并等操作的效率得到提高
百度笔试题:
写一段程序,找出数组中第k大小的数,输出数所在的位置。例如{2,4,3,4,7}中,第一大的数是7,位置在4。第二大、第三大的数都是4,位置在1、3随便输出哪一个均可。函数接口为:int find_orderk(const int * narry, const int n, const int k) 要求算法复杂度不能是O(n^2)
参考答案:
/*************************************** 输入: n:数组元素的个数 k:第几大的数 a:待查找的数组元素 ****************************************/ #include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 100
void Rand_select(int*, int, int );
int partition( int*, int, int );
int swap( int&, int& );
int k, ans;
int main()
int n, a[N], i;
while(scanf( "%d%d", &n, &k ) != EOF )
srand(time(NULL));
k--;
for( i = 0; i < n; i++ )
scanf("%d", a + i );
Rand_select( a, 0, n-1 );
printf( "%d/n", ans );
return 0;
void Rand_select(int a[], int p, int q)
int m;
if (p <= q)
m = partition( a, p, q );
if( k == m )
ans = a[m];
return;
else if( k > m )
Rand_select( a, m+1, q);
else
Rand_select( a, p, m-1 );
int partition(int a[], int p, int q)
int last, i;
if( q != p )
swap( a[rand()%(q-p)+p], a[p] );
for( i = p+1, last = p; i <= q; i++ )
if( a[i] >= a[p] )
swap( a[i], a[++last] );
swap( a[last], a[p] );
return last;
int swap(int &p, int &q)
int temp = p;
p = q;
q = temp;
return 0;
2可以先用快速排序进行排序,其中用另外一个进行地址查找 代码如下,在VC++6.0运行通过。 //快速排序
#include <iostream> using namespace std; int Partition (int *L,int low,int high) { int temp = L[low]; int pt = L[low]; while (low < high) { while (low < high && L[high] >= pt) --high; L[low] = L[high]; while (low < high && L[low] <= pt) ++low; L[low] = temp; } L[low] = temp; return low; } void QSort (int *L,int low, int high) { if (low < high) { int pl = Partition (L,low,high); QSort (L, low, pl - 1); QSort (L, pl + 1, high); } } int main () { int narry[100],addr[100]; int sum = 1, t; cout << "Input number:" << endl; cin >> t; while (t != -1) { narry[sum] = t; addr[sum - 1] = t; sum++; cin >> t; } sum -= 1; QSort (narry,1,sum); for (int i = 1; i <= sum; i++) cout << narry[i] << '/t'; cout << endl; int k; cout << "Please input place you want:" << endl; cin >> k; int aa = 1; int kk = 0; for (;;) { if (aa == k) break; if (narry[kk] != narry[kk + 1]) { aa += 1; kk++; }
cout << "The NO." << k << "number is:" << narry[sum - kk] << endl; cout << "And it's place is:" ; for (i = 0;i < sum;i++) { if (addr[i] == narry[sum - kk]) cout << i << '/t'; } return 0;
cout << endl << “第” << k << “大的数字为:” << maxNum[k-1] << endl; return 0; } 分析:算法对n个数字只访问一次,此部分的时间复杂度为O(n);但每访问一次,须与k个数字分别比较,所以总的时间渐复杂度为O(n*k)
=================================================================== 函数功能:返回一个第K小的元素(采用快排思想) 参数:(T a[]目标数组 || int l左边界 || int r右边界 || int k第几小元素) ================================================================= template <class T> T select(T a[], int l, int r, int k) { if(l >= r) return a[l]; //参数错误直接返回 int i = l; int j = r+1; T pivot = a[i]; while(true) { do { i = i + 1; }while(a[i] > pivot); do { j = j - 1; }while(a[j] < pivot); if(i >= j) { break; } Swap(a[i], a[j]); } if(j - l + 1 == k) //如果当前基准适合的位置刚好是K的话,则满足了条件 返回基准值 { return pivot; } a[l] = a[j]; a[j] = pivot; if(j - l + 1 < k) { return select(a, j+1, r, k-j+l-1); //如果基准当前位置在K的左边则对右进行快排 } else { return select(a, l, j-1, k); //如果基准当前位置在K的右边则对左进行快排 } }
分析程序:
#include<stdio.h>
class A
public:
static int numA;
A()
num++;
};
~A()
num--;
};
virtual void print(void)
printf("class A, mum:%d/n", num);
void test(void)
printf("class A test/n");
print();
};
class B:public A
public:
static int numB;
B()
num--;
};
~B()
};
virtual void print()
printf("class B, num:%d/n", num);
void test(void)
printf("class B test/n");
print();
};
class C
public:
virtual void print()
printf("class B/n");
};
int A::numA = 0;
int B::numB = 0;
void main()
B b;
printf("1/n");
A *a;
B *p= new(class B);
p->print();
p->test();
printf("1/n");
a = p;
a->print();
a->test();
delete(a);
printf("sizeof(C):%d/n", sizeof(C));
#include <stdio.h>
class A1
public:
A1(){ doSth(); }
virtual void doSth(){printf("I am A/n");}
void test() {doSth();}
};
class B1:public A1
public:
virtual void doSth(){ printf("I am B/n");}
};
void main()
B1 *b = new B1;
b->test();
1 用C++开发的时候,用来做基类的类的析构函数一般都是虚函数。
class ClxBase{public: ClxBase() {}; virtual ~ClxBase() {}; virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };};class ClxDerived : public ClxBase{public: ClxDerived() {}; ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; }; void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };};
void main()
ClxBase *pTest = new ClxDerived;pTest->DoSomething();delete pTest;
的输出结果是:
Do something in class ClxDerived!Output from the destructor of class ClxDerived!
这个很简单,非常好理解。 但是,如果把类ClxBase析构函数前的virtual去掉,那输出结果就是下面的样子了:
Do something in class ClxDerived!
也就是说,类ClxDerived的析构函数根本没有被调用!一般情况下类的析构函数里面都是释放内存资源,而析构函数不被调用的话就会造成内存泄漏。我想所有的C++程序员都知道这样的危险性。当然,如果在析构函数中做了其他工作的话,那你的所有努力也都是白费力气。 所以,文章开头的那个问题的答案就是--这样做是为了当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。