预处理篇

1 C/C++头文件中的 ifndef/define/endif 的作用是什么?

如果一个项目中存在两个C文件,而这两个C文件都 include (包含)了同一个头文件, 当编译时,这两个C文件要一同编译成一个可运行文件,可能会产生大量的声明冲突。所以要把头文件的内容都放在 #ifhdef 和 #endif 中,防止该头文件被重复引用。

2 #include <filename.h>和#include “filename.h” 有什么区別?

对于 #include <filename.h>,编译器先从标准库路径开始搜索 filename.h,然后才去用户的工作路径搜索,使得系统文件调用较快。

对于 #include “filename.h”,编译器先从用户的工作路径开始搜索 filename.h,然后才去标准库路径搜索,使得自定义文件调用较快。

3 头文件的作用有哪些?

头文件的作用主要表现为以下两个方面:

(1)通过头文件来调用库功能。出于对源代码保密的考虑,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口是怎么实现的。编译器会从库中提取相应的代码。

(2)头文件能加强类型安全检查。如果某个接口被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,能大大减轻程序员调试、改错的负担。

4 #define有哪些缺陷?

由于宏定义在预处理阶段进行,主要做的是字符替换工作,所以它存在着一些固有的缺陷:

(1)它无法进行类型检查。宏定义是在编译前进行字符的替换,因为还没编译,不能编译前就检查好类型是否匹配,而只能在编译时才知道,所以不具备类型检查功能。

(2)由于优先级的不同,使用宏定义时,可能会存在副作用。例如,执行加法操作的宏定义运算 #define ADD(a,b) a+b 在使用的过程中,对于表达式的运算就可能存在潜在的问题,而应该改成 #define ADD(a,b) ((a)+(b))

(3) 无法进行单步调试。

(4)会导致代码膨胀。由于宏定义是文本替换,需要对代码进行展开,相比较函数调用的 方式,会存在较多的冗余代码。

(5)在C++中,使用宏无法操作类的私有数据成员。

5 如何使用define声明一个常数,用以表明1年中有多少秒(忽略闰年间题)?

#define SECOND_PER_YEAR (365*24*60*60)UL

考虑到可能存在数据溢出问题,更加规范化的写法是使用长整型类型,即UL类型, 告诉编译器这个常数是长整型数。

6 不能使用大于、小于、if语句,如何定义一个宏来比较两个数a、b的大小?

如果只是进行简单的比较,则返回比较结果即可,宏定义可以写为如下方式:

#define check(a,b)   (((a)-(b))=fabs((a)-(b)))? “greater”: “smaller”

但如果需要返回较大的值,则宏定义可以写为:

#define MAX(a,b) (fabs((a)-(b))=((a)-(b))?(a):(b))

虽然 #define MAX(a,b) (abs((a)-(b))=((a)-(b))?(a):(b)) 也是一种比较好的做法,但是函数 abs() 接收的参数及其返回值都是整数,这样在传递实参时,其小数部分可能被截去,从而导致误差。例如,a=-12.345b=-24.1467abs((a)-(b)) 返回值为 12,但 (a)-(b) 显然不等于 12。

fabs() 所接受的参数及返回值都是 double 型的,这样无论它是接受整数还是接受 float 型的数据,都不会因精度问题而出现误差。

7 如何判断一个变量是有符号数还是无符号数?

采用取反操作。

对于这个变量分两种情况进行分析,一种情况是它为某种类型的值,另一种情况是它为某种类型。对于值而言,如果这个数以及其求反后的值都大于0,则该数为无符号数,反之则为有符号数,因为数据在计算机中都是以二进制的0或1存储的,正数以0开头,负数以1开头,求反操作符会把所有的1改为0,所有的0改为1。如果是有符号数,那么取反之后,开头的0会被改为1,开头的1会被改为0,开头为1时即表示该数为负数,如果是无符号数则不会受此影响。对于类型而言,也同样适用。

对于为值的情况,可以采用如下宏定义的方式:

#defme ISUNSIGNED(a) (a>=0 && ~a>=0)

对于为类型的情况:

#define ISUNSIGNED(type) ((type)O-l > 0)

程序示例代码如下:

#include <stdio.h>

#define ISUNSIGNED(a)	(a >= 0) && (〜a >= 0)
#define ISUNSIGNED_TYPE(type) ((type)O-l > 0) int main()
{
int a = 0;
unsigned int b = 0; printf("%d \n", ISUNSIGNED(a));
printf("%d \n", ISUNSIGNED(b));
printf("%d \n", ISUNSIGNED_TYPE(int));
printf("%d \n", ISUNSIGNED_TYPE(unsigned int)); return 0;
}

