• 应聘笔试模拟试题 > C/C 笔试、面试题目大汇总
  • C/C 笔试、面试题目大汇总

    免费下载 下载该文档 文档格式:PDF   更新时间:2014-06-05   下载次数:0   点击次数:1
    C/C++笔试、面试题目大汇总 1.求下面函数的返回值(微软) int func(x) { int countx = 0; while(x) { countx ++; x = x&(x-1); } return countx; } 假定 x = 9999. 答案:8 思路:将x转化为 2 进制,看含有的 1 的个数. 2. 什么是"引用"?申明和使用"引用"要注意哪些问题? 答:引用就是某个目标变量的"别名"(alias),对应用的操作与对变量直接操作效果完全相同. 申明一个引用的时候,切记要对其进行初始化.引用声明完毕后,相当于目标变量名有两个 名称, 即该目标原名称和引用名, 不能再把该引用名作为其他变量名的别名. 声明一个引用, 不是新定义了一个变量, 它只表示该引用名是目标变量名的一个别名, 它本身不是一种数据 类型,因此引用本身不占存储单元,系统也不给引用分配存储单元.不能建立数组的引用. 3. 将"引用"作为函数参数有哪些特点? (1)传递引用给函数与传递指针的效果是一样的.这时,被调函数的形参就成为原来主调 函数中的实参变量或对象的一个别名来使用, 所以在被调函数中对形参变量的操作就是对其 相应的目标对象(在主调函数中)的操作. (2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作; 而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量 是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数.因此,当参数传递的数据 较大时,用引用比用一般变量传递参数的效率和所占空间都好. (3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样 要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错 误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参. 而引用更容易使用,更清晰. 4. 在什么时候需要使用"常引用"? 如果既要利用引用提高程序的效率, 又要保护传递给函数的数据不在函数中被改变, 就应使 用常引用.常引用声明方式:const 类型标识符 &引用名=目标变量名; 例1int a ; const int &ra=a; ra=1; //错误 a=1; //正确 例2string foo( ); void bar(string & s); 那么下面的表达式将是非法的: bar(foo( )); bar("hello world"); 原因在于 foo( )和"hello world"串都会产生一个临时对象,而在 C++中,这些临时对象都是 const 类型的.因 此上面的表达式就是试图将一个 const 类型的对象转换为非 const 类型,这是非法的. 引用型参数应该在能被定义为 const 的情况下,尽量定义为 const . 5. 将"引用"作为函数返回值类型的格式、好处和需要遵守的规则? 格式:类型标识符 &函数名(形参列表及类型说明){ //函数体 } 好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局部变 1 量的引用是不可取的.因为随着该局部变量生存期的结束,相应的引用也会失效,产生 runtime error! 注意事项: (1)不能返回局部变量的引用.这条可以参照 Effective C++[1]的Item 31.主要原因是局部 变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知 状态. (2)不能返回函数内部 new 分配的内存的引用.这条可以参照 Effective C++[1]的Item 31. 虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部 new 分配内存的引 用),又面临其它尴尬局面.例如,被函数返回的引用只是作为一个临时变量出现,而没有 被赋予一个实际的变量, 那么这个引用所指向的空间 (由new 分配) 就无法释放, 造成 memory leak. (3) 可以返回类成员的引用, 但最好是 const. 这条原则可以参照 Effective C++[1]的Item 30. 主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与 某些其它属性或者对象的状态有关, 因此有必要将赋值操作封装在一个业务规则当中. 如果 其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务 规则的完整性. (4)流操作符重载返回值申明为"引用"的作用: 流操作符<<和>>,这两个操作符常常希望被连续使用,例如:cout << "hello" << endl; 因 此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用.可选的其它方案包 括:返回一个流对象和返回一个流对象指针.但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这 无法让人接受.对于返回一个流指针则不能连续使用<<操作符.因此,返回一个流对象引 用是惟一选择.这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是 C++语言中引入引用这个概念的原因吧.赋值操作符=.这个操作符象流操作符一样,是可 以连续使用的,例如:x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以 便可以被继续赋值.因此引用成了这个操作符的惟一返回值选择. 例3#include int &put(int n); int vals[10]; int error=-1; void main() { put(0)=10; //以put(0)函数值作为左值,等价于 vals[0]=10; put(9)=20; //以put(9)函数值作为左值,等价于 vals[9]=20; cout<=0 && n<=9 ) return vals[n]; else { cout<<"subscript error"; return error; } } (5)在另外的一些操作符中,却千万不能返回引用:+-*/ 四则运算符.它们不能返回引用, Effective C++[1]的Item23 详细的讨论了这个问题. 主要原因是这四个操作符没有 side effect, 因此,它们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部 变量的引用,返回一个 new 分配的对象的引用、返回一个静态对象引用.根据前面提到的 引用作为返回值的三个规则, 第2、 3 两个方案都被否决了. 静态对象的引用又因为((a+b) == (c+d))会永远为 true 而导致错误.所以可选的只剩下返回一个对象了. 6. "引用"与多态的关系? 引用是除指针外另一个可以产生多态效果的手段. 这意味着, 一个基类的引用可以指向它的 派生类实例. 例4Class A; Class B : Class A{...}; B b; A& ref = b; 2 7. "引用"与指针的区别是什么? 指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作.程序中使用指针,程 序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作.此外,就是上面提到的对函数传 ref 和pointer 的区别. 8. 什么时候需要"引用"? 流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其 它情况都推荐使用引用. 9. 结构与联合有和区别? 1. 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了 一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的 存放地址不同). 2. 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构 的不同成员赋值是互不影响的. 10. 下面关于"联合"的题目的输出? a) #include union { int i; char x[2]; }a; void main() { a.x[0] = 10; a.x[1] = 1; printf("%d",a.i); } 答案:266 (低位低地址,高位高地址,内存占用情况是 Ox010A) b) main() { union{ /*定义一个联合*/ int i; struct{ /*在联合中定义一个结构*/ char first; char second; }half; }number; number.i=0x4241; /*联合成员赋值*/ printf("%c%c\n", number.half.first, mumber.half.second); number.half.first='a'; /*联合中结构成员赋值*/ number.half.second='b'; printf("%x\n", number.i); getch(); } 答案: AB (0x41 对应'A',是低位;Ox42 对应'B',是高位) 6261 (number.i 和number.half 共用一块地址空间) 11. 已知 strcpy 的函数原型:char *strcpy(char *strDest, const char *strSrc)其中 strDest 是 目的字符串,strSrc 是源字符串.不调用 C++/C 的字符串库函数,请编写函数 strcpy. 答案: char *strcpy(char *strDest, const char *strSrc) { if ( strDest == NULL || strSrc == NULL) 3 return NULL ; if ( strDest == strSrc) return strDest ; char *tempptr = strDest ; while( (*strDest++ = *strSrc+0'); return tempptr ; } 12. 已知 String 类定义如下: class String { public: String(const char *str = NULL); // 通用构造函数 String(const String &another); // 拷贝构造函数 ~ String(); // 析构函数 String & operater =(const String &rhs); // 赋值函数 private: char *m_data; // 用于保存字符串 }; 尝试写出类的成员函数实现. 答案: String::String(const char *str) { if ( str == NULL ) //strlen 在参数为 NULL 时会抛异常才会有这步判断 { m_data = new char[1] ; m_data[0] = '\0' ; } else { m_data = new char[strlen(str) + 1]; strcpy(m_data,str); } } String::String(const String &another) { m_data = new char[strlen(another.m_data) + 1]; strcpy(m_data,other.m_data); } String& String::operator =(const String &rhs) { if ( this == &rhs) return *this ; delete []m_data; //删除原来的数据,新开一块内存 m_data = new char[strlen(rhs.m_data) + 1]; strcpy(m_data,rhs.m_data); return this ; } String::~String() { delete []m_data ; } 13. .h 头文件中的 ifndef/define/endif 的作用? 答:防止该头文件被重复引用. 14. #i nclude 与#i nclude "file.h"的区别? 答:前者是从 Standard Library 的路径寻找和引用 file.h,而后者是从当前工作路径搜寻并引 用file.h. 4 15.在C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern "C"? 首先,作为 extern 是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字, 该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用. 通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字 extern 声明. 例如, 如果模块 B 欲引用该模块 A 中定义的全局变量和函数时只需包含模块 A 的头文件即可.这样,模块 B 中调用模块 A 中的函数时,在编译阶段,模块 B 虽然找不到 该函数,但是并不会报错;它会在连接阶段中从模块 A 编译生成的目标代码中找到此函数 extern "C"是连接申明(linkage declaration),被extern "C"修饰的变量和函数是按照 C 语言 方式编译和连接的,来看看 C++中对类似 C 的函数是怎样编译的: 作为一种面向对象的语言, C++支持函数重载,而过程式语言 C 则不支持.函数被 C++编译后在符号库中的名字与 C 语言的不同.例如,假设某个函数的原型为:void foo( int x, int y ); 该函数被 C 编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像 _foo_int_int 之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制, 生成的新名字称为"mangled name"). _foo_int_int 这样的名字包含了函数名、 函数参数数量及类型信息, C++就是靠这种机制 来实现函数重载的.例如,在C++中,函数 void foo( int x, int y )与void foo( int x, float y )编 译生成的符号是不相同的,后者为_foo_int_float. 同样地,C++中的变量除支持局部变量外,还支持类成员变量和全局变量.用户所编写 程序的类成员变量可能与全局变量同名,我们以"."来区分.而本质上,编译器在进行编译 时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,这个名字与用户程序中 同名的全局变量名字不同. 未加 extern "C"声明时的连接方式 假设在 C++中,模块 A 的头文件如下: // 模块 A 头文件 moduleA.h #ifndef MODULE_A_H #define MODULE_A_H int foo( int x, int y ); #endif 在模块 B 中引用该函数: // 模块 B 实现文件 moduleB.cpp #i nclude "moduleA.h" foo(2,3); 实际上, 在连接阶段, 连接器会从模块 A 生成的目标文件 moduleA.obj 中寻找_foo_int_int 这样的符号! 加extern "C"声明后的编译和连接方式 加extern "C"声明后,模块 A 的头文件变为: // 模块 A 头文件 moduleA.h #ifndef MODULE_A_H #define MODULE_A_H extern "C" int foo( int x, int y ); #endif 在模块 B 的实现文件中仍然调用 foo( 2,3 ),其结果是: (1)模块 A 编译生成 foo 的目标代码时,没有对其名字进行特殊处理,采用了 C 语言的方式; (2)连接器在为模块 B 的目标代码寻找 foo(2,3)调用时,寻找的是未经修改的符号名_foo. 如果在模块 A 中函数声明了 foo 为extern "C"类型,而模块 B 中包含的是 extern int foo( int x, int y ) ,则模 块B找不到模块 A 中的函数;反之亦然. 所以,可以用一句话概括 extern "C"这个声明的真实目的(任何语言中的任何语法特性 的诞生都不是随意而为的,来源于真实世界的需求驱动.我们在思考问题时,不能只停留在 这个语言是怎么做的,还要问一问它为什么要这么做,动机是什么,这样我们可以更深入地 理解许多问题):实现 C++与C及其它语言的混合编程. 明白了 C++中extern "C"的设立动机,我们下面来具体分析 extern "C"通常的使用技巧: extern "C"的惯用法 5 (1) 在C++中引用 C 语言中的函数和变量, 在包含 C 语言头文件 (假设为 cExample.h) 时, 需进行下列处理: extern "C" { #i nclude "cExample.h" } 而在 C 语言的头文件中,对其外部函数只能指定为 extern 类型,C 语言中不支持 extern "C" 声明,在.c 文件中包含了 extern "C"时会出现编译语法错误. C++引用 C 函数例子工程中包含的三个文件的源代码如下: / c 语言头文件:cExample.h / #ifndef C_EXAMPLE_H #define C_EXAMPLE_H extern int add(int x,int y); #endif / c 语言实现文件:cExample.c / #include "cExample.h" int add( int x, int y ) { return x + y; } // c++实现文件,调用 add:cppFile.cpp extern "C" { #i nclude "cExample.h" } int main(int argc, char argv[]) { add(2,3); return 0; } 如果 C++调用一个 C 语言编写的.DLL 时,当包括.DLL 的头文件或声明接口函数时,应加 extern "C" { } (2)在C中引用 C++语言中的函数和变量时,C++的头文件需添加 extern "C",但是在 C 语言中不能直接引用声明了 extern "C"的该头文件, 应该仅将 C 文件中将 C++中定义的 extern "C"函数声明为 extern 类型. C 引用 C++函数例子工程中包含的三个文件的源代码如下: //C++头文件 cppExample.h #ifndef CPP_EXAMPLE_H #define CPP_EXAMPLE_H extern "C" int add( int x, int y ); #endif //C++实现文件 cppExample.cpp #i nclude "cppExample.h" int add( int x, int y ) { return x + y; } /* C 实现文件 cFile.c /* 这样会编译出错:#i nclude "cExample.h" / extern int add( int x, int y ); int main( int argc, char argv[] ) { add( 2, 3 ); return 0; } 16. 关联、聚合(Aggregation)以及组合(Composition)的区别? 涉及 UML 中的一些概念:关联是表示两个类的一般性联系,比如"学生"和"老师"就是一种 6 关联关系;聚合表示 has-a 的关系,是一种相对松散的关系,聚合类不需要对被聚合类负责. 从实现的角度讲,聚合可以表示为:class A {...} class B { A* a; .....} 而组合表示 contains-a 的关系,关联性强于聚合:组合类与被组合类有相同的生命周期,组 合类要对被组合类负责. 17.面向对象的三个基本特征,并简单叙述之? 1. 封装:将客观事物抽象成类,每个类对自身的数据和方法实行 protection(private, protected,public) 2. 继承:广义的继承有三种实现形式:实现继承(指使用基类的属性和方法而无需额外编 码的能力)、可视继承(子窗体使用父窗体的外观和实现代码)、接口继承(仅使用属性和 方法,实现滞后到子类实现).前两种(类继承)和后一种(对象组合=>接口继承以及纯 虚函数)构成了功能复用的两种方式. 3. 多态:是将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象 就可以根据当前赋值给它的子对象的特性以不同的方式运作.简单的说,就是一句话:允许 将子类类型的指针赋值给父类类型的指针. 18. 重载(overload)和重写(overried,有的书也叫做"覆盖")的区别? 常考的题目. 重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参 数类型不同,或许两者都不同). 重写:是指子类重新定义复类虚函数的方法. 从实现原理上来说: 重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了 不同的函数(至少对于编译器来说是这样的).如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;. 那么编译器做过修饰后的函数名称可 能是这样的:int_func、str_func.对于这两个函数的调用,在编译器间就已经确定了,是静 态的.也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关! 重写:和多态真正相关.当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的 子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用 的子类的虚函数的地址无法给出).因此,这样的函数地址是在运行期绑定的(晚绑定). 19. 多态的作用? 主要是两个:1. 隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;2. 接 口重用: 为了类在继承和派生的时候, 保证使用家族中任一类的实例的某一属性时的正确调 用. 20. Ado 与Ado.net 的相同与不同? 除了"能够让应用程序处理存储于 DBMS 中的数据"这一基本相似点外,两者没有太多共同 之处.但是 Ado 使用 OLE DB 接口并基于微软的 COM 技术,而ADO.NET 拥有自己的 ADO.NET 接口并且基于微软的.NET 体系架构.众所周知.NET 体系不同于 COM 体系, ADO.NET 接口也就完全不同于 ADO 和OLE DB 接口, 这也就是说 ADO.NET 和ADO 是两 种数据访问方式.ADO.net 提供对 XML 的支持. 21. New delete 与malloc free 的联系与区别? 答案:都是在堆(heap)上进行动态的内存操作.用malloc 函数需要指定内存分配的字节数并 且不能初始化对象, new 会自动调用对象的构造函数. delete 会调用对象的 destructor, 而free 不会调用对象的 destructor. 22. #define DOUBLE(x) x+x ,i = 5*DOUBLE(5); i 是多少? 答案:i 为30. 23. 有哪几种情况只能用 intialization list 而不能用 assignment? 7 答案:当类中含有 const、reference 成员变量;基类的构造函数都需要初始化表. 24. C++是不是类型安全的? 答案:不是.两个不同类型的指针之间可以强制转换(用reinterpret cast).C#是类型安全 的. 25. main 函数执行以前,还会执行什么代码? 答案:全局对象的构造函数会在 main 函数之前执行. 26. 描述内存分配方式以及它们的区别? 1) 从静态存储区域分配.内存在程序编译的时候就已经分配好,这块内存在程序的整个运 行期间都存在.例如全局变量,static 变量. 2) 在栈上创建.在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行 结束时这些存储单元自动被释放.栈内存分配运算内置于处理器的指令集. 3) 从堆上分配,亦称动态内存分配.程序在运行的时候用 malloc 或new 申请任意多少的 内存,程序员自己负责在何时用 free 或delete 释放内存.动态内存的生存期由程序员决定, 使用非常灵活,但问题也最多. 27.struct 和class 的区别 答案:struct 的成员默认是公有的,而类的成员默认是私有的.struct 和class 在其他方面是 功能相当的. 从感情上讲, 大多数的开发者感到类和结构有很大的差别. 感觉上结构仅仅象一堆缺乏封装 和功能的开放的内存位,而类就象活的并且可靠的社会成员,它有智能服务,有牢固的封装 屏障和一个良好定义的接口. 既然大多数人都这么认为, 那么只有在你的类有很少的方法并 且有公有数据(这种事情在良好设计的系统中是存在的!)时,你也许应该使用 struct 关键 字,否则,你应该使用 class 关键字. 28.当一个类A中没有生命任何成员变量与成员函数,这时sizeof(A)的值是多少, 如果不是零, 请解释一下编译器为什么没有让它为零.(Autodesk) 答案:肯定不是零.举个反例,如果是零的话,声明一个 class A[10]对象数组,而每一个对 象占用的空间是零,这时就没办法区分 A[0],A[1]…了. 29. 在8086 汇编下,逻辑地址和物理地址是怎样转换的?(Intel) 答案:通用寄存器给出的地址,是段内偏移地址,相应段寄存器地址*10H+通用寄存器内地 址,就得到了真正要访问的地址. 30. 比较 C++中的 4 种类型转换方式? 请参考:http://blog.csdn.net/wfwd/archive/2006/05/30/763785.aspx , 重点是static_cast, dynamic_cast 和reinterpret_cast 的区别和应用. 31.分别写出 BOOL,int,float,指针类型的变量 a 与"零"的比较语句. 答案: BOOL : if ( !a ) or if(a) int : if ( a == 0) float : const EXPRESSION EXP = 0.000001 if ( a < EXP && a >-EXP) pointer : if ( a != NULL) or if(a == NULL) 32.请说出 const 与#define 相比,有何优点? 答案:1) const 常量有数据类型,而宏常量没有数据类型.编译器可以对前者进行类型安 全检查.而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不 到的错误. 2) 有些集成化的调试工具可以对 const 常量进行调试,但是不能对宏常量进行调试. 8 33.简述数组与指针的区别? 数组要么在静态存储区被创建(如全局数组),要么在栈上被创建.指针可以随时指向任意 类型的内存块. (1)修改内容上的差别 char a[] = "hello"; a[0] = 'X'; char *p = "world"; // 注意 p 指向常量字符串 p[0] = 'X'; // 编译器不能发现该错误,运行时错误 (2) 用运算符 sizeof 可以计算出数组的容量(字节数).sizeof(p),p 为指针得到的是一个指 针变量的字节数, 而不是 p 所指的内存容量. C++/C 语言没有办法知道指针所指的内存容量, 除非在申请内存时记住它. 注意当数组作为函数的参数进行传递时, 该数组自动退化为同类 型的指针. char a[] = "hello world"; char *p = a; cout<< sizeof(a) << endl; // 12 字节 cout<< sizeof(p) << endl; // 4 字节 计算数组和指针的内存容量 void Func(char a[100]) { cout<< sizeof(a) << endl; // 4 字节而不是 100 字节 } 34.类成员函数的重载、覆盖和隐藏区别? 答案: a.成员函数被重载的特征: (1)相同的范围(在同一个类中); (2)函数名字相同; (3)参数不同; (4)virtual 关键字可有可无. b.覆盖是指派生类函数覆盖基类函数,特征是: (1)不同的范围(分别位于派生类与基类); (2)函数名字相同; (3)参数相同; (4)基类函数必须有 virtual 关键字. c."隐藏"是指派生类的函数屏蔽了与其同名的基类函数,规则如下: (1)如果派生类的函数与基类的函数同名,但是参数不同.此时,不论有无 virtual 关键字, 基类的函数将被隐藏(注意别与重载混淆). (2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有 virtual 关 键字.此时,基类的函数被隐藏(注意别与覆盖混淆) 35. There are two int variables: a and b, don't use "if"switch"or other judgement statements, find out the biggest one of the two numbers. 答案:( ( a + b ) + abs( a - b ) ) / 2 36. 如何打印出当前源文件的文件名以及源文件的当前行号? 答案: cout << __FILE__ ; cout<<__LINE__ ; __FILE__和__LINE__是系统预定义宏,这种宏并不是在某个文件中定义的,而是由编译器定义的. 37. main 主函数执行完毕后,是否可能会再执行一段代码,给出说明? 答案:可以,可以用_onexit 注册一个函数,它会在 main 之后执行 int fn1(void), fn2(void), fn3(void), fn4 (void); void main( void ) 9 { String str("zhanglin"); _onexit( fn1 ); _onexit( fn2 ); _onexit( fn3 ); _onexit( fn4 ); printf( "This is executed first.\n" ); } int fn1() { printf( "next.\n" ); return 0; } int fn2() { printf( "executed " ); return 0; } int fn3() { printf( "is " ); return 0; } int fn4() { printf( "This " ); return 0; } The _onexit function is passed the address of a function (func) to be called when the program terminates normally. Successive calls to _onexit create a register of functions that are executed in LIFO (last-in-first-out) order. The functions passed to _onexit cannot take parameters. 38. 如何判断一段程序是由 C 编译程序还是由 C++编译程序编译的? 答案: #ifdef __cplusplus cout<<"c++"; #else cout<<"c"; #endif 39.文件中有一组整数,要求排序后输出到另一个文件中 答案: #include #include using namespace std; void Order(vector& data) //bubble sort { int count = data.size() ; int tag = false ; // 设置是否需要继续冒泡的标志位 for ( int i = 0 ; i < count ; i++) { for ( int j = 0 ; j < count - i - 1 ; j++) { if ( data[j] > data[j+1]) { tag = true ; int temp = data[j] ; data[j] = data[j+1] ; data[j+1] = temp ; } } if ( !tag ) break ; } 10 } void main( void ) { vectordata; ifstream in("c:\\data.txt"); if ( !in) { cout<<"file error!"; exit(1); } int temp; while (!in.eof()) { in>>temp; data.push_back(temp); } in.close(); //关闭输入文件流 Order(data); ofstream out("c:\\result.txt"); if ( !out) { cout<<"file error!"; exit(1); } for ( i = 0 ; i < data.size() ; i++) out<next == NULL ) return head; Node *p1 = head ; Node *p2 = p1->next ; Node *p3 = p2->next ; p1->next = NULL ; while ( p3 != NULL ) { p2->next = p1 ; p1 = p2 ; p2 = p3 ; p3 = p3->next ; } p2->next = p1 ; head = p2 ; return head ; } (2)已知两个链表 head1 和head2 各自有序,请把它们合并成一个链表依然有序.(保留所有 结点,即便大小相同) Node * Merge(Node *head1 , Node *head2) { if ( head1 == NULL) return head2 ; if ( head2 == NULL) return head1 ; 11 Node *head = NULL ; Node *p1 = NULL; Node *p2 = NULL; if ( head1->data < head2->data ) { head = head1 ; p1 = head1->next; p2 = head2 ; } else { head = head2 ; p2 = head2->next ; p1 = head1 ; } Node *pcurrent = head ; while ( p1 != NULL && p2 != NULL) { if ( p1->data <= p2->data ) { pcurrent->next = p1 ; pcurrent = p1 ; p1 = p1->next ; } else { pcurrent->next = p2 ; pcurrent = p2 ; p2 = p2->next ; } } if ( p1 != NULL ) pcurrent->next = p1 ; if ( p2 != NULL ) pcurrent->next = p2 ; return head ; } (3)已知两个链表 head1 和head2 各自有序,请把它们合并成一个链表依然有序,这次要求 用递归方法进行. (Autodesk) Node * MergeRecursive(Node *head1 , Node *head2) { if ( head1 == NULL ) return head2 ; if ( head2 == NULL) return head1 ; Node head = NULL ; if ( head1->data < head2->data ) { head = head1 ; head->next = MergeRecursive(head1->next,head2); } else { head = head2 ; head->next = MergeRecursive(head1,head2->next); } return head ; } 41. 分析一下这段程序的输出 (Autodesk) class B { public: B(){ cout<<"default constructor"< instance of B){ cout<<"constructed by parameter " << data < maxnumber ) { sec_max = maxnumber ; maxnumber = data[i] ; } else { if ( data[i] > sec_max ) sec_max = data[i] ; } } return sec_max ; } 43. 写一个在一个字符串(n)中寻找一个子串(m)第一个位置的函数. KMP 算法效率最好,时间复杂度是O(n+m). 44. 多重继承的内存分配问题: 比如有 class A : public class B, public class C {} 那么 A 的内存结构大致是怎么样的? 这个是 compiler-dependent 的, 不同的实现其细节可能不同. 如果不考虑有虚函数、虚继承的话就相当简单;否则的话,相当复杂. 45. 如何判断一个单链表是有环的?(注意不能用标志位,最多只能用两个额外指针) 13 struct node { char val; node* next;} bool check(const node* head) {} //return false : 无环;true: 有环 一种 O(n)的办法就是(搞两个指针,一个每次递增一步,一个每次递增两步,如果有环的话两者必然重 合,反之亦然): bool check(const node* head) { if(head==NULL) return false; node *low=head, *fast=head->next; while(fast!=NULL && fast->next!=NULL) { low=low->next; fast=fast->next->next; if(low==fast) return true; } return false; } 16道C语言面试题 预处理器(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语言课本的附录去找出象这种 问题的答案.当然如果你不是在找一个书呆子,那么应试者最好希望自己不要知道答案. 死循环(Infinite loops) 4. 嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢? 这个问题用几个解决方案.我首选的方案是: while(1) { } 一些程序员更喜欢如下方案: for(;;) { 14 } 这个实现方式让我为难, 因为这个语法没有确切表达到底怎么回事. 如果一个应试者给出这个作 为方案,我将用这个作为一个机会去探究他们这样做的 基本原理.如果他们的基本答案是:"我 被教着这样做,但从没有想到过为什么."这会给我留下一个坏印象. 第三个方案是用 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 Static 6. 关键字static的作用是什么? 这个简单的问题很少有人能回答完全.在C语言中,关键字static有三个明显的作用: 1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变. 2). 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能 被模块外其它函数访问.它是一个本地的全局变量. 3). 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用.那就是,这个函数 被限制在声明它的模块的本地范围内使用. 大多数应试者能正确回答第一部分, 一部分能正确回答第二部分, 同是很少的人能懂得第三部分. 这是一个应试者的严重的缺点,因为他显然不懂得本地化数据和代码范围的好处和重要性. Const 7.关键字const是什么含意? 我只要一听到被面试者说:"const意味着常数",我就知道我正在和一个业余者打交道.去年Dan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文 章,只要能说出const意味着"只读"就可以了.尽管这个答案不是完全的答案,但我接受它作为一 个正确的答案.(如果你想知道更详细的答案,仔细读一下Saks的文章吧.)如果应试者能正确 回答这个问题,我将问他一个附加的问题:下面的声明都是什么意思? const int a; int const a; const int *a; int * const a; int const * a const; 前两个的作用是一样,a是一个常整型数.第三个意味着a是一个指向常整型数的指针(也就是, 15 整型数是不可修改的,但指针可以).第四个意思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.在以上两个操作中,要保持其它位不变. 16 对这个问题有三种基本的反应 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; 17 (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; 18 这个问题将做为这个测验的一个愉快的结尾.不管你相不相信,上面的例子是完全合乎语法的. 问题是编译器如何处理它?水平不高的编译作者实际上会争论这个问题, 根据最处理原则, 编译 器应当能处理尽可能所有合法的用法.因此,上面的代码被处理成: c = a++ + b; 因此, 这段代码持行后a = 6, b = 7, c = 12. 如果你知道答案,或猜出正确答案,做得好.如果你不知道答案,我也不把这个当作问题.我发 现这个问题的最大好处是:这是一个关于代码编写风格,代码的可读性,代码的可修改性的好的 话题 1.用最简单的方法实现函数 int strcmp(char *p1,char *p2) 2.指针与数组 a.请定义一个指向此函数的指针 int test(char *p1,int nTestCount) b.声明此指针的数组,包含 10 个元素. c.给数组的第一个元素赋值,使其指向 test. d.写出调用数组的第一个元素,执行 test 操作实例的语句. 3.表述应在什么情况下使用 extern"C"声明. 4.对如下双链表 typedef struct _node { int iData; struct _node *pPrev; struct _node *pNext; }node; a.请写出代码,将node*n 插入到 node*p 后. b.如果多线程同时访问此链表,需要加锁,请说明以下步骤 (a)申请内存给 n. (b)N 数据初始化. (c)插入 注意加锁和解锁的时机. 5.简述 C 语言中可变长参数的实现原理,如int sprintf(char *buf,const char*format,..) 6.Big endian 和Little endian 的定义是什么?请用最简单方法(程序实现)判断某个 CPU 的类型. 7.子类 B 重写父类 A 的一个成员函数 f,创建一个子类对象 C,如果使用 C 调用父类 A 的成 员函数. 8.C++中public,protect,private 成员函数编译后的可执行代码有何区别? 9.对如下定义 typedef struct { int iData; int iAge; char*pName; }STUDENT; STUDENT arrStudent[10]; 请写出使用 qsort 对arrStrudent 数组按 iAge 由小到大的顺序排序.标准函数 qsort 声明如下: void qsort(void *base,size_t num,size_t width,int(_cdecl*compare)(const void *elem1,const void *elem2)); 关于 compare 函数,返回<0 表示 elem1 小于 elem2,表示相等,>0 表示 elem1>elem2. 10.对于如下函数 int test(char* p1,int nTestCount) { int i,j; i=nTestCount;} 画出执行完 i=nTestCount 后的栈分布状况(你熟悉的 CPU 和编译器) ,并说明返回值是如何传递回父函数 的. 某人答案: 1. int strcmp(char *p1,char *p2) { 19 return(*p1-*p2); } 2. a.b. void(*p[10])(void) 是不是声明为函数指针数组. c. p[0]=test d. 不会 3.只知道 extern 声明外部变量,extern"C"是什么不知道. 4.用C++写的代码. a. current=first->pNext; while(current!=first&¤t->data!=p->iData) current=current->iData; current->pNext=new _node(iData,current,current->pNext); current=current->pNext; current->pPrev=current->pNext; b. 多线程访问链表是什么情况都不知道,更别提处理了. 5.不明白这也有什么可解释的,直接用不就行了吗. 挑两个简单的做下,请帮忙指出错误,谢谢 1.用最简单的方法实现函数 int strcmp(char *p1,char *p2) while( *p1++ = *p2++); 2. 指针与数组 a.请定义一个指向此函数的指针 int test(char *p1,int nTestCount) b.声明此指针的数组,包含 10 个元素. c.给数组的第一个元素赋值,使其指向 test. d.写出调用数组的第一个元素,执行 test 操作实例的语句. a. int (*func) ( char*, int ); b. func myFunc[10]; c. myFunc[0] = test; d. char* p; int n; (*myFunc[0])( p, n ); 3.表述应在什么情况下使用 extern"C"声明. 如void foo( int x, int y ) 在C编译下会将函数编译为_foo,而C++下会编译为_foo_int_int(通常应该是这样), 在调用约 定上要特别注意,你的函数名字是否被改编过了,在调用时应保持与你的声明函数的方式一样 如: test.h extern "C" int add( int x, int y); test.cpp int add( int x, int y) { return x+y; } 调用时: #include"test.h" extern int add( int x, int y ); void main() { int sum = add(2,3); //出错,找不到_add_int_int 的函数,加载的 test.obj 里的函数名为_add printf( "%d\n", sum ); } 改成这样 extern "C" { include"tes.h" } extern int add( int x, int y ); void main() 20 { int sum = add(2,3); //OK printf( "%d\n", sum ); } 7.子类 B 重写父类 A 的一个成员函数 f,创建一个子类对象 C,如果使用 C 调用父类 A 的成 员函数. 直接显示调用就可以了 C::A::f(); C++复习面试题 1.下面的程序是否有错误,如果有错,请说明原因. char* const pszHelp = "hello"; pszHelp[0] = 'a'; 因为 pszHelp 指向一个常量字符串,根本不允许修改字符串内容.除非使用一个字符数组. const 用法: const char* p p 是一字符串指针,该指针指向的内容是常量,指针指向的内容不能被修改 char const* p 同上 char* const p p 是一字符串指针,该指针是常量指针,指针不能被修改 2.什么是抽象类 包含抽象函数的类是抽象类,满足 virtual fun() = 0; 的语法的函数是抽象函数.主要用于提 供接口,如同 Java 中的 Interface. 3.什么时候需要使用虚拟析构函数 一般情况下类的析构函数都定义成虚函数,主要是考虑在使用基类指针操作派生类对象时 保证类的析构顺序. 4.请指出下面代码存在的潜在问题 class CC { int* m_pCount; public: void clear() { if ( m_pCount ) delete m_pCount; } CC() { m_pCount = new int; } ~CC() { clear(); } } 主要存在的问题是 clear 函数在 delete m_pCount;后并没有置指针为空(m_pCount = NULL), 这样当第二次调用 clear 时,会出现问题. 5.如何定义禁止实例化的纯抽象基类 这个问题很奇怪,抽象类本身就不能被实例化,在程序编译时就会提示出错. 6.在进行私有继承时,如何修改基类成员的存取权限 定义一个指向该基类的友元函数,就可以绕过私有继承操作基类成员. 7.如何定义一个指向类的成员函数的指针 void (CC::*bfpClear)() = &CC::clear; 8.类A派生类 B 和C,类D从类 B、C 派生,如何将一个类 A 的指针指向一个类 D 的指针 直接使用即可,主要是考察运行时多态的概念. 9.请说出程序运行的结果 class A { public: virtual void func() { cout << "I am in base" << endl; }; } class B : public A { public: virtual void func() { cout << "I am in derived" << endl; } } void main() { 21 B* bb = new B; bb->func(); A* aa = (A*)bb; aa->func(); …. } I am in derived I am in derived 主要考察虚函数的使用 10.分析下面代码,指出问题 int* func() { int i =0 ; return &i; } 返回的是一个局部简单类型变量地址,程序可能会出错,而且返回的指针不能采用 delete 进行删除. class CObject; CObject* funobj() { CObject obj; …. return &obj; } 返回的是一个局部 CObject 类型变量地址,程序会出错,而且返回的指针不能采用 delete 进行删除. CString funobj() { CString strTemp = _T("temp"); return strTemp; } 不会出错,返回的 CString 会调用 CString 的拷贝构造函数返回. 11.Debug 版本中经常使用 ASSERT 进行断言, 在Release 版本中有一个起同样作用的函数, 请说明. VERIFY,而且要注意 ASSERT 中的语句在 Release 版本中会忽略. 12.STL 类模板 标准库中容器和算法这部分一般称为标准模板库 13.为什么定义虚的析构函数? 避免内存问题, 当你可能通过基类指针删除派生类对象时. 必须保证基类析构函数为虚函数. 14.项目管理怎么进行? 9 制定软件项目计划; 9 软件项目跟踪及监控. 9 软件项目计划(Software Project Planning)的目的是为了完成软件工程和管理软件项目 所制定的计划,其内容包括估计待完成的工作,建立必要的约定和完成工作的计划. 9 软件项目跟踪和控制(Software Project Tracking and Oversighting)的目的是对实际进展 建立足够的可视性,使管理着能够在软件项目的执行明显偏离软件计划时采取有效措 施.其内容包括将实际的完成情况及结果和已文档化的估计、约定和计划进行对照,并 且基于实际的完成情况和结果调整这些计划 15.单元测试如何进行? 单元测试主要编写驱动函数和桩函数,测试的目标是完成语句覆盖和路径覆盖.驱动函数 用于调用被测函数,传递测试参数给被测函数.桩函数用于模拟被测函数调用的底层函数, 需要对被测函数传来的各种可能参数进行正确的响应. 16.VSS 的三个核心操作是什么? Check in、check out、get latest version. 17.USECASE 图画到哪一级? 一般要提供用例图、行为图和交互图及各角色定义. 18.在评审过程中应该做哪些工作? 以文档评审为例 文档评审电子流基本流程: 文档负责人创建文档评审电子流,指定评审专家和评审负责人 ->QA 经理确认通过,通知文档作者->文档作者提交文档->评审专家提出评审意见-> 文档作者修改文档再次提交评审->评审专家会签通过(可能需要召开评审会议)->秘书 进行文档归档 19.提交配置管理前做哪些工作? 已经得到 QA 及配置管理委员会的确认.如果提交的是源代码,确认包括需求、详细设计 等一系列文档都已做了相应的修改以及确认. 20.概要设计文档如果到编码时发现问题怎么处理? 先提交问题报告单,经过软件经理审核,然后确认修改设计方案,修改代码,测试通过后, 提交到配置库,同时更新相关的概要设计文档和详细设计文档. 22.工作计划怎么制定? 22 根据项目总体计划落实到每个具体人员身上,按照月度和周来制度工作计划. 23.Rose 的应用情况 Rose 作为一种建模语言工具可以应用于软件的需求分析、 概要设计、 详细设计和测试阶段. 24.软件开发模式包括哪些? 公司的主要开发模式包括面向对象、 面向过程的开发模式, 无论哪种方式都体现模块组装的 思想. 25.设计的主要目标是什么? 逐步消化、细化需求,用程序的逻辑来表达文字性的描述. 26.设计包含哪些方面? 概要设计、详细设计、接口设计. 27.测试在设计阶段有没有活动要做? 在设计阶段要确定集成测试计划和单元测试计划. 28.合作性质项目和传统项目方式有什么区别? 传统项目主要根据公司自己的开发流程进行项目管理,合作性项目则需要接纳,有时甚至 是按照合作方的开发模式进行项目管理,这主要由项目性质和项目需求决定的. 29.在项目启动时,如何制定项目计划? 如果是有规定交付日期的项目,就必须根据交付日期限定、项目需求、项目组成员的素质 (体现在代码生产率)来确定项目计划.在项目计划的基础上可以作出月、周计划,计划执 行时根据实际情况在不影响开发进度的前提下调整计划. 30.项目管理计划分为哪些类别? 粗的方面包括项目开发计划、质量计划、配置计划、测试计划. 31.计划的制定过程中,需要由哪些人参与? 项目经理、开发经理、质量经理和配置经理均要参与项目计划的制定. 32.月度计划由哪些人参与?主要过程是什么?什么时候启动月度计划,如何确定? 月度计划由项目经理、各小组负责人、质量经理、配置经理参与,主要是根据上月工作完 成情况和项目总体计划来确定本月度计划. 一般是在工作月的最后 3 天启动, 目的是给大家 一个熟悉和工作安排的考虑. 33.怎样对计划进行跟踪? 每周对计划完成情况进行审核和评估,及时进行反馈和调整. CASE 管理在开发过程中的应用情况 34.JAVA 的缺陷有哪些? 代码运行效率低. 35.CMMⅡ的主要 KPA 包括哪些? 包括 6 个关键过程域:软件配置管理、软件质量保证、软件子合同管理、软件项目跟踪和 监督、软件项目策划、需求管理. 36.需求管理方面需要 QA 做哪些工作? 需求管理(Requirement Management)的目的是在客户和将处理客户需求的软件项目之间建 立对客户需求的共同理解.需求管理包括和客户一起建立和维护有关软件项目需求的协议, 该协议称作"分配给软件的系统需求","客户"可解释为系统工程组、销售组、另一个内部组 织,或者一个外部客户,"协议"既包括技术需求,又包括非技术需求(如交付日期),该协 议形成估计,策划和跟踪整个软件生命周期内项目活动的基础. 将系统需求分配给软件、 硬件和其它系统组成部分(如人力等)的工作可能由软件工程组以外的组(如系统工程组) 完成,软件工程组可能对此分配无法直接控制,在项目约束范围内,软件工程可采取适当的 步骤以确保软件的需求已有相对应的定位,并且实现文档化并处于受控状态 37.需求跟踪方面做哪些工作?可以跟踪到哪一步?谁来负责? (1)定义对其他需求的连接链 (2)定义对其他系统元素的连接链,这些元素可能包括软件计划、产品和活动、需求和设计 文档、代码. 目的是它们与软件需求保持一致. 38.SEPG 有多少人员?主要负责哪些方面的工作? 23 软件工程过程小组包括项目经理和技术人员, 主要负责推进组织所采用的软件过程的定义、 组织和改进工作. 39.如何理解度量机制? 软件度量(Software Measurement)通过各种不同的量度(metric)对软件生命周期中的各个元 素进行度量(Measure),它能够为项目管理者提供有关项目的各种重要信息,同时也是进行 大多评估活动的基础 40.如何保证专家已经很好地阅读了文档? 评审作者要负责准备充分的评审材料,对文档进行详细的说明,确保专家能够理解文档; 同时专家要拿出专门的时间来阅读文档. 41.开发工具用过哪些? VC: MSDev Case: Rational Rose Java: Jbuilder Database: PowerDesigner 42.描述一下 Windows 的线程同步对象. 临界区、互斥、信号、事件 43.类静态成员变量在什么范围内有效? 等同于全局变量 44.面向对象的好处 继承性、多态性、封装性. 45.在C++中声明类,如何做到多个实例共享一个全局变量? 声明一个类静态成员变量. 46.在编码过程中文档的书写情况 简单的是对函数的返回值、参数、用途以及关键技术做文档说明.基本上向 MSDN 靠拢. 47.规范在自己日常工作中如何体现? 规范体现在:编程规范、文档模板、电子流程. 每天填写日志、每周作计划和总结,使用 PSP 工具 48.控件编写过没有 主要编写访问数据库的 COM 组件给前台程序调用.一般的 UI 控件是在 CWnd 的基础上编 写的. 49.调试代码过程中遇到困难如何解决? 多使用 Watch, Variable, Breakpoint, Stack 等观察窗口发现问题; 使用联机帮助系统; 想其它开发小组成员请教. 50.C++构造函数,抽象类 构造函数是类初始化第一个调用的函数,如果一个类中包括抽象函数,则该类为抽象类, 抽象类不能实例化,主要是作为接口定义. 51.介绍在项目开发中的角色 开发人员角色,负责详细设计、接口设计、单元测试计划和代码编写,一般要负责自己开 发模块的单元测试. 52.文档和代码不一致应如何处理? 确认文档是否存在错误和滞后,如果是则应该及时的更新文档,使之与代码保存一致;反 之如果代码没有按照正确的文档所描述的进行设计, 则应该更正代码, 使之符合设计的要求. 53.修改代码或文档后,应提交哪些信息?配置管理人员做哪些工作? 应该提交配置标识项和相关的代码和文档. 54.使用过哪些配置管理工具? Microsoft Visual SourceSafe 和Rational ClearCase 55.配置管理人员的职责和主要工作内容 软件配置管理(Software Configuration Management)的目的是在整个项目的生命周期建立 和维护软件项目产品的完整性. 软件配置管理包括在给定的时间点上标示软件的配置 (即选 定的软件工作产品及其描述),系统的控制对配置的更改,并在整个软件生命周期中维护配 置的完整性和可跟踪性.置于软件配置管理之下的工作产品包括需向客户交付的软件产品 (如软件需求文档和代码),以及与建立这些产品相关的项目(如编译程序). 56.C++ 中常用到的 this 的含义 24 类中隐含的指向对象自身的指针,不用声明. 57.用过 stl 模板吗? 标准模板库包括容器、 算法和迭代子, 包括向量类、 链表类. MFC 中常用的为 Clist, Carray. 58.构造函数与析构函数的功能? 构造函数用来初始化.析构函数用来做清除工作,一般包括内存释放. 59.MFC 的熟悉程度,说明 CListView 和CListCtrl 有什么不同. ClistView 是从视图派生,ClistCtrl 是从控件类派生,在ClistView 中通过使用 GetListCtrl 可以获得 ClistCtrl 指针,将其作为一个 ClistCtrl 控件来使用. 60.Socket 通讯过程中如何保证连接正常. 一般通过握手机制来确定链接正常,即定时在客户和服务器之间发送连接帧和确认帧来确 定.如果是使用 MFC 的Csocket,通过检测 OnClose 事件和错误处理可以确定连接是否正 常. 61.在开发过程中如何使用 SourceSafe. 主要在版本控制和配置管理过程中使用 SourceSafe. 62.下面的程序是否有错误,如果有错,请说明原因. struct A1 { int i; }; A1 a1; a1.i = 0; 没有错误 class A1 { int i;}; A1 a1; a1.i = 0; 有错误,未显示权限定义符缺省为 private. 63.下面程序是否有错,有错,请说明原因. char szTest[] = "hello"; char* const psz = szTest; psz[0] = 'b'; 没有错误,psz 指向的字符串指针是常量,指针不能被修改,但指针指向的内容可以修改 64.在继承层次上,构造函数和析构函数的调用顺序如何? 构造函数:先基类,后派生类 析构函数:先派生类,后基类 65.如果一个包容器中包含指向对象的指针,当从包容器中删除某个指针时,会析构该指针 指向的对象吗? 包容器并不关心存放对象的类型,所以从包容器中删除成员时,仅仅只是从容器中释放该 成员占有的空间. 如果该成员是一个指向某个对象的指针, 由用户保证指针指向对象的内存 释放. 66.如何阻止构造函数的自动转换 首先要理解什么是构造函数的自动转换. 比如有 class A {….}; class B { B(A& func(B) {}; main() { A a; Func(a); } 在调用 Func(a)时,会自动调用 B(A&)构造函数,保证传给 Func 的参数是 B 类型.应该采 用explicit 关键字来防止构造函数的自动转换. 67.类的成员函数作为回调函数有什么要求? 必须是静态成员函数. 68.分析代码,给出 i, j ,k 的结果. int i = 0, j = 0; k = 0; if (++i || j++ || ++k ) {} 主要是注意前置操作符和后置操作符的区别,还要考虑 或操作 在条件判断中的实现(如果一个为真,则 不进行后续判断). i = 1, j = 0, k = 0 25 69.标准建模语言 UML 的重要内容可以由哪五类图(共9种图形)来定义,并简要说明这 些图所描述的对象. 标准建模语言 UML 的重要内容可以由下列五类图(共9种图形)来定义: 第一类是用例图,从用户角度描述系统功能,并指出各功能的操作者. 第二类是静态图(Static diagram),包括类图、对象图和包图.其中类图描述系统中类的静态结 构.不仅定义系统中的类,表示类之间的联系如关联、依赖、聚合等,也包括类的内部结构(类 的属性和操作).类图描述的是一种静态关系,在系统的整个生命周期都是有效的.对象图是 类图的实例,几乎使用与类图完全相同的标识.他们的不同点在于对象图显示类的多个对象 实例,而不是实际的类.一个对象图是类图的一个实例.由于对象存在生命周期,因此对象图 只能在系统某一时间段存在.包由包或类组成,表示包与包之间的关系.包图用于描述系统 的分层结构. 第三类是行为图(Behavior diagram),描述系统的动态模型和组成对象间的交互关系.其中状 态图描述类的对象所有可能的状态以及事件发生时状态的转移条件.通常,状态图是对类图 的补充.在实用上并不需要为所有的类画状态图,仅为那些有多个状态其行为受外界环境的 影响并且发生改变的类画状态图. 而活动图描述满足用例要求所要进行的活动以及活动间的 约束关系,有利于识别并行活动. 第四类是交互图(Interactive diagram),描述对象间的交互关系.其中顺序图显示对象之间的动 态合作关系,它强调对象之间消息发送的顺序,同时显示对象之间的交互;合作图描述对象间 的协作关系,合作图跟顺序图相似,显示对象间的动态合作关系. 除显示信息交换外,合作图还 显示对象以及它们之间的关系.如果强调时间和顺序,则使用顺序图;如果强调上下级关系, 则选择合作图.这两种图合称为交互图. 第五类是实现图( Implementation diagram ). 其中构件图描述代码部件的物理结构及各部件之 间的依赖关系.一个部件可能是一个资源代码部件、一个二进制部件或一个可执行部件.它 包含逻辑类或实现类的有关信息.部件图有助于分析和理解部件之间的相互影响程度. 70.什么是虚拟函式、虚拟继承? 71.你在使用面向对象程序设计语言和可重用的对象类库上有何经验?实际效果如何?有什 么优缺点? 72.引用和指针之间有什么区别? 引用是一个别名,而指针是一个保存地址的变量.引用不能为空也不能赋值. 73.什么是函数重载? 函数重载是指用同一个函数名写多个函数的能力, 这些函数根据参数的数目、 类型加以区别. 74.C++与其它传统编程语言相比有何特点? 75.为什么有些类成员函数在类声明中定义而有一些则不是呢? 在声明中定义成员函数的实现是按内嵌方式进行的. 一般来讲, 只有当函数特别简单才这么 做.注意,即使函数是在类声明之外声明的,你也可以用关键字 inline 使成员函数变成内嵌 的. 76.面向对象分析和设计的阶段有哪些? 概念化、分析、设计、实现、测试、发行(谨供参考) 77.什么是多态性?多态性是如何实现的? 函数多态性是指用多个含义重载一个函数的能力,即允许创建多个名称相同的函数. 可通过改变同名函数变元的类型或个数来实现. 78.查错:下面这段代码有什么错误? class Shape() { public: Shape(); virtual ~Shape(); virtual Shape(const Shape&); } 不能声明一个副本构造函数为虚拟. 79.查错:下面的代码有什么错? int main() { 26 int SomeVariable = 5; count <<"SomeVariable:"< int * FuncOne(); int main() { int * pint = FuncOne(); cout <<"the value of pInt back in main is:"<< * pint << endl return 0; } int * FuncOne() { int * pint = new int(5); count <<"the value of pInt in FuncOne is:"<<* pint< int FuncOne(); int main() { int theint = FuncOne(); cout <<"the value of pInt back in main is:"<0.000001&&x<-0.000001) 16. Internet 采用哪种网络协议?该协议的主要层次结构? tcp/ip 应用层/传输层/网络层/数据链路层/物理层 17. Internet 物理地址和 IP 地址转换采用什么协议? ARP (Address Resolution Protocol)(地址解析协议) 18.IP 地址的编码分为哪俩部分? IP 地址由两部分组成,网络号和主机号.不过是要和"子网掩码"按位与上之后才能区分 哪些是网络位哪些是主机位. 2.用户输入 M,N 值,从1至N开始顺序循环数数,每数到 M 输出该数值,直至全部输出. 写出 C 程序. 循环链表,用取余操作做 3.不能做 switch()的参数类型是: switch 的参数不能为实型. 华为 1、局部变量能否和全局变量重名? 答:能,局部会屏蔽全局.要用全局变量,需要使用"::" 局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会 用到全局变量.对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如 在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内 2、如何引用一个已经定义过的全局变量? 答:extern 可以用引用头文件的方式, 也可以用 extern 关键字, 如果用引用头文件方式来引用某个在头 文件中声明的全局变理,假定你将那个变写错了,那么在编译期间会报错,如果你用 extern 方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错 3、全局变量可不可以定义在可被多个.C 文件包含的头文件中?为什么? 答:可以,在不同的 C 文件中以 static 形式来声明同名全局变量. 可以在不同的 C 文件中声明同名的全局变量,前提是其中只能有一个 C 文件中对此变量赋 初值,此时连接不会出错 4、语句 for( ;1 ;)有什么问题?它是什么意思? 答:和while(1)相同. 5、do……while 和while……do 有什么区别? 答:前一个循环一遍再判断,后一个判断以后再循环 6、请写出下列代码的输出内容 #include main() { int a,b,c,d; a=10; b=a++; c=++a; d=10*a++; printf("b,c,d:%d,%d,%d",b,c,d); return 0; } 答:10,12,120 1、static 全局变量与普通的全局变量有什么区别?static 局部变量和普通局部变量有什么区 28 别?static 函数与普通函数有什么区别? 全局变量(外部变量)的说明之前再冠以 static 就构成了静态的全局变量.全局变量本身就是 静态存储方式, 静态全局变量当然也是静态存储方式. 这两者在存储方式上并无不同.这 两者的区别虽在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组 成时, 非静态的全局变量在各个源文件中都是有效的. 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它.由于静态 全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用, 因此可以避免在 其它源文件中引起错误. 从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的 生存期.把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围. static 函数与普通函数作用域不同.仅在本文件.只在当前源文件中使用的函数应该说明为 内部函数(static),内部函数应该在当前源文件中说明和定义.对于可在当前源文件以外使用 的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件 static 全局变量与普通的全局变量有什么区别:static 全局变量只初使化一次,防止在其他文 件单元中被引用; static 局部变量和普通局部变量有什么区别:static 局部变量只被初始化一次,下一次依据上 一次结果值; static 函数与普通函数有什么区别:static 函数在内存中只有一份,普通函数在每个被调用中 维持一份拷贝 2、程序的局部变量存在于(堆栈)中,全局变量存在于(静态区 )中,动态申请数据存 在于( 堆)中. 3、设有以下说明和定义: typedef union {long i; int k[5]; char c;} DATE; struct data { int cat; DATE cow; double dog;} too; DATE max; 则语句 printf("%d",sizeof(struct date)+sizeof(max));的执行结果是:___52____ 答:DATE 是一个 union, 变量公用空间. 里面最大的变量类型是 int[5], 占用 20 个字节. 所 以它的大小是 20 data 是一个 struct, 每个变量分开占用空间. 依次为 int4 + DATE20 + double8 = 32. 所以结果是 20 + 32 = 52. 当然...在某些 16 位编辑器下, int 可能是 2 字节,那么结果是 int2 + DATE10 + double8 = 20 4、队列和栈有什么区别? 队列先进先出,栈后进先出 5、写出下列代码的输出内容 #include int inc(int a) { return(++a); } int multi(int*a,int*b,int*c) { return(*c=*a**b); } typedef int(FUNC1)(int in); typedef int(FUNC2) (int*,int*,int*); void show(FUNC2 fun,int arg1, int*arg2) { INCp=&inc; int temp =p(arg1); fun(&temp,&arg1, arg2); printf("%d\n",*arg2); } main() { int a; 29 show(multi,10,&a); return 0; } 答:110 7、请找出下面代码中的所以错误 说明:以下代码是把一个字符串倒序,如"abcd"倒序后变为"dcba" 1、#include"string.h" 2、main() 3、{ 4、 char*src="hello,world"; 5、 char* dest=NULL; 6、 int len=strlen(src); 7、 dest=(char*)malloc(len); 8、 char* d=dest; 9、 char* s=src[len]; 10、 while(len--!=0) 11、 d++=s--; 12、 printf("%s",dest); 13、 return 0; 14、} 答: 方法 1: int main(){ char* src = "hello,world"; int len = strlen(src); char* dest = (char*)malloc(len+1);//要为\0 分配一个空间 char* d = dest; char* s = &src[len-1];//指向最后一个字符 while( len-- != 0 ) *d++=*s--; *d = 0;//尾部要加\0 printf("%s\n",dest); free(dest);// 使用完,应当释放空间,以免造成内存汇泄露 return 0; } 方法 2: #include #include main() { char str[]="hello,world"; int len=strlen(str); char t; for(int i=0; i #include int main(void) { long l; char *str = "98765432"; l = atol(lstr); printf("string = %s integer = %ld\n", str, l); return(0); } 2.对于一个频繁使用的短小函数,在C语言中应用什么实现,在C++中应用什么实现? c 用宏定义,c++用inline 3.直接链接两个信令点的一组链路称作什么? PPP 点到点连接 4.接入网用的是什么接口? 5.voip 都用了那些协议? 6.软件测试都有那些种类? 黑盒:针对系统功能的测试 白盒:测试函数功能,各函数接口 7.确定模块的功能和模块的接口是在软件设计的那个队段完成的? 概要设计阶段 8.enum string { x1, x2, x3=10, x4, x5, }x; 问x= 0x801005,0x8010f4 ; 9.unsigned char *p1; unsigned long *p2; p1=(unsigned char *)0x801000; p2=(unsigned long *)0x810000; 请问 p1+5= ; p2+5= ; 三.选择题: 1.Ethternet 链接到 Internet 用到以下那个协议? A.HDLC;B.ARP;C.UDP;D.TCP;E.ID 2.属于网络层协议的是: A.TCP;B.IP;C.ICMP;D.X.25 3.Windows 消息调度机制是: A.指令队列;B.指令堆栈;C.消息队列;D.消息堆栈; 4. unsigned short hash(unsigned short key) { 31 return (key>>)%256 } 请问 hash(16),hash(256)的值分别是: A.1.16;B.8.32;C.4.16;D.1.32 四.找错题: 1.请问下面程序有什么错误? int a[60][250][1000],i,j,k; for(k=0;k<=1000;k++) for(j=0;j<250;j++) for(i=0;i<60;i++) a[i][j][k]=0; 把循环语句内外换一下 2. #define Max_CB 500 void LmiQueryCSmd(Struct MSgCB * pmsg) { unsigned char ucCmdNum; ...... for(ucCmdNum=0;ucCmdNumMax_GT_Length) { return GT_Length_ERROR; } ....... } 五.问答题: 1.IP Phone 的原理是什么? IPV6 2.TCP/IP 通信建立的过程怎样,端口有什么作用? 三次握手,确定是哪个应用程序使用该协议 3.1 号信令和 7 号信令有什么区别,我国某前广泛使用的是那一种? 4.列举 5 种以上的电话新业务? 微软亚洲技术中心的面试题! ! ! 1.进程和线程的差别. 线程是指进程内的一个执行单元,也是进程内的可调度实体. 与进程的区别: (1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位 (2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行 (3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于 进程的资源. (4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销 32 明显大于创建或撤消线程时的开销. 2.测试方法 人工测试:个人复查、抽查和会审 机器测试:黑盒测试和白盒测试 2.Heap 与stack 的差别. Heap 是堆,stack 是栈. Stack 的空间由操作系统自动分配/释放,Heap 上的空间手动分配/释放. Stack 空间有限,Heap 是很大的自由存储区 C 中的 malloc 函数分配的内存空间即在堆上,C++中对应的是 new 操作符. 程序在编译期对变量和函数分配内存都在栈上进行,且程序运行过程中函数调用时参数的传 递也在栈上进行 3.Windows 下的内存是如何管理的? 4.介绍.Net 和.Net 的安全性. 5.客户端如何访问.Net 组件实现 Web Service? 6.C/C++编译器中虚表是如何完成的? 7.谈谈 COM 的线程模型.然后讨论进程内/外组件的差别. 8.谈谈 IA32 下的分页机制 小页(4K)两级分页模式,大页(4M)一级 9.给两个变量,如何找出一个带环单链表中是什么地方出现环的? 一个递增一,一个递增二,他们指向同一个接点时就是环出现的地方 10.在IA32 中一共有多少种办法从用户态跳到内核态? 通过调用门,从ring3 到ring0,中断从 ring3 到ring0,进入 vm86 等等 11.如果只想让程序有一个实例运行,不能运行两个.像winamp 一样,只能开一个窗口, 怎样实现? 用内存映射或全局原子(互斥变量) 、查找窗口句柄.. FindWindow,互斥,写标志到文件或注册表,共享内存.. 12.如何截取键盘的响应,让所有的'a'变成'b'? 键盘钩子 SetWindowsHookEx 13.Apartment 在COM 中有什么用?为什么要引入? 14.存储过程是什么?有什么用?有什么优点? 我的理解就是一堆 sql 的集合,可以建立非常复杂的查询,编译运行,所以运行一次后,以 后再运行速度比单独执行 SQL 快很多 15.Template 有什么特点?什么时候用? 16.谈谈 Windows DNA 结构的特点和优点. 网络编程中设计并发服务器,使用多进程与多线程 ,请问有什么区别? 1,进程:子进程是父进程的复制品.子进程获得父进程数据空间、堆和栈的复制品. 2,线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他 线程共享数据,但拥有自己的栈空间,拥有独立的执行序列. 两者都可以提高程序的并发度,提高程序运行效率和响应时间. 线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程正相 反.同时,线程适合于在 SMP 机器上运行,而进程则可以跨机器迁移. 思科 1. 用宏定义写出 swap(x,y) #define swap(x, y)\ x = x + y;\ y = x - y;\ x = x - y; 2.数组 a[N],存放了 1 至N-1 个数,其中某个数重复一次.写一个函数,找出被重复的数 字.时间复杂度必须为 o(N)函数原型: 33 int do_dup(int a[],int N) 3 一语句实现 x 是否为 2 的若干次幂的判断 int i = 512; cout << boolalpha << ((i & (i - 1)) ? false : true) << endl; 4.unsigned int intvert(unsigned int x,int p,int n)实现对 x 的进行转换,p 为起始转化位,n 为需 要转换的长度,假设起始点在右边.如x=0b0001 0001,p=4,n=3 转换后 x=0b0110 0001 unsigned int intvert(unsigned int x,int p,int n){ unsigned int _t = 0; unsigned int _a = 1; for(int i = 0; i < n; ++i){ _t |= _a; _a = _a << 1; } _t = _t << p; x ^= _t; return x; } 慧通: 1.什么是预编译 2.何时需要预编译: 1、总是使用不经常改动的大型代码体. 2、程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项.在这种 情况下,可以将所有包含文件预编译为一个预编译头. 3. char * const p; char const * p const char *p 上述三个有什么区别? char * const p; //常量指针,p 的值不可以修改 char const * p;//指向常量的指针,指向的常量值不可以改 const char *p; //和char const *p char 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 是指针,它们指向相同的常量区域. 12. 以下代码中的两个 sizeof 用法有问题吗?[C 易] void UpperCase( char str[] ) // 将str 中的小写字母转换成大写字母 { for( size_t i=0; i(Y)?(Y):(X))//结尾没有; 2、嵌入式系统中经常要用到无限循环,你怎么用 C 编写死循环. while(1){}或者 for(;;) 3、关键字 static 的作用是什么? 定义静态变量 35 4、关键字 const 有什么含意? 表示常量不可以修改的变量. 5、关键字 volatile 有什么含意?并举出三个不同的例子? 提示编译器对象的值可能在编译器未监测到的情况下改变. 6.int (*s[10])(int) 表示的是什么啊 int (*s[10])(int) 函数指针数组,每个指针指向一个 int func(int param)的函数. 1.有以下表达式: int a=248; b=4;int const c=21;const int *d=&a; int *const e=&b;int const *f const =&a; 请问下列表达式哪些会被编译器禁止?为什么? *c=32;d=&b;*d=43;e=34;e=&a;f=0x321f; *c 这是个什么东东,禁止 *d 说了是 const, 禁止 e = &a 说了是 const 禁止 const *f const =&a; 禁止 2.交换两个变量的值,不使用第三个变量.即a=3,b=5,交换之后 a=5,b=3; 有两种解法, 一种用算术算法, 一种用^(异或) a = a + b; b = a - b; a = a - b; or a = a^b;// 只能对 int,char.. b = a^b; a = a^b; or a ^= b ^= a; 3.c 和c++中的 struct 有什么不同? c 和c++中struct 的主要区别是 c 中的 struct 不可以含有成员函数,而c++中的 struct 可以. c++中struct 和class 的主要区别在于默认的存取权限不同,struct 默认为 public,而class 默 认为 private 4. #include #include void getmemory(char *p) { p=(char *) malloc(100); strcpy(p,"hello world"); } int main( ) { char *str=NULL; getmemory(str); printf("%s/n",str); free(str); return 0; } 程序崩溃,getmemory 中的 malloc 不能返回动态内存, free()对str 操作很危险 5. char szstr[10]; strcpy(szstr,"0123456789"); 产生什么结果?为什么? 长度不一样,会造成非法的 OS 6.列举几种进程的同步机制,并比较其优缺点. 原子操作 信号量机制 自旋锁 管程,会合,分布式系统 36 7.进程之间通信的途径 共享存储系统 消息传递系统 管道:以文件系统为基础 11.进程死锁的原因 资源竞争及进程推进顺序非法 12.死锁的 4 个必要条件 互斥、请求保持、不可剥夺、环路 13.死锁的处理 鸵鸟策略、预防策略、避免策略、检测与解除死锁 15. 操作系统中进程调度策略有哪几种? FCFS(先来先服务),优先级,时间片轮转,多级反馈 8.类的静态成员和非静态成员有何区别? 类的静态成员每个类只有一个,非静态成员每个对象一个 9.纯虚函数如何定义?使用时应注意什么? virtual void f()=0; 是接口,子类必须要实现 10.数组和链表的区别 数组:数据顺序存储,固定大小 连表:数据可以随机存储,大小可动态改变 12.ISO 的七层模型是什么?tcp/udp 是属于哪一层?tcp/udp 有何优缺点? 应用层 表示层 会话层 运输层 网络层 物理链路层 物理层 tcp /udp 属于运输层 TCP 服务提供了数据流传输、可靠性、有效流控制、全双工操作和多路复用技术等. 与TCP 不同, UDP 并不提供对 IP 协议的可靠机制、流控制以及错误恢复功能等.由于 UDP 比较简单, UDP 头包含很少的字节,比TCP 负载消耗少. tcp: 提供稳定的传输服务,有流量控制,缺点是包头大,冗余性不好 udp: 不提供稳定的服务,包头小,开销小 1:(void *)ptr 和(*(void**))ptr 的结果是否相同?其中 ptr 为同一个指针 .(void *)ptr 和(*(void**))ptr 值是相同的 2: int main() { int x=3; printf("%d",x); return 1; } 问函数既然不会被其它函数调用,为什么要返回 1? mian 中,c 标准认为 0 表示成功,非0表示错误.具体的值是某中具体出错信息 1,要对绝对地址 0x100000 赋值,我们可以用(unsigned int*)0x100000 = 1234; 那么要是想让程序跳转到绝对地址是 0x100000 去执行,应该怎么做? 37 *((void (*)( ))0x100000 ) ( ); 首先要将 0x100000 强制转换成函数指针,即: (void (*)())0x100000 然后再调用它: *((void (*)())0x100000)(); 用typedef 可以看得更直观些: typedef void(*)() voidFuncPtr; *((voidFuncPtr)0x100000)(); 2,已知一个数组 table,用一个宏定义,求出数据的元素个数 #define NTBL #define NTBL (sizeof(table)/sizeof(table[0])) 面试题: 1.线程与进程的区别和联系? 线程是否具有相同的堆栈? dll 是否有独立的堆栈? 进程是死的,只是一些资源的集合,真正的程序执行都是线程来完成的,程序启动的时候操 作系统就帮你创建了一个主线程. 每个线程有自己的堆栈. DLL 中有没有独立的堆栈, 这个问题不好回答, 或者说这个问题本身是否有问题. 因为 DLL 中的代码是被某些线程所执行,只有线程拥有堆栈,如果 DLL 中的代码是 EXE 中的线程所 调用, 那么这个时候是不是说这个 DLL 没有自己独立的堆栈?如果 DLL 中的代码是由 DLL 自己创建的线程所执行,那么是不是说 DLL 有独立的堆栈? 以上讲的是堆栈,如果对于堆来说,每个 DLL 有自己的堆,所以如果是从 DLL 中动态分配 的内存,最好是从 DLL 中删除,如果你从 DLL 中分配内存,然后在 EXE 中,或者另外一 个DLL 中删除,很有可能导致程序崩溃 2. unsigned short A = 10; printf("~A = %u\n", ~A); char c=128; printf("c=%d\n",c); 输出多少?并分析过程 第一题,~A =0xfffffff5,int 值为-11,但输出的是 uint.所以输出 4294967285 第二题, c=0x10,输出的是 int, 最高位为 1, 是负数, 所以它的值就是 0x00 的补码就是 128, 所以输出-128. 这两道题都是在考察二进制向 int 或uint 转换时的最高位处理. 3.分析下面的程序: void GetMemory(char **p,int num) { *p=(char *)malloc(num); } int main() { char *str=NULL; GetMemory(&str,100); strcpy(str,"hello"); free(str); if(str!=NULL) { strcpy(str,"world"); } printf("\n str is %s",str); getchar(); } 问输出结果是什么?希望大家能说说原因,先谢谢了 输出 str is world. free 只是释放的 str 指向的内存空间,它本身的值还是存在的. 38 所以 free 之后,有一个好的习惯就是将 str=NULL. 此时 str 指向空间的内存已被回收,如果输出语句之前还存在分配空间的操作的话,这段存储 空间是可能被重新分配给其他变量的, 尽管这段程序确实是存在大大的问题(上面各位已经说得很清楚了) ,但是通常会打印出 world 来. 这是因为,进程中的内存管理一般不是由操作系统完成的,而是由库函数自己完成的. 当你 malloc 一块内存的时候, 管理库向操作系统申请一块空间 (可能会比你申请的大一些) , 然后在这块空间中记录一些管理信息(一般是在你申请的内存前面一点) ,并将可用内存的 地址返回.但是释放内存的时候,管理库通常都不会将内存还给操作系统,因此你是可以继 续访问这块地址的,只不过. . . . . . . .楼上都说过了,最好别这么干. 4.char a[10],strlen(a)为什么等于 15?运行的结果 #include "stdio.h" #include "string.h" void main() { char aa[10]; printf("%d",strlen(aa)); } sizeof()和初不初始化,没有关系; strlen()和初始化有关. char (*str)[20];/*str 是一个数组指针,即指向数组的指针.*/ char *str[20];/*str 是一个指针数组,其元素为指针型数据.*/ long a=0x801010; a+5=? 0x801010 用二进制表示为: "1000 0000 0001 0000 0001 0000" ,十进制的值为 8392720,再 加上 5 就是 8392725 罗1) 给定结构 struct A { char t:4; char k:4; unsigned short i:8; unsigned long m; }; 问sizeof(A) = ? 给定结构 struct A { char t:4; 4 位char k:4; 4 位unsigned short i:8; 8 位unsigned long m; // 偏移 2 字节保证 4 字节对齐 }; // 共8字节 2)下面的函数实现在一个数上加一个数,有什么错误?请改正. int add_n ( int n ) { static int i = 100; i += n; return i; } 当你第二次调用时得不到正确的结果,难道你写个函数就是为了调用一次?问题就出在 static 上? 39 5. // 帮忙分析一下 #include #include #include #include #include #include typedef struct AA { int b1:5; int b2:2; }AA; void main() { AA aa; char cc[100]; strcpy(cc,"0123456789abcdefghijklmnopqrstuvwxyz"); memcpy(&aa,cc,sizeof(AA)); cout << aa.b1 <0 && b>0 &&(*ca || *c>b))); } 分析: struct bit { int a:3; int b:2; int c:3; }; int main() { bit s; char *c=(char*)&s; cout< int main(void) { int **p; int arr[100]; p = &arr; return 0; } 解答: 搞错了,是指针类型不同, int **p; //二级指针 &arr; //得到的是指向第一维为 100 的数组的指针 #include int main(void) { int **p, *q; int arr[100]; q = arr; p = &q; return 0; } 9.下面这个程序执行后会有什么错误或者效果: #define MAX 255 int main() { unsigned char A[MAX],i;//i 被定义为 unsigned char for (i=0;i<=MAX;i++) A[i]=i; } 解答:死循环加数组越界访问(C/C++不进行数组越界检查) MAX=255 数组 A 的下标范围为:0..MAX-1,这是其一.. 其二.当i循环到 255 时,循环内执行: A[255]=255; 这句本身没有问题..但是返回 for (i=0;i<=MAX;i++)语句时, 由于 unsigned char 的取值范围在(0..255),i++以后 i 又为 0 了..无限循环下去. 42 struct name1{ char str; short x; int num; } struct name2{ char str; int num; short x; } sizeof(struct name1)=8,sizeof(struct name2)=12 在第二个结构中,为保证 num 按四个字节对齐,char 后必须留出 3 字节的空间;同时为保 证整个结构的自然对齐 (这里是 4 字节对齐) , 在x后还要补齐 2 个字节, 这样就是 12 字节. intel: 1. A.c 和B.c 两个 c 文件中使用了两个相同名字的 static 变量,编译的时候会不会有问题? 这两个 static 变量会保存到哪里(栈还是堆或者其他的)? static 的全局变量,表明这个变量仅在本模块中有意义,不会影响其他模块. 他们都放在数据区,但是编译器对他们的命名是不同的. 如果要使变量在其他模块也有意义的话,需要使用 extern 关键字. 2. struct s1 { int i: 8; int j: 4; int a: 3; double b; }; struct s2 { int i: 8; int j: 4; double b; int a:3; }; printf("sizeof(s1)= %d\n", sizeof(s1)); printf("sizeof(s2)= %d\n", sizeof(s2)); result: 16, 24 第一个 struct s1 { int i: 8; int j: 4; int a: 3; double b; }; 理论上是这样的,首先是 i 在相对 0 的位置,占8位一个字节,然后,j 就在相对一个字节的位置,由于一 个位置的字节数是 4 位的倍数,因此不用对齐,就放在那里了,然后是 a,要在 3 位的倍数关系的位置上, 因此要移一位,在15 位的位置上放下,目前总共是 18 位,折算过来是 2 字节 2 位的样子,由于 double 是8字节的,因此要在相对 0 要是 8 个字节的位置上放下,因此从 18 位开始到 8 个字节之间的位置被忽略, 直接放在 8 字节的位置了,因此,总共是 16 字节. 第二个最后会对照是不是结构体内最大数据的倍数,不是的话,会补成是最大数据的倍数 接下来是编程问题: 本人很弱,这几个题也搞不定,特来求救: 1)读文件 file1.txt 的内容(例如) : 12 34 56 输出到 file2.txt: 56 43 34 12 (逆序) 2)输出和为一个给定整数的所有组合 例如 n=5 5=1+4;5=2+3(相加的数不能重复) 则输出 1,4;2,3. 望高手赐教! ! 第一题,注意可增长数组的应用. #include #include int main(void) { int MAX = 10; int *a = (int *)malloc(MAX * sizeof(int)); int *b; FILE *fp1; FILE *fp2; fp1 = fopen("a.txt","r"); if(fp1 == NULL) { printf("error1"); exit(-1); } fp2 = fopen("b.txt","w"); if(fp2 == NULL) { printf("error2"); exit(-1); } int i = 0; int j = 0; while(fscanf(fp1,"%d",&a[i]) != EOF) { i++; j++; if(i >= MAX) { MAX = 2 * MAX; b = (int*)realloc(a,MAX * sizeof(int)); if(b == NULL) { printf("error3"); exit(-1); } a = b; } } for(;--j >= 0;) fprintf(fp2,"%d\n",a[j]); fclose(fp1); fclose(fp2); return 0; } 第二题. #include int main(void) { unsigned long int i,j,k; printf("please input the number\n"); 44 scanf("%d",&i); if( i % 2 == 0) j = i / 2; else j = i / 2 + 1; printf("The result is \n"); for(k = 0; k < j; k++) printf("%d = %d + %d\n",i,k,i - k); return 0; } #include void main() { unsigned long int a,i=1; scanf("%d",&a); if(a%2==0) { for(i=1;i void test(FILE *fread, FILE *fwrite) { char buf[1024] = {0}; if (!fgets(buf, sizeof(buf), fread)) return; test( fread, fwrite ); fputs(buf, fwrite); } int main(int argc, char *argv[]) { FILE *fr = NULL; FILE *fw = NULL; fr = fopen("data", "rb"); fw = fopen("dataout", "wb"); test(fr, fw); fclose(fr); fclose(fw); return 0; } 3.在对齐为 4 的情况下 45 struct BBB { long num; char *name; short int data; char ha; short ba[5]; }*p; p=0x1000000; p+0x200=____; (Ulong)p+0x200=____; (char*)p+0x200=____; 希望各位达人给出答案和原因,谢谢拉 解答:假设在 32 位CPU 上, sizeof(long) = 4 bytes sizeof(char *) = 4 bytes sizeof(short int) = sizeof(short) = 2 bytes sizeof(char) = 1 bytes 由于是 4 字节对齐, sizeof(struct BBB) = sizeof(*p) = 4 + 4 + 2 + 1 + 1/*补齐*/ + 2*5 + 2/*补齐*/ = 24 bytes (经Dev-C++验证) p=0x1000000; p+0x200=____; = 0x1000000 + 0x200*24 (Ulong)p+0x200=____; = 0x1000000 + 0x200 (char*)p+0x200=____; = 0x1000000 + 0x200*4 你可以参考一下指针运算的细节 4. 写一段程序,找出数组中第 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) 谢谢! 可以先用快速排序进行排序,其中用另外一个进行地址查找 代码如下,在VC++6.0 运行通过.给分吧^-^ //快速排序 #include usingnamespacestd; intPartition (int*L,intlow,int high) { inttemp = L[low]; intpt = 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; returnlow; } voidQSort (int*L,intlow,int high) { 46 if (low < high) { intpl = Partition (L,low,high); QSort (L,low,pl - 1); QSort (L,pl + 1,high); } } intmain () { intnarry[100],addr[100]; intsum = 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; intk; cout << "Please input place you want:" << endl; cin >> k; intaa = 1; intkk = 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'; } return0; } 1、找错 Void test1() { char string[10]; char* str1="0123456789"; strcpy(string, str1);// 溢出,应该包括一个存放'\0'的字符 string[11] } Void test2() { char string[10], str1[10]; for(I=0; I<10;I++) { str1[i] ='a'; 47 } strcpy(string, str1);// I,i 没有声明. } Void test3(char* str1) { char string[10]; if(strlen(str1)<=10)// 改成<10,字符溢出,将strlen 改为 sizeof 也可以 { strcpy(string, str1); } } 2. void g(int**); int main() { int line[10],i; int *p=line; //p 是地址的地址 for (i=0;i<10;i++) { *p=i; g(&p);//数组对应的值加 1 } for(i=0;i<10;i++) printf("%d\n",line[i]); return 0; } void g(int**p) { (**p)++; (*p)++;// 无效 } 输出: 1 2 3 4 5 6 7 8 9 10 3. 写出程序运行结果 int sum(int a) { auto int c=0; static int b=3; c+=1; b+=2; return(a+b+c); } void main() { int I; int a=2; for(I=0;I<5;I++) { printf("%d,", sum(a)); } } // static 会保存上次结果,记住这一点,剩下的自己写 输出:8,10,12,14,16, 4. 48 int func(int a) { int b; switch(a) { case 1: 30; case 2: 20; case 3: 16; default: 0 } return b; } 则func(1)=? // b 定义后就没有赋值. 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] 解释:指针一次移动一个 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; } 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; 49 if( n= =2 ) return a[n-1] >= a[n-2]; return fun( a,n-1) && ( a[n-1] >= a[n-2] ); } 4、编写算法,从10 亿个浮点数当中,选出其中最大的 10000 个. 用外部排序,在《数据结构》书上有 《计算方法导论》在找到第 n 大的数的算法上加工 5、编写一 unix 程序,防止僵尸进程的出现. 同学的 4 道面试题,应聘的职位是搜索引擎工程师,后两道超级难, (希望大家多给一些算 发) 1.给两个数组和他们的大小, 还有一动态开辟的内存, 求交集, 把交集放到动态内存 dongtai, 并且返回交集个数 long jiaoji(long* a[],long b[],long* alength,long blength,long* dongtai[]) 2.单连表的建立,把'a'--'z'26 个字母插入到连表中,并且倒序,还要打印! 方法 1: typedef struct val { int date_1; struct val *next; }*p; void main(void) { char c; for(c=122;c>=97;c--) { p.date=c; p=p->next; } p.next=NULL; } 方法 2: node *p = NULL; node *q = NULL; node *head = (node*)malloc(sizeof(node)); head->data = ' ';head->next=NULL; node *first = (node*)malloc(sizeof(node)); first->data = 'a';first->next=NULL;head->next = first; p = first; int longth = 'z' - 'b'; int i=0; while ( i<=longth ) { node *temp = (node*)malloc(sizeof(node)); temp->data = 'b'+i;temp->next=NULL;q=temp; head->next = temp; temp->next=p;p=q; i++; } print(head); 3.可怕的题目终于来了 象搜索的输入信息是一个字符串, 统计 300 万输入信息中的最热门的前十条, 我们每次输入 的一个字符串为不超过 255byte,内存使用只有 1G, 请描述思想,写出算发(c 语言) ,空间和时间复杂度, 4.国内的一些帖吧,如baidu,有几十万个主题,假设每一个主题都有上亿的跟帖子,怎么样 设计这个系统速度最好,请描述思想,写出算发(c 语言) ,空间和时间复杂度, 1. #include string.h 50 main(void) { char *src="hello,world"; char *dest=NULL; dest=(char *)malloc(strlen(src)); int len=strlen(str); char *d=dest; char *s=src[len]; while(len--!=0) d++=s--; printf("%s",dest); } 找出错误! ! #include "string.h" #include "stdio.h" #include "malloc.h" main(void) { char *src="hello,world"; char *dest=NULL; dest=(char *)malloc(sizeof(char)*(strlen(src)+1)); int len=strlen(src); char *d=dest; char *s=src+len-1; while(len--!=0) *d++=*s--; *d='\0'; printf("%s",dest); } 1. 简述一个 Linux 驱动程序的主要流程与功能. 2. 请列举一个软件中时间换空间或者空间换时间的例子. void swap(int a,int b) { int c; c=a;a=b;b=a; } --->空优 void swap(int a,int b) { a=a+b;b=a-b;a=a-b; } 6. 请问一下程序将输出什么结果? char *RetMenory(void) { char p[] = "hellow world"; return p; } void Test(void) { char *str = NULL; str = RetMemory(); printf(str); } RetMenory 执行完毕,p 资源被回收,指向未知地址.返回地址,str 的内容应是不可预测的, 打印的应该是 str 的地址 7.写一个函数,它的原形是 int continumax(char *outputstr,char *intputstr) 功能: 在字符串中找出连续最长的数字串, 并把这个串的长度返回, 并把这个最长数字串付给其中 一个函数参数 outputstr 所指内存. 例如: "abcd12345ed125ss123456789"的首地址传给 intputstr 后,函数将返回 9,outputstr 所指的值为 123456789 int continumax(char *outputstr, char *inputstr) 51 { char *in = inputstr, *out = outputstr, *temp, *final; int count = 0, maxlen = 0; while( *in != '\0' ) { if( *in > 47 && *in < 58 ) { for(temp = in; *in > 47 && *in < 58 ; in++ ) count++; } else in++; if( maxlen < count ) { maxlen = count; count = 0; final = temp; } } for(int i = 0; i < maxlen; i++) { *out = *final; out++; final++; } *out = '\0'; return maxlen; } 8.不用库函数,用C语言实现将一整型数字转化为字符串 方法 1: int getlen(char *s){ int n; for(n = 0; *s != '\0'; s++) n++; return n; } void reverse(char s[]) { int c,i,j; for(i = 0,j = getlen(s) - 1; i < j; i++,j--){ c = s[i]; s[i] = s[j]; s[j] = c; } } void itoa(int n,char s[]) { int i,sign; if((sign = n) < 0) n = -n; i = 0; do{/*以反序生成数字*/ s[i++] = n%10 + '0';/*get next number*/ }while((n /= 10) > 0);/*delete the number*/ if(sign < 0) s[i+ s[i] = '\0'; reverse(s); } 方法 2: #include using namespace std; void itochar(int num); 52 void itochar(int num) { int i = 0; int j ; char stra[10]; char strb[10]; while ( num ) { stra[i++]=num%10+48; num=num/10; } stra[i] = '\0'; for( j=0; j < i; j++) { strb[j] = stra[i-j-1]; } strb[j] = '\0'; cout<>num; itochar(num); return 0; } 9.前几天面试,有一题想不明白,请教大家! typedef struct { int a:2; int b:2; int c:1; }test; test t; t.a = 1; t.b = 3; t.c = 1; printf("%d",t.a); printf("%d",t.b); printf("%d",t.c); 谢谢! t.a 为01,输出就是 1 t.b 为11,输出就是-1 t.c 为1,输出也是-1 3 个都是有符号数 int 嘛. 这是位扩展问题 01 11 1 编译器进行符号扩展 10.求组合数: 求n个数(1....n)中k个数的组合.... 如:combination(5,3) 要求输出:543,542,541,532,531,521,432,431,421,321, #include int pop(int *); int push(int ); 53 void combination(int ,int ); int stack[3]={0}; top=-1; int main() { int n,m; printf("Input two numbers:\n"); while( (2!=scanf("%d%*c%d",&n,&m)) ) { fflush(stdin); printf("Input error! Again:\n"); } combination(n,m); printf("\n"); } void combination(int m,int n) { int temp=m; push(temp); while(1) { if(1==temp) { if(pop(&temp)&&stack[0]==n) //当栈底元素弹出&&为可能取的最小值,循环退出 break; } else if( push(--temp)) { printf("%d%d%d ",stack[0],stack[1],stack[2]);//§ä¨ì¤@? pop(&temp); } } } int push(int i) { stack[++top]=i; if(top<2) return 0; else return 1; } int pop(int *i) { *i=stack[top--]; if(top>=0) return 0; else return 1; } 1、用指针的方法,将字符串"ABCD1234efgh"前后对调显示 #include #include #include int main() { char str[] = "ABCD1234efgh"; int length = strlen(str); char * p1 = str; char * p2 = str + length - 1; while(p1 < p2) { char c = *p1; *p1 = *p2; 54 *p2 = c; ++p1; --p2; } printf("str now is %s\n",str); system("pause"); return 0; } 2、有一分数序列:1/2,1/4,1/6,1/8……,用函数调用的方法,求此数列前 20 项的和 #include double getValue() { double result = 0; int i = 2; while(i < 42) { result += 1.0 / i;//一定要使用 1.0 做除数,不能用 1,否则结果将自动转化成整数,即0.000000 i += 2; } return result; } int main() { printf("result is %f\n", getValue()); system("pause"); return 0; } 3.有一个数组 a[1000]存放 0--1000;要求每隔二个数删掉一个数, 到末尾时循环至开头继续进 行,求最后一个被删掉的数的原始下标位置. 以7个数为例: {0,1,2,3,4,5,6,7} 0-->1-->2(删除)-->3-->4-->5(删除)-->6-->7-->0(删除) ,如此循环直到 最后一个数被删除. 方法 1:数组 #include using namespace std; #define null 1000 int 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< using namespace std; #define null 0 struct 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<data; return 0; } 方法 3:通用算法 #include #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;inumber==key) { Head=Pointer->next; free(Pointer); break; } Back = Pointer; Pointer=Pointer->next; if(Pointer->number==key) { 58 Back->next=Pointer->next; free(Pointer); break; } void delete(Node* p) { if(Head = Node) while(p); } 1.有一个 16 位的整数,每4位为一个数,写函数求他们的和. 解释: 整数 1101010110110111 和1101+0101+1011+0111 感觉应该不难,当时对题理解的不是很清楚,所以写了一个函数,也不知道对不对. 疑问: 既然是 16 位的整数,1101010110110111 是2进制的,那么函数参数怎么定义呢,请大 虾指教. 答案:用十进制做参数,计算时按二进制考虑. /* n 就是 16 位的数,函数返回它的四个部分之和 */ char SumOfQuaters(unsigned short n) { char c = 0; int i = 4; do { c += n & 15; n = n >> 4; } while (--i); return c; } 2. 有1,2,....一直到 n 的无序数组,求排序算法,并且要求时间复杂度为 O(n),空间复杂度 O(1), 使用交换,而且一次只能交换两个数.(华为) #include int main() { int a[] = {10,6,9,5,2,8,4,7,1,3}; int len = sizeof(a) / sizeof(int); int temp; for(int i = 0; i < len; ) { temp = a[a[i] - 1]; a[a[i] - 1] = a[i]; a[i] = temp; if ( a[i] == i + 1) i++; } for (int j = 0; j < len; j++) coutnext; q->next=p; p=q; q=r; } head->next=NULL; head=p; return head; } 2 写出程序删除链表中的所有接点 void del_all(node *head) { node *p; while(head!=NULL) { p=head->next; free(head); head=p; } cout<<"释放空间成功!"< src ) { 282 d = (char *)dst + len - 1; 283 s = (char *)src + len - 1; 284 while ( len >= 4 ) { 285 *d-- = *s--; 286 *d-- = *s--; 287 *d-- = *s--; 288 *d-- = *s--; 289 len -= 4; 290 } 291 while ( len-- ) { 292 *d-- = *s--; 293 } 294 } else if ( dst < src ) { 295 d = (char *)dst; 296 s = (char *)src; 297 while ( len >= 4 ) { 298 *d++ = *s++; 299 *d++ = *s++; 300 *d++ = *s++; 301 *d++ = *s++; 302 len -= 4; 303 } 304 while ( len-- ) { 305 *d++ = *s++; 306 } 307 } 308 return dst; 309 } 公司考试这种题目主要考你编写的代码是否考虑到各种情况,是否安全(不会溢出) 各种情况包括: 1、参数是指针,检查指针是否有效 2、检查复制的源目标和目的地是否为同一个,若为同一个,则直接跳出 3、读写权限检查 4、安全检查,是否会溢出 memcpy 拷贝一块内存,内存的大小你告诉它 strcpy 是字符串拷贝,遇到'\0'结束 /* memcpy 拷贝不重叠的内存块 */ void memcpy(void* pvTo, void* pvFrom, size_t size) { void* pbTo = (byte*)pvTo; void* pbFrom = (byte*)pvFrom; ASSERT(pvTo != NULL && pvFrom != NULL); //检查输入指针的有效性 ASSERT(pbTo>=pbFrom+size || pbFrom>=pbTo+size);//检查两个指针指向的内存是否重叠 while(size-->0) *pbTo++ == *pbFrom++; return(pvTo); } 1. 华为面试题:怎么判断链表中是否有环? bool CircleInList(Link* pHead) { if(pHead = = NULL || pHead->next = = NULL)//无节点或只有一个节点并且无自环 return (false); 61 if(pHead->next = = pHead)//自环 return (true); Link *pTemp1 = pHead;//step 1 Link *pTemp = pHead->next;//step 2 while(pTemp != pTemp1 && pTemp != NULL && pTemp->next != NULL) { pTemp1 = pTemp1->next; pTemp = pTemp->next->next; } if(pTemp = = pTemp1) return (true); return (false); } 2. 两个字符串,s,t;把t字符串插入到 s 字符串中,s 字符串有足够的空间存放 t 字符串 void insert(char *s, char *t, int i) { memcpy(&s[strlen(t)+i],&s[i],strlen(s)-i); memcpy(&s[i],t,strlen(t)); s[strlen(s)+strlen(t)]='\0'; } 1.编写一个 C 函数,该函数在一个字符串中找到可能的最长的子字符串,且该字符串是由 同一字符组成的. char * search(char *cpSource, char ch) { char *cpTemp=NULL, *cpDest=NULL; int iTemp, iCount=0; while(*cpSource) { if(*cpSource == ch) { iTemp = 0; cpTemp = cpSource; while(*cpSource == ch) ++iTemp, ++cpSource; if(iTemp > iCount) iCount = iTemp, cpDest = cpTemp; if(!*cpSource) break; } ++cpSource; } return cpDest; } 2.请编写一个 C 函数,该函数在给定的内存区域搜索给定的字符,并返回该字符所在位置 索引值. int search(char *cpSource, int n, char ch) { int i; for(i=0; inext,并随后删除原 next 指向的节点. 4. #include void foo(int m, int n) { 62 printf("m=%d, n=%d\n", m, n); } int main() { int b = 3; foo(b+=3, ++b); printf("b=%d\n", b); return 0; } 输出:m=7,n=4,b=7(VC6.0) 这种方式和编译器中得函数调用关系相关即先后入栈顺序. 不过不同编译器得处理不同. 也 是因为 C 标准中对这种方式说明为未定义,所以各个编译器厂商都有自己得理解,所以最 后产生得结果完全不同.因为这样,所以遇见这种函数,我们首先要考虑我们得编译器会如 何处理这样得函数,其次看函数得调用方式,不同得调用方式,可能产生不同得结果.最后 是看编译器优化. 2.写一函数,实现删除字符串 str1 中含有的字符串 str2. 第二个就是利用一个 KMP 匹配算法找到 str2 然后删除(用链表实现的话,便捷于数组) /*雅虎笔试题(字符串操作) 1. 给定字符串 A 和B,输出 A 和B中的最大公共子串. 比如 A="aocdfe" B="pmcdfa" 则输出"cdf" */ //Author: azhen #include #include #include char *commanstring(char shortstring[], char longstring[]) { int i, j; char *substring=malloc(256); if(strstr(longstring, shortstring)!=NULL) //如果……,那么返回 shortstring return 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); else comman=commanstring(str1, str2); printf("the longest comman string is: %s\n", comman); } 63 11.写一个函数比较两个字符串 str1 和str2 的大小, 若相等返回 0, 若str1 大于 str2 返回 1, 若str1 小于 str2 返回-1 int 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 #define NUM 1000 int 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) { 64 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); 65 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; } 输入 N, 打印 N*N 矩阵 比如 N = 3,打印: 1 2 3 8 9 4 7 6 5 N = 4,打印: 1 2 3 4 12 13 14 5 11 16 15 6 10 9 8 7 解答: 1 #define N 15 int s[N][N]; void main() { int k = 0, i = 0, j = 0; int a = 1; for( ; k < (N+1)/2; k++ ) { while( j < N-k ) s[i][j++] = a++; i++; j--; while( i < N-k ) s[i++][j] = a++; i--; j--; while( j > k-1 ) s[i][j--] = a++; i--; j++; while( i > k ) s[i--][j] = a++; i++; j++; } for( i = 0; i < N; i++ ) { 66 for( j = 0; j < N; j++ ) cout << s[i][j] << '\t'; cout << endl; } } 2 define MAX_N 100 int matrix[MAX_N][MAX_N]; /* *(x,y) :第一个元素的坐标 * start:第一个元素的值 * n:矩阵的大小 */ void SetMatrix(int x, int y, int start, int n) { int i, j; if (n <= 0) //递归结束条件 return; if (n == 1) { //矩阵大小为 1 时matrix[x][y] = start; return; } for (i = x; i < x + n-1; i++) //矩阵上部 matrix[y][i] = start++; for (j = y; j < y + n-1; j++) //右部 matrix[j][x+n-1] = start++; for (i = x+n-1; i > x; i--) //底部 matrix[y+n-1][i] = start++; for (j = y+n-1; j > y; j--) //左部 matrix[j][x] = start++; SetMatrix(x+1, y+1, start, n-2); //递归 } void main() { int i, j; int n; scanf("%d", &n); SetMatrix(0, 0, 1, n); //打印螺旋矩阵 for(i = 0; i < n; i++) { for (j = 0; j < n; j++) printf("%4d", matrix[i][j]); printf("\n"); } } 4. 斐波拉契数列递归实现的方法如下: int Funct( int n ) { if(n==0) return 1; if(n==1) return 1; retrurn Funct(n-1) + Funct(n-2); } 请问,如何不使用递归,来实现上述函数? 请教各位高手! 67 解答: 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; } 2. 判断一个字符串是不是回文 int IsReverseStr(char *aStr) { int i,j; int found=1; if(aStr==NULL) return -1; j=strlen(aStr); for(i=0;i #include 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; } int main() 68 { int n, m; scanf("%d%d", &n, &m); printf("最后胜利的是%d 号!\n", Josephu(n, m)); system("pause"); return 0; } 链表实现: #include #include typedef struct Node { int index; struct Node *next; }JosephuNode; int Josephu(int n, int m) { int i, j; JosephuNode *head, *tail; head = tail = (JosephuNode *)malloc(sizeof(JosephuNode)); for (i = 1; i < n; ++i) { tail->index = i; tail->next = (JosephuNode *)malloc(sizeof(JosephuNode)); tail = tail->next; } tail->index = i; tail->next = head; for (i = 1; tail != head; ++i) { for (j = 1; j < m; ++j) { tail = head; head = head->next; } tail->next = head->next; printf("第%4d 个出局的人是:%4d 号\n", i, head->index); free(head); head = tail->next; } i = head->index; free(head); return i; } int main() { int n, m; scanf("%d%d", &n, &m); printf("最后胜利的是%d 号!\n", Josephu(n, m)); system("pause"); return 0; } 1. 已知 strcpy 函数的原型是: char * strcpy(char * strDest,const char * strSrc); 1.不调用库函数,实现 strcpy 函数. 2.解释为什么要返回 char *. 解说: 1.strcpy 的实现代码 char * strcpy(char * strDest,const char * strSrc) { if ((strDest==NULL)||(strSrc==NULL)) file://[/1] throw "Invalid argument(s)"; //[2] 69 char * strDestCopy=strDest; file://[/3] while ((*strDest++=*strSrc++)!='\0'); file://[/4] return strDestCopy; } 错误的做法: [1] (A)不检查指针的有效性,说明答题者不注重代码的健壮性. (B)检查指针的有效性时使用((!strDest)||(!strSrc))或(!(strDest&&strSrc)), 说明答题者对 C 语言中类型的隐式转换没有深刻认识. 在本例中 char *转换为 bool 即是类型隐式转换, 这种 功能虽然灵活,但更多的是导致出错概率增大和维护成本升高.所以 C++专门增加了 bool、 true、false 三个关键字以提供更安全的条件表达式. (C)检查指针的有效性时使用((strDest==0)||(strSrc==0)),说明答题者不知道使用常量的 好处.直接使用字面常量(如本例中的 0)会减少程序的可维护性.0 虽然简单,但程序中 可能出现很多处对指针的检查, 万一出现笔误, 编译器不能发现, 生成的程序内含逻辑错误, 很难排除.而使用 NULL 代替 0,如果出现拼写错误,编译器就会检查出来. [2] (A)return new string("Invalid argument(s)");,说明答题者根本不知道返回值的用途,并且 他对内存泄漏也没有警惕心. 从函数中返回函数体内分配的内存是十分危险的做法, 他把释 放内存的义务抛给不知情的调用者,绝大多数情况下,调用者不会释放内存,这导致内存泄 漏. (B)return 0;,说明答题者没有掌握异常机制.调用者有可能忘记检查返回值,调用者还 可能无法检查返回值(见后面的链式表达式) .妄想让返回值肩负返回正确值和异常值的双 重功能,其结果往往是两种功能都失效.应该以抛出异常来代替返回值,这样可以减轻调用 者的负担、使错误不会被忽略、增强程序的可维护性. [3] (A)忘记保存原始的 strDest 值,说明答题者逻辑思维不严密. [4] (A)循环写成 while (*strDest++=*strSrc++);,同[1](B). (B)循环写成 while (*strSrc!='\0') *strDest++=*strSrc++;,说明答题者对边界条件的检查 不力.循环体结束后,strDest 字符串的末尾没有正确地加上'\0'. 11. 堆栈溢出一般是由什么原因导致的? 没有回收垃圾资源 这个答案是错误.没有回收垃圾资源是内存泄漏,不是堆栈溢出.堆栈溢出一般是由于函数 的递归调用引起的. 1. 找出字符串 q 在字符串 p 中出现的次数 #include #include main() { char p[256]; char q[20]; int i; int n; n=0; memset(p,' ',256); memset(q,' ',20); scanf("%s",p); p[strlen(p)]='\0'; scanf("%s",q); q[strlen(q)]='\0'; for(i=0;i<=(strlen(p)-strlen(q));i++) if(memcmp(&p[i],q,strlen(q))==0) n++; printf("The time is %d\n",n); } 70 3、全局变量可不可以定义在可被多个.C 文件包含的头文件中?为什么? 答:可以,在不同的 C 文件中以 static 形式来声明同名全局变量. 可以在不同的 C 文件中声明同名的全局变量,前提是其中只能有一个 C 文件中对此变量赋 初值,此>时连接不会出错 12. 什么函数不能声明为虚函数? 构造函数 静态成员函数 非成员函数 部分模板函数 11. 堆栈溢出一般是由什么原因导致的? 堆栈溢出一般是由于没有对数组进行边界检查. 没有回收垃圾资源导致的是内存泄露最后内存耗尽. 堆栈溢出一般是循环的递归调用导致的, 如果使用的大数据结构的局部变量,也可能导致堆栈溢出. 1、static 全局变量与普通的全局变量有什么区别?static 局部变量和普通局部变量有什么区 别?static 函数与普通函数有什么区别? static 函数与普通函数有什么区别:static 函数在内存中只有一份,普通函数在每个被调 用中维持一份拷贝. 71
  • 下载地址 (推荐使用迅雷下载地址,速度快,支持断点续传)
  • 免费下载 PDF格式下载
  • 您可能感兴趣的
  • 应聘会计岗位笔试试题  护士应聘笔试试题  出纳应聘笔试试题  医学检验应聘笔试试题  美术老师应聘笔试试题  应聘机械设计笔试试题  应聘电气老师笔试试题  考车牌笔试模拟试题  驾校笔试模拟试题题库  考车牌笔试模拟试题c1