‘壹’ 进入IT企业必读的200个 .NET面试题的目 录
第1章 应聘开发职位的技巧和禁忌 1
程序员在准备面试的过程中,有时会过分注重技术上的准备工作,事实上,一些非技术的准备工作也相当重要。掌握好应聘技术职位过程中的软技巧、准备一份出色的简历、提高警惕避免在应聘中触犯一些禁忌,可以大大地提高面试的成功率。在开始面试时,还要把握一些强势的招聘网站,给自己一个展现自我的平台。本章从了解、准备、开始,层层渐进,让读者对整个程序员面试的过程有个全局性的掌握。
1.1 技术职位需要怎样的人才——了解大环境 1
1.1.1 对技术的执着和热情 1
1.1.2 对编程始终抱有认真的态度 1
1.1.3 实事求是的态度和谦逊的品质 1
1.1.4 适合应聘公司的文化 1
1.2 一份出色的个人简历——面试准备 2
1.2.1 一份简历不宜超过一页 2
1.2.2 永远准备中文简历 2
1.2.3 不要在个人简历上注明希望薪水 2
1.2.4 简历模板 2
1.3 展现自我——开始发简历 3
1.3.1 应聘渠道 3
1.3.2 应聘流程 7
1.4 真正的面试——开始面试 8
1.4.1 笔试 8
1.4.2 面试 10
1.4.3 电话面试 10
1.4.4 网络考试 10
1.5 面试中的一些禁忌 11
1.6 小结 12
第2章 .NET框架基础 13
本章覆盖了.NET面试笔试中常见的.NET框架技术题。此类题目侧重于考查应聘者对于.NET机制的深入了解,彻底理解.NET的运行机制,并且熟悉一个.NET系统常用的管理部署方法。
2.1 .NET基础概念 13
2.1.1 什么是CTS、CLS和CLR 13
2.1.2 开发和运行.NET程序需要的
最基本环境是什么 15
2.1.3 .NET是否支持多编程语言开发 15
2.1.4 CLR技术和COM技术的比较 17
2.1.5 什么是程序集和应用程序域 18
2.2 .NET运行机制 20
2.2.1 .NET程序被编译成什么形式的代码 20
2.2.2 JIT是如何工作的 22
2.2.3 简述程序集的加载机制 23
2.2.4 如何配置程序集的版本策略 25
2.3 生成、部署和管理 27
2.3.1 如何生成强签名的程序集 27
2.3.2 如何把程序集放入GAC中 29
2.3.3 延迟签名及其作用 30
2.3.4 程序集的版本分哪几部分 32
2.4 名企面试真题 32
2.5 小结 33
第3章 .NET类型语法基础 34
本章覆盖了.NET面试笔试中最基础的语法和类型题。纵观.NET的面试题,此类题目涉及了最基础的知识点,其难度也相对最小。但是应聘者如果对此类的面试题回答得不正确或者不完整,将会给面试官留下技术水平较差的印象,建议读者对本章的题目做到深刻理解和掌握。
3.1 基础类型和语法 34
3.1.1 .NET中所有内建类型的基类是什么 34
3.1.2 System.Object中包含哪些方法,哪些是虚方法 35
3.1.3 值类型和引用类型的区别 37
3.1.4 简述装箱和拆箱原理 40
3.1.5 C#中是否有全局变量 43
3.1.6 struct和class的区别,
struct适用哪些场合 43
3.1.7 类型的初始化器何时被调用 44
3.1.8 C#中方法的参数可以有哪几种传递方式 47
3.1.9 C#中string和String有什么区别 50
3.1.10 .NET支持哪几种可访问性级别,C#实现了其中的哪几种 50
3.1.11 简述属性的特点及属性和方法的异同 51
3.1.12 简述C#中的浅复制和深复制 54
3.1.13 简述C#中的循环语法和各自的特点 57
3.1.14 C#中的using语句有什么作用 60
3.2 内存管理和垃圾回收 62
3.2.1 简述.NET中堆栈和堆的特点和差异 62
3.2.2 执行string abc=aaa+bbb+ccc共分配了多少内存 64
3.2.3 .NET中GC的运行机制 66
3.2.4 Dispose方法和Finalize方法在何时被调用 67
3.2.5 GC中代(Generation)是什么,一共分几代 70
3.2.6 GC机制中如何判断一个对象是否仍在被使用 71
3.2.7 .NET的托管堆中是否可能出现内存泄漏现象 72
3.3 面向对象的实现 75
3.3.1 C#中类可以有多个父类、可以实现多个接口吗 75
3.3.2 简述C#中重写、重载和隐藏的概念 76
3.3.3 为什么在构造方法中调用虚方法会导致问题 78
3.3.4 在C#中如何声明一个类不能被继承 82
3.4 异常的处理 82
3.4.1 如何针对不同的异常进行捕捉 82
3.4.2 如何使用Conditional特性 84
3.4.3 如何避免类型转换时的异常 86
3.5 名企面试真题 88
3.6 小结 89
第4章 字符串、集合和流的使用 90
字符串、集合和流在程序中处理数据时经常被用到,这些代码的编写将直接影响到系统的正确性和效率。本章将包含关于字符串、集合和流的常见面试题,并且通过分析这些题目和知识点,帮助读者梳理这些方面的知识。
4.1 字符串处理 90
4.1.1 System.String是值类型还是引用类型 90
4.1.2 StringBuilder类型有何作用 91
4.1.3 如何在String和Byte[]对象之间进行转换 92
4.1.4 简述BASE64编码的作用以及C#中对其的支持 94
4.1.5 SecureString的实例如何被分配和释放 96
4.1.6 什么是字符串池机制 98
4.2 常用集合和泛型 99
4.2.1 Int[]是引用类型还是值类型 99
4.2.2 数组之间如何进行转换 100
4.2.3 解释泛型的基本原理 102
4.2.4 什么是泛型的主要约束和次要约束 104
4.2.5 .NET中是否可用标准模板库(STL) 105
4.3 流和序列化 106
4.3.1 什么是流,.NET中有哪些常见的流 106
4.3.2 如何使用压缩流 109
4.3.3 Serializable特性有何作用 111
4.3.4 .NET提供了哪几种可进行序列化操作的类型 113
4.3.5 如何自定义序列化和反序
列化的过程 116
4.4 名企面试真题 119
4.5 小结 119
第5章 常用类和接口 120
.NET除了提供运行引擎之外,还提供了丰富的内建类型。理解这些类型的作用和机制,能够帮助程序员减少代码工作,编写高效简洁的代码。而有时候误用类型,则会导致性能的降低,更严重时则会为系统带了潜伏的bug。本章将介绍一些经常出现在.NET面试中的类型和接口。
5.1 类型的基类System.Object 120
5.1.1 是否存在不继承自System.Object类型的类 120
5.1.2 在System.Object中定义的三个比较方法有何异同 122
5.1.3 如何重写GetHashCode方法 125
5.2 时间的操作System.DateTime 127
5.2.1 DateTime如何存储时间 127
5.2.2 如何在DateTime对象和
字符串对象之间进行转换 127
5.2.3 什么是UTC时间,如何转换到UTC时间 130
5.3 IFormattable和IformatProvider的使用 131
5.3.1 如何使用IFormattable接口实现格式化输出 131
5.3.2 如何告诉类型格式化输出的方式 133
5.4 管理文件和文件夹的类型 135
5.4.1 如何操作文件和文件夹 135
5.4.2 如何实现文件和文件夹的监控功能 139
5.5 .NET中的定时器 141
5.5.1 .NET提供了哪几个定时器类型 141
5.5.2 .NET的内建定时器类型
是否会发生回调方法重入 146
5.6 名企面试真题 151
5.7 小结 151
第6章 .NET中的高级特性 152
本章的内容覆盖了诸如委托、事件、反射和特性等.NET框架中的高级特性。对这些特性的掌握和成熟运用,往往成为.NET程序员从入门级进阶到中级的判断标准。也正因为如此,此类题目在.NET技术笔试、面试中被大量采用。读者在阅读本章时,应力求做到知其然更知其所以然,充分理解各种特性在.NET框架下是如何实现的,这样的设计如何提高了程序的灵活性和可扩展性。
6.1 委托 152
6.1.1 请解释委托的基本原理 152
6.1.2 委托回调静态方法和实例方法有何区别 154
6.1.3 什么是链式委托 154
6.1.4 链式委托的执行顺序是怎么样的 156
6.1.5 可否定义拥有返回值的方法的委托链 157
6.1.6 委托通常可以应用在哪些场合 159
6.2 事件 165
6.2.1 请解释事件的基本使用方法 165
6.2.2 事件和委托有何联系 167
6.2.3 如何设计一个带有很多事件的类型 169
6.2.4 用代码表示如下情景:猫叫、老鼠逃跑、主人惊醒 173
6.3 反射 175
6.3.1 请解释反射的基本原理和其实现的基石 176
6.3.2 .NET提供了哪些类型来实现反射 179
6.3.3 如何实现动态地发射程序集 184
6.3.4 如何利用反射来实现工厂模式 188
6.3.5 如何以较小的内存代价保存
Type、Field和Method信息 194
6.4 特性 196
6.4.1 什么是特性,如何自定义一个特性 196
6.4.2 .NET中特性可以在哪些元素上使用 198
6.4.3 有哪几种方法可以获知一个元素是否申明某个特性 200
6.4.4 一个元素是否可以重复申明同一个特性 202
6.5 名企面试真题 204
6.6 小结 204
第7章 .NET多线程编程 205
多线程编程是每个技术框架下都需要面对的问题,在多CPU、多核的硬件架构逐渐普及的今天,多线程编程也渐渐变得更加重要。本章将集中覆盖关于.NET中多线程编程的面试题。
7.1 多线程编程的基本概念 205
7.1.1 请解释操作系统层面上的线程和进程 205
7.1.2 多线程程序在操作系统里是并行执行的吗 206
7.1.3 什么是纤程 207
7.2 .NET中的多线程编程 208
7.2.1 如何在.NET程序中手动控制多个线程 208
7.2.2 如何使用.NET的线程池 212
7.2.3 如何查看和设置线程池的上下限 215
7.2.4 如何定义线程独享的全局数据 217
7.2.5 如何使用异步模式读取一个文件 221
7.2.6 如何阻止线程执行上下文的传递 223
7.3 多线程程序的线程同步 227
7.3.1 什么是同步块和同步块索引 227
7.3.2 C#中的lock关键字有何作用 229
7.3.3 可否使用值类型对象来
实现线程同步 232
7.3.4 可否对引用类型对象自身进行同步 233
7.3.5 什么是互斥体,Mutex类型和Monitor类型的功能有何区别 235
7.4 名企面试真题 238
7.5 小结 238
第8章 ASP NET应用开发 239
ASP NET是微软公司提供的编写动态网站的技术框架,其特点是基于.NET框架基础,所有ASP NET程序都可以使用针对.NET的语言编写。在微软公司的Visual Studio开发平台中,实现了拖放控件等便捷的功能,使得ASP NET应用程序的开发效率得到了较大的提高,近些年来ASP NET技术逐渐成为网站开发的主流技术之一,本章将覆盖一些常见的关于ASP NET开发的面试题。
8.1 ASP NET应用开发基础 239
8.1.1 请解释ASP NET以什么形式运行 239
8.1.2 常见的HTTP Code有哪些 242
8.1.3 GET请求和POST请求有何区别 245
8.1.4 介绍ASP NET的页面生存周期 247
8.2 控件和页面 249
8.2.1 什么是静态页面,什么是动态页面 250
8.2.2 请简述ViewState的功能和实现机制 251
8.2.3 Session有哪几种存储方式,之间有何区别,如何进行设置 255
8.2.4 如何嵌套使用GridView控件 259
8.2.5 列举几种实现页面跳转的方法,并说明其实现机制 263
8.2.6 请解释<%# Eval(source)%>的功能和实现机制 270
8.2.7 ObjectDataSource控件有何作用 273
8.3 验证和安全 277
8.3.1 如何使用正则表达式来验证一个
上海市电话号码 277
8.3.2 介绍ASP NET验证控件的功能和
使用方法 280
8.3.3 如何防止SQL注入式攻击 287
8.4 名企面试真题 289
8.5 小结 289
第9章 .NET中的数据库开发 290
大部分系统都会包含数据库应用。数据库应用设计往往成为系统设计中最重要的组成之一,这其中不止包括数据库的架构、库结构的设计,也包括了程序访问数据库策略的设计。在.NET的程序开发中,ADO NET已经成为访问数据库最主要的组件框架。本章将覆盖和数据库访问及ADO NET有关的常见面试题,具体会覆盖ADO NET基本概念、数据库的链接、数据库读写等主题。
9.1 ADO NET和数据库程序基础 290
9.1.1 什么是关系型数据库 290
9.1.2 如何通过SQL语句来实现行列转换 291
9.1.3 ADO NET支持哪几种数据源 293
9.2 ADO NET和数据库的连接 295
9.2.1 请简要叙述数据库连接池的机制 295
9.2.2 如何提高连接池内连接的重用率 298
9.2.3 一个连接字符串可以包含哪些属性 300
9.2.4 CommandBehavior.CloseConnection有何作用 302
9.3 使用ADO NET读写数据库 305
9.3.1 ADO NET支持哪两种方式来访问关系数据库 305
9.3.2 什么是强类型的DataSet 309
9.3.3 请解释SqlDataAdapter的
基本工作机制 312
9.3.4 如何自动生成SqlDataAdapter的
更新命令 316
9.3.5 如何实现批量更新的功能 319
9.4 名企面试真题 321
9.5 小结 321
第10章 XML的应用和处理 322
XML可算是近10年来最炙手可热的技术之一,由于其跨平台的特性,很多技术应用都选择基于XML来进行发展。在.NET中,对XML的支持和应用随处可见。例如配置文件的格式、数据结构的表示、Web Service应用等,都是以XML语法为基础的。本章将详细覆盖常见的关于XML本身及其在.NET中应用的面试题。
10.1 XML的基本特性 322
10.1.1 什么是XML 322
10.1.2 简述XML的常用领域及其优势 323
10.1.3 XML中<![CDATA[ ]]>标签的作用 324
10.1.4 XML规范是否允许空的属性值 325
10.1.5 XML中如何处理诸如“<”的字符 326
10.1.6 XML中的命名空间如何使用 328
10.2 使用.NET组件读写XML 330
10.2.1 .NET中操作XML的基本类型有哪些 330
10.2.2 如何使用XmlDocument类型操作XML文档的节点和属性 334
10.2.3 如何使用XPath来指向带有属性的节点 337
10.2.4 .NET中如何验证一个XML文档的格式 338
10.2.5 .NET中XML文档和关系模式如何转换 340
10.3 利用XSLT处理XML文档 344
10.3.1 什么是XSLT,XSLT有何作用 344
10.3.2 如何使用XSLT中的模板 346
10.3.3 如何在XSLT文档中调用
其他XSLT文档 349
10.3.4 如何在代码中使用XSLT文档 351
10.4 名企面试真题 353
10.5 小结 353
第11章 Web Service的开发与应用 354
Web Service是一种网络服务,形式非常类似于当前智能手机上的应用。通过通用的规范,Web Service技术允许使用者访问网络上每一个Web Service所提供的服务。在网络快速发展的今天,这种基于网络的分布式服务已经被广泛地应用。本章将讨论关于.NET中如何应用Web Service的面试题。
11.1 SOAP和Web Service的基础概念 354
11.1.1 请简述SOAP协议 354
11.1.2 什么是WSDL,它有何作用 356
11.1.3 Web Service中如何处理附件 357
11.2 使用.NET开发Web Service 360
11.2.1 如何在.NET中创建Web Service 360
11.2.2 WebMethod特性包含哪些属性,各有何用处 363
11.2.3 如何生成Web Service代理类型 367
11.2.4 请简述.NET中Web Service的异常机制 368
11.3 Web Service的安全机制 371
11.3.1 请简要介绍WS-Security的签名机制 371
11.3.2 WS-Security规范申明了哪几种身份验证的方法 373
11.4 名企面试真题 375
11.5 小结 375
第12章 .NET Remoting分布式应用开发 376
在企业级应用开发中,分布式开发占据了越来越重要的地位。.NET Remoting是一种可扩展性很高的分布式开发技术,相对于DCOM、CORBA、RMI等分布式开发技术而言,.NET Remoting拥有着众多独特的优势。.NET Remoting是一个庞大的技术话题,如果详细展开的话可能要占据一本书的篇幅。本章主要针对那些经常出现在.NET面试中的、与Remoting基础相关的面试题。
12.1 .NET Remoting框架基础 376
12.1.1 请简要介绍.NET Remoting的运行机制 376
12.1.2 请列举.NET Remoting机制中有哪些组件可以扩展替换 379
12.1.3 请简述.NET Remoting生存周期机制 384
12.2 使用.NET Remoting进行分布式应用开发 387
12.2.1 请介绍服务端激活模式和客户端激活模式的区别 387
12.2.2 请简述Remoting中有哪几种远程调用方式 390
12.2.3 Remoting机制中如何处理以ObjRef为参数的方法调用 393
12.2.4 请简述Remoting中配置文件的使用 397
12.2.5 如何在客户端和服务器端共享远程对象类型 400
12.3 名企面试真题 404
12.4 小结 404
第13章 代码和算法 405
无论是面试还是笔试,算法和代码的问题都是必不可少的,其区别仅在于笔试中更侧重于应聘者书写代码的能力,而面试中则更注重于应聘者的设计能力和算法思路。本章着重覆盖了一些在.NET面试中经常出现的和代码、算法有关的面试题,并且给出了解答思路和实现示例。
13.1 基础算法题 405
13.1.1 请实现一个快速排序算法 405
13.1.2 请实现一个二分查找算法 406
13.1.3 请实现一棵二叉树的中序、后序遍历 408
13.1.4 请写出一个奇偶分割算法 413
13.1.5 请实现一个简单的最短路径算法 414
13.2 程序设计题 423
13.2.1 请编程实现斐波拉契数列问题 423
13.2.2 请设计窗口程序演示八皇后问题 425
13.3 名企面试真题 432
13.4 小结 432
第14章 .NET中的单元测试 433
单元测试是软件开发中必不可少的一个环节,单元测试的优劣直接影响到集成测试、系统测试的效果,甚至会影响到最终产品的质量。大多数开发团队对单元测试非常重视,并且要求程序员掌握相应的知识。本章将覆盖在.NET面试中经常出现的关于单元测试的面试题。
14.1 单元测试基础概念 433
14.1.1 请简述单元测试的作用和其优点 433
14.1.2 请举例说明TDD开发方式的流程 434
14.1.3 请编写实现阶乘功能模块的测试用例 437
14.2 使用NUNIT进行单元测试 439
14.2.1 如何使用NUNIT来进行单元测试 439
14.2.2 如何对NUNIT的测试用例进行分类 442
14.2.3 请解释SetUp、TearDown、TestFixtureSetUp和
TestFixtureTearDown 446
14.3 名企面试真题 448
14.4 小结 448
‘贰’ java基础面试题有哪些
下面是10道java基础面试题,后附答案
1.什么是 Java 虚拟机?为什么 Java 被称作是“平台无关的编程语言”?
Java 虚拟机是一个可以执行 Java 字节码的虚拟机进程。Java 源文件被编译成能被 Java 虚拟机执行的字节码文件。
Java 被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。Java 虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。
2.“static”关键字是什么意思?Java 中是否可以覆盖(override)一个 private 或者是static 的方法?
“static”关键字表明一个成员变量或者是成员方法可以在没有所属的类的实例变量的情况下被访问。
Java 中 static 方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而 static 方法是编译时静态绑定的。static 方法跟类的任何实例都不相关,所以概念上不适用。
3.JDK 和 JRE 的区别是什么?
Java 运行时环境(JRE)是将要执行 Java 程序的 Java 虚拟机。它同时也包含了执行 applet 需要的浏览器插件。Java 开发工具包 (JDK)是完整的 Java 软件开发包,包含了 JRE,编译器和其他的工具(比如:JavaDoc,Java 调试器),可以让开发者开发、编译、执行 Java 应用程序。
4.是否可以在 static 环境中访问非 static 变量?
static 变量在 Java 中是属于类的,它在所有的实例中的值是一样的。当类被 Java 虚拟机载入的时候,会对 static 变量进行初始化。如果你的代码尝试不用实例来访问非 static 的变量,编译器会报错,因为这些变量还没有被创建出来,还没有跟任何实例关联上。
5.Java 支持的数据类型有哪些?什么是自动拆装箱?
Java 语言支持的 8 中基本数据类型是:
byte
short
int
long
float
double
boolean
char
自动装箱是 Java 编译器在基本数据类型和对应的对象包装类型之间做的一个转化。比如:把 int 转化成 Integer,double 转化成 double,等等。反之就是自动拆箱。
6.Java 支持多继承么?
不支持,Java 不支持多继承。每个类都只能继承一个类,但是可以实现多个接口。
7.Java 中,什么是构造函数?什么是构造函数重载?什么是复制构造函数?
当新对象被创建的时候,构造函数会被调用。每一个类都有构造函数。在程序员没有给类提供构造函数的情况下,Java 编译器会为这个类创建一个默认的构造函数。
Java 中构造函数重载和方法重载很相似。可以为一个类创建多个构造函数。每一个构造函数必须有它自己唯一的参数列表。
Java 不支持像 C++中那样的复制构造函数,这个不同点是因为如果你不自己写构造函数的情况下,Java 不会创建默认的复制构造函数。
8.Java 中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思?
Java 中的方法重载发生在同一个类里面两个或者是多个方法的方法名相同但是参数不同的情况。与此相对,方法覆盖是说子类重新定义了父类的方法。方法覆盖必须有相同的方法名,参数列表和返回类型。覆盖者可能不会限制它所覆盖的方法的访问。
9.接口和抽象类的区别是什么?
Java 提供和支持创建抽象类和接口。它们的实现有共同点,不同点在于:
接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。
类可以实现很多个接口,但是只能继承一个抽象类
类如果要实现一个接口,它必须要实现接口声明的所有方法。但是,类可以不实现抽象类声明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。
抽象类可以在不提供接口方法实现的情况下实现接口。
Java 接口中声明的变量默认都是 final 的。抽象类可以包含非 final 的变量。
Java 接口中的成员函数默认是 public 的。抽象类的成员函数可以是 private, protected 或者是 public。
接口是绝对抽象的,不可以被实例化。抽象类也不可以被实例化,但是,如果它包含 main 方法的话是可以被调用的。
10.什么是值传递和引用传递?
对象被值传递,意味着传递了对象的一个副本。因此,就算是改变了对象副本,也不会影响源对象的值。
对象被引用传递,意味着传递的并不是实际的对象,而是对象的引用。因此,外部对引用对象所做的改变会反映到所有的对象上。
最后祝你面试顺利!
‘叁’ c++经典面试题及答案
1. C++的类和C里面的struct有什么区别?
struct成员默认访问权限为public,而class成员默认访问权限为private
2. 析构函数和虚函数的用法和作用
析构函数是在对象生存期结束时自动调用的函数,用来释放在构造函数分配的内存。
虚函数是指被关键字virtual说明的函数,作用是使用C++语言的多态特性
3. 全局变量和局部变量有什么区别?是怎么实现的?操作系统和编译器是怎么知道的?
1) 全局变量的作用用这个程序块,而局部变量作用于当前函数
2) 前者在内存中分配在全局数据区,后者分配在栈区
3) 生命周期不同:全局变量随主程序创建和创建,随主程序销毁而销毁,局部变量在局部函数内部,甚至局部循环体等内部存在,退出就不存在
4) 使用方式不同:通过声明后全局变量程序的各个部分都可以用到,局部变量只能在局部使用
4. 有N个大小不等的自然数(1–N),请将它们由小到大排序.要求程序算法:时间复杂度为O(n),空间复杂度为O(1)。
void sort(int e[], int n)
{
int i;
int t;
for (i=1; i {
t = e[e[i]];
e[e[i]] = e[i];
e[i] = t;
}
}
5. 堆与栈的去区别
A. 申请方式不同
Stack由系统自动分配,而heap需要程序员自己申请,并指明大小。
B. 申请后系统的响应不同
Stack:只要栈的剩余空间大于申请空间,系统就为程序提供内存,否则将抛出栈溢出异常
Heap:当系统收到程序申请时,先遍历操作系统中记录空闲内存地址的链表,寻找第一个大于所申请空间的堆结点,然后将该结点从空间结点链表中删 除,并将该结点的空间分配给程序。另外,大多数系统还会在这块内存空间中的首地址处记录本次分配的大小,以便于delete语句正确释放空间。而且,由于 找到的堆结点的大小不一定正好等于申请的大小,系统会自动将多余的那部分重新放入空闲链表。
C. 申请大小限制的不同
Stack:在windows下,栈的大小是2M(也可能是1M它是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
Heap:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
D. 申请效率的比较:
栈由系统自动分配,速度较快。但程序员是无法控制的。
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。
E. 堆和栈中的存储内容
栈:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器 中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开 始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的.头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。
6. 含参数的宏与函数的优缺点
宏: 优点:在预处理阶段完成,不占用编译时间,同时,省去了函数调用的开销,运行效率高
缺点:不进行类型检查,多次宏替换会导致代码体积变大,而且由于宏本质上是字符串替换,故可能会由于一些参数的副作用导致得出错误的结果。
函数: 优点:没有带参数宏可能导致的副作用,进行类型检查,计算的正确性更有保证。
缺点:函数调用需要参数、返回地址等的入栈、出栈开销,效率没有带参数宏高
PS:宏与内联函数的区别
内联函数和宏都是在程序出现的地方展开,内联函数不是通过函数调用实现的,是在调用该函数的程序处将它展开(在编译期间完成的);宏同样是;
不同的是:内联函数可以在编译期间完成诸如类型检测,语句是否正确等编译功能;宏就不具有这样的功能,而且宏展开的时间和内联函数也是不同的(在运行期间展开)
7. Windows程序的入口是哪里?写出Windows消息机制的流程
Windows程序的入口是WinMain()函数。
Windows应用程序消息处理机制:
A. 操作系统接收应用程序的窗口消息,将消息投递到该应用程序的消息队列中
B. 应用程序在消息循环中调用GetMessage函数从消息队列中取出一条一条的消息,取出消息后,应用程序可以对消息进行一些预处理。
C. 应用程序调用DispatchMessage,将消息回传给操作系统。
D. 系统利用WNDCLASS结构体的lpfnWndProc成员保存的窗口过程函数的指针调用窗口过程,对消息进行处理。
8. 如何定义和实现一个类的成员函数为回调函数
A.什么是回调函数?
简而言之,回调函数就是被调用者回头调用调用者的函数。
使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己的一个函数(这个函数为回调函数)的地址作为参数传递给那个被调用函数。而该被调用函数在需要的时候,利用传递的地址调用回调函数。
回调函数,就是由你自己写的,你需要调用另外一个函数,而这个函数的其中一个参数,就是你的这个回调函数名。这样,系统在必要的时候,就会调用你写的回调函数,这样你就可以在回调函数里完成你要做的事。
B.如何定义和实现一个类的成员函数为回调函数
要定义和实现一个类的成员函数为回调函数需要做三件事:
a.声明;
b.定义;
c.设置触发条件,就是在你的函数中把你的回调函数名作为一个参数,以便系统调用
如:
一、声明回调函数类型
typedef void (*FunPtr)(void);
二、定义回调函数
class A
{
public:
A();
static void callBackFun(void) //回调函数,必须声明为static
{
cout<<"callBackFun"<
}
virtual ~A();
};
三、设置触发条件
void Funtype(FunPtr p)
{
p();
}
void main(void)
{
Funtype(A::callBackFun);
}
C. 回调函数与API函数
回调和API非常接近,他们的共性都是跨层调用的函数。但区别是API是低层提供给高层的调用,一般这个函数对高层都是已知的;而回调正好相反, 他是高层提供给底层的调用,对于低层他是未知的,必须由高层进行安装,这个安装函数其实就是一个低层提供的API,安装后低层不知道这个回调的名字,但它 通过一个函数指针来保存这个回调函数,在需要调用时,只需引用这个函数指针和相关的参数指针。
其实:回调就是该函数写在高层,低层通过一个函数指针保存这个函数,在某个事件的触发下,低层通过该函数指针调用高层那个函数。
‘肆’ C++面试题汇总
某个文件中定义的静态全局变量(或称静态局部变量)作用域是------本文件内
①:默认继承权限:
class的继承按照private继承处理,struct的继承按照public继承处理
②:成员的默认访问权限
class的成员默认是private权限, struct默认是public权限
注:C++有内置的宏__cplusplus -------有个习惯带“__”表示内部变量,只供内部使用;不带双下划线的,表示外部接口的变量(标识符)
C++函数的三种传递方式为:值传递。指针传递 和 引用传递
注:值传递和指针传递,本质上就是指针传递。
在A类中fun1是虚函数;B类中fun2是虚函数。
①:机制上:c是面向过程的(c也可以是面向对象发的程序); C++是面向对象,提供了类。C++的面向对象的程序比c容易。
②:使用方向:c适合代码体积小的,效率高的场合,如嵌入式;C++更适合上层的,复杂的;Linux核心大部分是c写的,因为他是系统软件,效率要求极高
③:C++是c的超集;
④:C语言是结构化编程语言,C++是面向对象编程语言。
⑤:C++侧重于对象而不是过程,侧重于类的设计而不是逻辑设计。
C中struct主要提供的是自定义类型,和构造一种新的类型出来;
一致的地方:
不一致的地方:
C语言: 无Protection行为; 不能定义函数,但可以有函数指针;
C++: 有Procetion行为,默认是private; 可以定义函数。
注: 就是访问权限,struct对于外部是完全访问的,C++是有访问 权限 设置的;
正确, sizeof 是编译时运算符,编译时就确定了 可以看成是和及其有关的常量
注:定义数组的时候,数组的长度必须是一个确定的常量;
形参:是在定义函数时指定的参数,在未调用时他们并不占用内存中的存储单元。只有在调用的时候才会被分配内存,调用结束后,形参所占用的内存单元会被释放
实参:即你调用函数时传递的参数;
重载: 同一个名字空间--- -函数名相同,参数列表不同 ; 注释:理解成一个类里面的多个同名函数
重写/覆盖: 不同名字空间-----用于继承,子类重新定义父类中 函数名相同,参数列表也相同 虚函数 的方法
重定义/隐藏:重定义(隐藏)是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
a 如果派生类的函数和基类的 函数同名,但是参数不同 ,此时,不管有无virtual,基类的函数被隐藏。
b 如果派生类的函数与基类 的函数同名,并且参数也相同 ,但是基类函数没有vitual关键字,此时,基类的函数被隐藏。
①: 隐藏 实现 细节 ,使得代码能够模块化;扩展代码模块, 实现代码重写
②: 接口重用 :为了使用多个派生类中某个派生类的属性正确调用
用sizeof的方法:
定义一个指针P,打印出sizeof(P),如果结果是4,怎么标识改操作系统是32位,如果打印结果是2,则标识是16位、。
虚函数 表 ,是在 编译 期就建立了。各个虚函数被组织成一个虚函数的入口地址的数组(简而言之,就是组成了一个存放虚函数地址的数组)
虚函数表 指针 是在 运行 时建立的,也就是构造函数被调用时进行初始化的。
封装,继承,多态 是什么?怎那么用?为什么使用它?
封装:将客观事物抽象成类,每个类对自身的 数据 和 方法 实行 protection ; 注释 : 保护内部成员
继承:广义的继承有三种实现形式:
实现继承:指使用基类的属性和方法,而无需额外编码的能力;
可视继承:子窗体使用父窗体的外观和实现代码
接口继承:仅使用属性和方法,实现之后到子类实现
前两种和后一种构成了功能复用的两种方法
多态: 主要是为了抽象
只要是函数都会做类型检查。
这是内联函数跟宏观比的优势。
①:静态存储区域分配; 内存在编译的时候就已经分配好了,这块内存在程序的整个运行期间都存在。例如全局变量。
②:在展区创建;在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时,自动被释放。效率高,但是内存容量有限。
③:从堆上分配:或者叫:动态内存分配。程序员自己负责在何时用free或delete释放内存。
C语言中用带参数的宏定义,C++中用inline
生命周期不同 空间 周期
局部变量 函数调用时创建,结束时销毁。static除外
局部变量不具有外部链接,全局变量
全局变量 : 静态数据区
局部变量: 放在栈区
malloc、free是C++/C语言标准库,new、delete是C++运算符。
注意:new、delete不是库函数;
malloc/free 无法 满足 对象在创建的时候要自动 执行 构造函 数,对象消亡之前要自动执行 析构函数 。他们是库函数,而不是运算符,不在编译器的控制权限内,。
new、delete 能完成内存的分配和释放,已经初始化和清理工作。
判断指针是否为空,如果空,则打印错误log,并且return,终止本函数。
不是,两个不同类型的指针可以强制转换。
动态申请;
知道运行时才知道一个对象需要多少存储空间,不需要知道对象的生存周期有多长。
Debug调试版本,它包含调试信息,比如assert的适用,并且不作任何优化,便于程序员调试程序。
Release称为发布版本,他往往时进行了各种优化,
析构函数时特殊的类成员函数,没有返回类型,没有参数,不能随意调用,也没有重载,只有在类对象的生命周期结束时,有系统自己调用。优势方内存空间的作用。
虚函数是C++多态的一种表现,使用虚函数,我们可以灵活的进行动态绑定,当然是以一定的开销为代价
(这里虚函数的适用还是不太懂,需要进一步学习,比如怎么调用子类的一切啊)
导致文件描述符结构中指针指向的内存背重复释放,进而导致一些不可预期的异常。
比如全局变量的初始化,就不是有main函数引起的。例如:
全局对象的构造函数,会在main函数之前执行。
多态,纯虚函数,抽象类
内联函数
虚函数的特点:如果希望派生类能够重新定义基类的方法,则在基类中将该方法定义为虚方法,这样可以启用动态联编。
内联函数的特点:使用内联函数的目的屎我了提高函数的运行侠侣。内联函数的代码不能过长,因为内联函数省去调用函数的时间是以代码膨胀为代价的。内联函数不能包含循环语句。因为执行循环语句要比调用函数的开销大。
函数模板的实例化是由编译程序在处理函数嗲用时自动完成的,
类模板的实例化必须由程序员在程序中显示的指定
函数名和参数列表
不能被重载的运算符:
①:不能改变C++内部数据类型(如int float 等)的运算符
②:不能重载“.”,因为.在类中对任何成员都有意义,已经成为标准用法
③:不能重载目前C++运算符集合中没有的符号,如:@, 等。愿意:一是难以理解,二是无法确定优先级
④:对已经存在的运算符重载不能改变优先级规则,否则将引起混乱。
有可能是派生类无法调用析构函数
模板可以说比较古老了,但是当前的泛型编程实质上就是模板编程。他体现了一种通用和泛化的思想。
STL有7中容器:
vector(零食进行存储数据的访问),list(经常进行数据的增删改查),deque(队列结构),map,multimap,set(构造栈形的数据使用),multiset.
容器是一种特定用途的类;
浅拷贝 知识拷贝了指针没有拷贝资源
深拷贝进行了资源的拷贝
三元表达式“?:”问好后面的两个操作数必须为同一个类型。否则会出问题。
总的来说,堆是C语言和操作系统的术语,是操作系统维护的一块动态分配内存;自由存储是C++中通过 new与delete动态分配和释放对象的抽象概念。 他们并不是完全一样。
从技术上来说,堆(heap)是C语言和操作系统的术语。堆是操作系统所维护的一块特殊内存,它提供了动态分配的功能,当运行程序调用malloc()时就会从中分配,稍后调用free可把内存交还。而自由存储是C++中通过new和delete动态分配和释放对象的抽象概念,通过new来申请的内存区域可称为自由存储区。基本上,所有的C++编译器默认使用堆来实现自由存储,也即是缺省的全局运算符new和delete也许会按照malloc和free的方式来被实现,这时借由new运算符分配的对象,说它在堆上也对,说它在自由存储区上也正确。
程序编译的过程中就是将用户的文本形式的源代码(c/c++)转化成计算机可以直接执行的机器代码的过程。主要经过四个过程:预处理、编译、汇编和链接。具体示例如下。
一个hello.c的c语言程序如下。
其编译过程如下:
‘伍’ 2018年的JAVA面试题及答案
Java知识点很多,每个知识点都可能会有面试题,而且不同的企业的考察点是不一样的。下面给你整理了几个Java面试题可以参考:
1、面向对象的特征有哪些方面?
封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。
多态性:多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。多态性分为编译时的多态性和运行时的多态性。方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。
2、获得一个类的类对象有哪些方式?
答:
-方法1:类型.class,例如:String.class
-方法2:对象.getClass(),例如:"hello".getClass()
-方法3:Class.forName(),例如:Class.forName("java.lang.String")
3、如何通过反射创建对象?
答:
-方法1:通过类对象调用newInstance()方法,例如:String.class.newInstance()
-方法2:通过类对象的getConstructor()或getDeclaredConstructor()方法获得构造器
(Constructor)对象并调用其newInstance()方法创建对象,例如:
String.class.getConstructor(String.class).newInstance("Hello");
‘陆’ C/C++经典面试题
C/C++经典面试题
面试题 1:变量的声明和定义有什么区别
为变量分配地址和存储空间的称为定义,不分配地址的称为声明。一个变量可以在多个地方声明,
但是只在一个地方定义。加入 extern修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分
定义。
说明:很多时候一个变量,只是声明不分配内存空间,直到具体使用时才初始化,分配内存空间,
如外部变量。
面试题 2:写出 bool 、int、 float、指针变量与“零值”比较的 if 语句
bool型数据:
if( flag )
{
A;
}
else
{
B;
}
int型数据:
if( 0 != flag )
{
A;
}
else
{
B;
}
指针型数:
if( NULL == flag )
{
A;
}
else
{
B;
}
float型数据:
if ( ( flag >= NORM ) && ( flag <= NORM ) )
{
A;
2
}
注意:应特别注意在 int、指针型变量和“零值”比较的时候,把“零值”放在左边,这样当把“==”
误写成“=”时,编译器可以报错,否则这种逻辑错误不容易发现,并且可能导致很严重的后果。
面试题 3:sizeof 和strlen的区别
sizeof和 strlen 有以下区别:
sizeof是一个操作符,strlen是库函数。
sizeof的参数可以是数据的类型,也可以是变量,而 strlen只能以结尾为‘ ‘的字符串作参数。
编译器在编译时就计算出了 sizeof 的结果。而 strlen 函数必须在运行时才能计算出来。并且 sizeof
计算的是数据类型占内存的大小,而 strlen计算的是字符串实际的长度。
数组做sizeof的参数不退化,传递给strlen就退化为指针了。
注意:有些是操作符看起来像是函数,而有些函数名看起来又像操作符,这类容易混淆的名称一定
要加以区分,否则遇到数组名这类特殊数据类型作参数时就很容易出错。最容易混淆为函数的操作符就
是 sizeof。
面试题 4:C 语言的关键字 static 和 C++ 的关键字 static 有什么区别
在C 中static 用来修饰局部静态变量和外部静态变量、函数。而 C++中除了上述功能外,还用来定
义类的成员变量和函数。即静态成员和静态成员函数。
注意:编程时 static的记忆性,和全局性的特点可以让在不同时期调用的函数进行通信,传递信息,
而 C++的静态成员则可以在多个对象实例间进行通信,传递信息。
面试题 5:C中的 malloc 和C++中的 new有什么区别
malloc和 new有以下不同:
(1)new、 是操作符,可以重载,只能在 C++中使用。
(2)malloc、free是函数,可以覆盖,C、C++中都可以使用。
(3)new 可以调用对象的构造函数,对应的 调用相应的析构函数。
(4)malloc仅仅分配内存,free 仅仅回收内存,并不执行构造和析构函数
(5)new、 返回的是某种数据类型指针,malloc、free 返回的是void指针。
注意:malloc申请的内存空间要用 free释放,而 new申请的内存空间要用 释放,不要混用。
因为两者实现的机理不同。
面试题 6:写一个“标准”宏 MIN
#define min(a,b)((a)<=(b)?(a):(b))
注意:在调用时一定要注意这个宏定义的副作用,如下调用:
((++*p)<=(x)?(++*p):(x)。
p指针就自加了两次,违背了 MIN的本意。
面试题 7:一个指针可以是 volatile 吗
可以,因为指针和普通变量一样,有时也有变化程序的不可控性。常见例:子中断服务子程序修改
一个指向一个 buffer的指针时,必须用 volatile来修饰这个指针。
说明:指针是一种普通的变量,从访问上没有什么不同于其他变量的特性。其保存的数值是个整型
数据,和整型变量不同的是,这个整型数据指向的是一段内存地址。
面试题 8:a 和&a 有什么区别
请写出以下代码的打印结果,主要目的是考察 a和&a的区别。
#include
void main( void )
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf(“%d,%d”,*(a+1),*(ptr-1));
return;
}
输出结果:2,5。
注意:数组名 a可以作数组的首地址,而&a是数组的指针。思考,将原式的 int *ptr=(int *)(&a+1);
改为 int *ptr=(int *)(a+1);时输出结果将是什么呢?
面试题 9:简述 C、C++程序编译的.内存分配情况
C、C++中内存分配方式可以分为三种:
(1)从静态存储区域分配:
内存在程序编译时就已经分配好,这块内存在程序的整个运行期间都存在。速度快、不容易出错,
因为有系统会善后。例如全局变量,static变量等。
(2)在栈上分配:
在执行函数时,函数内局部变量的存储单元都在栈上创建,函数执行结束时这些存储单元自动被释
放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3)从堆上分配:
即动态内存分配。程序在运行的时候用 malloc 或 new 申请任意大小的内存,程序员自己负责在何
时用free 或 释放内存。动态内存的生存期由程序员决定,使用非常灵活。如果在堆上分配了空间,
就有责任回收它,否则运行的程序会出现内存泄漏,另外频繁地分配和释放不同大小的堆空间将会产生
堆内碎块。
一个C、C++程序编译时内存分为 5大存储区:堆区、栈区、全局区、文字常量区、程序代码区。
4
面试题 10:简述 strcpy、sprintf 与 memcpy的区别
三者主要有以下不同之处:
(1)操作对象不同,strcpy的两个操作对象均为字符串,sprintf的操作源对象可以是多种数据类型,
目的操作对象是字符串, memcpy 的两个对象就是两个任意可操作的内存地址,并不限于何种数据类型。
(2)执行效率不同,memcpy最高,strcpy次之,sprintf的效率最低。
(3)实现功能不同,strcpy主要实现字符串变量间的拷贝,sprintf 主要实现其他数据类型格式到字
符串的转化,memcpy主要是内存块间的拷贝。
说明:strcpy、sprintf 与memcpy都可以实现拷贝的功能,但是针对的对象不同,根据实际需求,来
选择合适的函数实现拷贝功能。
‘柒’ 嵌入式开发—C语言面试题
嵌入式开发—C语言面试题
随着医疗电子、智能家居、物流管理和电力控制等方面的不断风靡,嵌入式系统利用自身积累的底蕴经验,重视和把握这个机会,想办法在已经成熟的平台和产品基础上与应用传感单元的结合,扩展物联和感知的支持能力,发掘某种领域物联网应用。下面是关于嵌入式开发—C语言面试题,希望大家认真阅读!
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) : ))
这个测试是为下面的目的而设的:
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(;;) { }
这个实现方式让我为难,因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案,我将用这个作为一个机会去探究他们这样做的
基本原理。如果他们的基本答案是:“我被教着这样做,但从没有想到过为什么。”这会给我留下一个坏印象。
第三个方案是用 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是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思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;
} 一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数,这也是可以接受的。我希望看到几个要点:说明常数、|=和&=~操作。
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。
如果你知道答案,或猜出正确答案,做得好。如果你不知道答案,我也不把这个当作问题。我发现这个问题的最大好处是:这是一个关于代码编写风格,代码的可读性,代码的可修改性的好的话题
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, 5
10, 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 Systems
isco 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.
;