程序输出结果:

0
1
0
1

8 #define TRACE(S) (printf("%s\n",#S),S) 是什么意思?

# 进行宏字符串连接,在宏中把参数解释为字符串,不可以在语句中直接使用。在宏定义中printf("%s\n", #S)会被解释为printf("%s\n”,”S”)

程序示例如下:

#include <stdio.h>

#define TRACE(S) (printf("%s\n", #S),S) 

int main()
{
int a = 5;
TRACE(a); char *str = "hello";
char des[20];
strcpy(des,TRACE(str));
printf("des=%s\n",des); return 0;
}

程序输出结果:

a
str
hello

上例中,宏定义又是一个逗号表达式,所以复制到des里面的值为后面S也就是str的值。所以最后输出的就是字符串hello了。

9 不使用 sizeof,如何求变量占用的内存空间大小?

(1)使用宏定义的方式:

#include <stdio.h>

#define MY_SIZEOF(value) ( (char *)(&value+1) - (char *)&value )

int main()
{
int i;
double f;
double a[4];
double *q; printf("%d\n",MY_SIZEOF(i)); // 4
printf("%d\n",MY_SIZEOF(f)); // 8
printf("%d\n",MY_SIZEOF(a)); // 32
printf("%d\n",MY_SIZEOF(q)); // 4 return 0;
}

注意使用#define MY_SIZEOF(value) ( (&value+1) - &value ),返回的是 1(偏移单位长度),而不是 4。

(2)使用函数的方式:

#include <iostream>
using namespace std; template <class T>
int getSize(T *p)
{
return int(p+1)-int(p);
} int main()
{
int* i;
double* q;
char a[10]; printf("%d\n",getSize(i)); // 4
printf("%d\n",getSize(q)); // 8
printf("%d\n",getSize(&a)); // 10 return 0;
}

注意使用#return (p+1)-(p);,返回的是 1(偏移单位长度),而不是 4。

10 如何使用宏求结构体的内存偏移地址?

#define OFFSET(type,field) ( (size_t)&(((type *)0)->field) )

在 C 语言中,ANSI C 标准允许值为 0 的常量被强制转换成任何一种类型的指针,而且转换结果是一个空指针,即 NULL 指针,因此对 0 取指针的操作 ((type *)0)的结果就是一个类型为 type* 的NULL指针。但如果利用这个 NULL 指针来访问 type 的成员当然是非法的,但可以用来计算 field 字段的地址。

C 语言编译器根本就不生成访问 type 的代码,而仅仅是根据 type 的内容布局和结构体实例首址在编译期计算这个(常量)地址,这样就完全避免了通过 NULL 指针访问内存可能出现的问题。同时又因为地址为 0,所以这个地址的值就是字段相对于结构体基址的偏移。

程序示例如下:

#include <stdio.h>

#define OFFSET(type,field) ( (size_t)&(((type *)0)->field) )

struct MyStr
{
char a; // 4byte (由于下一个成员为4个字节的,为了字节对齐,补齐为4个字节)
int b; // 8byte
float c; // 16byte (由于下一个成员为8个字节的,为了字节对齐,补齐为8个字节)
double d; // 24byte
char e; // 32byte (要是最大长度的整数倍)
}; int main()
{
printf("%d\n",OffSet(MyStr,a)); // 0
printf("%d\n",OffSet(MyStr,b)); // 4
printf("%d\n",OffSet(MyStr,c)); // 8
printf("%d\n",OffSet(MyStr,d)); // 16
printf("%d\n",OffSet(MyStr,e)); // 24
printf("%d\n",sizeof(MyStr)); // 32 return 0;
}

上述方法避免了实例化一个type对象,而且求值在编译期进行,没有运行期负担,程序效率大大提高。

11 枚举和 define 有什么不同?

具体而言,两者的区别表现在以下几 个方面:

(1)枚举常量是实体中的一种,而宏定义不是实体。

(2)枚举常量属于常量,但宏定义不是常量。

(3)枚举常量具有类型,但宏没有类型,枚举变量具有与普通变量相同的性质,如作用域、值等,但是宏没有。

(4)宏常量是在预编译阶段进行简单替换,枚举常量则是在编译的时候确定其值。

(5)—般在编译器里,可以调试枚举常量,但是不能调试宏常量。

(6)枚举可以一次定义大量相关的常量,而#define宏一次只能定义一个。

12 typdef 和 define 有什么区别?

typedef 与 define 都是替一个对象取一个别名,以此来增强程序的可读性,但是它们在使用和作用上也存在着以下几个方面的不同:

(1)原理不同。#define 是 C 语言中定义的语法,它是预处理指令,在预处理时进行简单的字符串替换,不作类型检查,不管含义是否正确照样带入,只有在编译己被展开的源程序时才会发现可能的错误并报错。typedef 是关键字,它在编译时处理,所以typedef有类型检查的功能。

(2)功能不同。typedef 只是用来定义类型的别名,这些类型不只包含内部类型(int、char 等),还包括自定义类型(如 struct),可以起到使类型易于记忆的功能。#define 不只是可以为类型取别名,还可以定义常量、变量、编译开关等。

(3)作用域不同。#define 没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用,而 typedef 有自己的作用域。

void fun()
{
#define A int
} void gun()
{
// 在这里也可以使用A,因为宏替换没有作用域,但如果上面用的是typedef,那这里就不能用
// 不过一般不在函数内使用typedef
}

(4)对指针的操作不同。两者修饰指针类型时,作用不同。

#define INTPTR1 int*
typedef int* INTPTR2; INTPTR1 pl,p2;
INTPTR2 p3,p4;

INTPTR1 p1,p2;INTPTR2 p3,p4;这两句的效果截然不同的。前者进行字符串替换后变成int\* p1,p2;,要表达的意义是声明一个指针变量 p1 和一个整型变量 p2。而后者由于 INTPTR2 是具有含义的,告诉我们是一个指向整型数据的指针,那么 p3 和 p4 都为指针变量。

13 C++ 中宏定义与内联函数有什么区别?

两者的区别主要表现在以下几个方面:第一,宏定义是在预处理阶段进行代码替换,而内联函数是在编译阶段插入代码;第二,宏定义没有类型检查,而内联函数有类型检查。

14 不使用第三方变量,如何交换两个变量的值?

#include <iostream>

using namespace std;

#define swap1(x,y) x=x+y,y=x-y,x=x-y

#define swap2(x,y) x^=y,y^=x,x^=y

int main()
{
int a=1,b=2;
//方法1 - 算数法
swap1(a,b);
cout << "a=" << a << " b=" << b << endl; //方法2 - 异或法
a=1,b=2;
swap2(a,b);
cout << "a=" << a << " b=" << b << endl;
}

[笔试面试题] 3-C++关键字篇的更多相关文章

  1. 面试题2:BAT及各大互联网公司2014前端笔试面试题:HTML/CSS篇

    BAT及各大互联网公司2014前端笔试面试题:HTML/CSS篇 Html篇: 1.你做的页面在哪些流览器测试过?这些浏览器的内核分别是什么? IE: trident内核 Firefox:gecko内 ...

  2. BAT及各大互联网公司2014前端笔试面试题:HTML/CSS篇

    BAT及各大互联网公司2014前端笔试面试题:HTML/CSS篇 2014/08/03 · Web前端, 开发 · CSS, HTML, 技术面试 分享到: 188 MongoDB集群之分片技术应用 ...

  3. BAT及各大互联网公司2014前端笔试面试题--Html,Css篇

    很多面试题是我自己面试BAT亲身经历碰到的.整理分享出来希望更多的前端er共同进步吧,不仅适用于求职者,对于巩固复习前端基础更是大有裨益. 而更多的题目是我一路以来收集的,也有往年的,答案不确保一定正 ...

  4. (转)BAT及各大互联网公司2014前端笔试面试题--Html,Css篇

    BAT及各大互联网公司2014前端笔试面试题--Html,Css篇   很多面试题是我自己面试BAT亲身经历碰到的.整理分享出来希望更多的前端er共同进步吧,不仅适用于求职者,对于巩固复习前端基础更是 ...

  5. BAT及各大互联网公司2014前端笔试面试题--Html,Css篇(昨天有个群友表示写的简单了点,然后我无情的把他的抄了一遍)

    某个群友 http://www.cnblogs.com/coco1s/   很多面试题是我自己面试BAT亲身经历碰到的.整理分享出来希望更多的前端er共同进步吧,不仅适用于求职者,对于巩固复习前端基础 ...

  6. BAT及各大互联网公司2014前端笔试面试题--JavaScript篇

    很多面试题是我自己面试BAT亲身经历碰到的.整理分享出来希望更多的前端er共同进步吧,不仅适用于求职者,对于巩固复习js更是大有裨益. 而更多的题目是我一路以来收集的,也有往年的,答案不确保一定正确, ...

  7. 【转】BAT及各大互联网公司2014前端笔试面试题:JavaScript篇

    原文转自:http://blog.jobbole.com/78738/ 很多面试题是我自己面试BAT亲身经历碰到的.整理分享出来希望更多的前端er共同进步吧,不仅适用于求职者,对于巩固复习前端基础更是 ...

  8. BAT及各大互联网公司2014前端笔试面试题--JavaScript篇(昨天某个群友表示写的简单了点,然后我无情的把他的抄了一遍)

    (某个群友)http://www.cnblogs.com/coco1s/ 很多面试题是我自己面试BAT亲身经历碰到的.整理分享出来希望更多的前端er共同进步吧,不仅适用于求职者,对于巩固复习js更是大 ...

  9. 秒杀多线程第一篇 多线程笔试面试题汇总 ZZ 【多线程】

    http://blog.csdn.net/morewindows/article/details/7392749 系列前言 本系列是本人参加微软亚洲研究院,腾讯研究院,迅雷面试时整理的,另外也加入一些 ...

  10. 前端试题本(Javascript篇)

    JS1. 下面这个JS程序的输出是什么:JS2.下面的JS程序输出是什么:JS3.页面有一个按钮button id为 button1,通过原生的js如何禁用?JS4.页面有一个按钮button id为 ...

随机推荐

  1. 网络编程进阶:并发编程之协程、IO模型

    协程: 基于单线程实现并发,即只用一个主线程(此时可利用的CPU只有一个)情况下实现并发: 并发的本质:切换+保存状态 CPU正在运行一个任务,会在两种情况下切走去执行其他任务(切换有操作系统强制控制 ...

  2. bzoj1834 网络扩容 网络流

    好久没写题解了啊··· 题目大意: 给你一幅n个点的网络,先求出其1到n的最大流,每条弧还会有个属性\(cost_i\),表示没扩容一个单位的费用,现在我们要求的就是扩容K个单位的最小费用 思路: 这 ...

  3. [bzoj4300]绝世好题_二进制拆分

    绝世好题 bzoj-4300 题目大意:题目链接. 注释:略. 想法: 二进制拆分然后用一个数组单独存一下当前答案即可. Code: #include <iostream> #includ ...

  4. codevs——2750 心系南方灾区

    2750 心系南方灾区  时间限制: 1 s  空间限制: 2000 KB  题目等级 : 青铜 Bronze 题解  查看运行结果     题目描述 Description 现在我国南方正在承受百年 ...

  5. CCCC2017大区赛补完

    L2-2 多项式除法 这题看懂题意就是个模拟 L3-2 周游世界 想法是相邻点连边,然后跑最短路,当最短路相同时候,比较之前经过的换乘数,取最小的作为方案 但是这样只过了2个点……? 网上dalao们 ...

  6. LoadRunner脚本编写

    性能測试project师要懂代码么?答案是必须的.好多測试员觉得在loadrunner中编写脚本非常难非常牛X ,主要是大多測试人员并未做过开发工作,大学的那点程序基础也忘记的几乎相同了. 还有非计算 ...

  7. Mysql数据库再度使用

    查看数据库端口: show global variables like 'port'; 谨记:每一条sql结束的语句后都要接上分号. 在连接数据库时遇到过这种问题: Fatal error: Call ...

  8. cocos2d-x+lua开发模式下编辑器的选择

    原本打算直接用CocosIDE的,毕竟是官方出品,并且支持Android远程调试,windows下的调试也非常方便,调试的信息也非常全,智能提示也不错.好了,一切看上去非常完美,可是它有一个致命缺陷, ...

  9. JAVA编程思想(2) - 操作符(二)

    5. 直接常量 -一般来说,假设程序里使用了"直接常量",编译器能够准确的知道要生成什么样的类型.但有时候却是模棱两可的. 这时候须要我们对编译器进行适当的"指导&quo ...

  10. Android清单文件具体解释(四) ---- backupAgent的使用方法

    在<application>节点中有一个很重要的属性,那就是backupAgent.这里我们将它单独列出来,从基本含义,使用方法及其相关属性等方面来具体介绍一下. 1.backupAgen ...