C/C++面试常见问题总结
1、对sizeof的理解
① sizeof不是一个函数,是C语言中的一个操作符,关键字。
② 字节的计算是在程序编译时就完成了,而不是在程序运行时才计算,不会涉及到对内存空间的访问。
int iArray[10]={0};
int *ptr=NULL;
cout<<sizeof(iArray)<<"\n"; //40
cout<<sizeof(iArray[10])<<"\n"; //4
cout<<sizeof(iArray[1])<<"\n"; //4
cout<<sizeof(ptr)<<"\n"; //4
cout<<sizeof(*ptr)<<"\n"; //4
注意:① sizeof计算任何类型的指针,在32位系统上,都是4个字节。② sizeof计算结构体对齐方式时,要考虑内存对齐的问题,内存对齐的规则是,按照结构体中所占字节最大的类型进行对齐。例子如下:
//sizeof 计算指针
char* pstr = "0123456789";
int* pInt = (int *)malloc(100);
char str[] = "0123456789";
cout<<sizeof(pstr)<<endl; // 4
cout<<sizeof(pInt)<<endl; // 4
cout<<sizeof(str)<<endl; // 11
//sizeof 计算结构体
struct A{
short a;
short b;
short c;
};
struct B{
A obA;
int b;
};
cout<<sizeof(A)<<endl; // 6
cout<<sizeof(B)<<endl; // 12
注意:字符数组在传递过程中会退化成指针,计算sizeof的时候也是4,如下:
char var[10];
void myfun(char var[]){
cout<<sizeof(var)<<endl;//输出是4,而不是10
}
注意:一个空类占1个字节,单一继承也占1个字节,多重继承还是占一个字节,虚继承涉及虚表(虚指针),所以占4个字节。
class A{};
class A1{};
class B: public A{};
class C: public A, public A1{};
class D: virtual B{};
int main()
{
cout<<sizeof(A)<<endl;//空类 1
cout<<sizeof(B)<<endl;//继承空类 1
cout<<sizeof(C)<<endl;//多继承 1
cout<<sizeof(D)<<endl;//虚继承 4
return 0;
}
2、对static的理解
从static变量的存放区域、初始化、作用域、生存周期的角度分析。
① 作用域:如下面代码,在counter函数中。
② 存放区域:存放在数据段。
③ 生存周期:生存周期为程序开始到程序结束。
注意:static int count = 0;相当于定义一个变量,只会执行一次。
static主要作用是什么?
① 定义一个静态变量。
② 定义一个静态函数,被定义的函数的作用域限定在当前的文件模块中。
③ 在C++中,类的成员定义时,包括静态成员变量和静态成员函数。此时该静态变量不属于类的对象,而属于类,对它的访问通过类的作用域进行,如A::mCount,该定义提供了一个类对象之间共享变量的方法。静态成员函数也只属于类,而不属于类的对象,只能访问类的静态成员变量,不能访问对象的其他数据,属于类的全局函数。
int counter(int i)
{
static int count = 0;
count = count+i;
return count;
}
int main()
{
for (int i=0; i<=5; i++)
{
int j=counter(i);
printf("i:%d,j:%d\n",i,j);
}
return 0;
}
//i:0,1,2,3,4,5
//j:0,1,3,6,10,15
3、对volatile关键字的理解(适用于嵌入式开发或者高并发应用服务器的开发)
volatile的用途是什么呢?volatile表示变量是易变的,其要求编译器每次直接读取原始的内存而不去读取编译优化后的结果。volatile的应用场景,
① 硬件寄存器数据访问时,如果这个寄存器的数据有可能被硬件修改,一定要使用volatile来定义变量。
② 中断服务程序中一些非自动变量的定义。
③ 在多线程中被几个线程共享的数据。
其中,setjmp用于设置一个跳转栈位置,longjmp执行跳转到最近的一个setjmp位置,并使用相应的环境变量。
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
static jmp_buf buf;
int main()
{
int a;
volatile int b;
a = 2;
b = 3;
if (setjmp(buf) !=0)
{
printf("a:%d,b:%d\n",a,b);
exit(0);
}
a = 5;
b = 5;
longjmp(buf,1);
return 0;
}
4、关于数据类型转换的面试题目
(1) double和float的精度分别是?float变量与0比较(32位平台)
float: 4BYTE, 1bit符号位+8bit的指数位+23bit的有效数值。
double:8BYTE,1bit符号位+11bit的指数位+52bit的有效数值位。
float一定有符号,即没有unsigned float和signed float,表示的范围由指数为决定,有效数值位由有效数值位决定。
float 23bit: 2^23转换成十进制最多有7位有效位,但只能保证6位有效位。
double 52bit:转换成十进制有15-16个有效精度。if(float f !=0)是不可行的,比较方式应该是
#define EPSINON 0.00001
float f;
if ((f >= -EPSINON) && (f <= EPSINON) ){}
5、++运算符
++a,先执行++(执行到下一个操作符可以执行时),再执行其他操作。
a++,先执行其他操作,(在整个语句执行后)再执行++
注意:以下代码的实验环境为VC6.0
int i = 3;
int j = (++i)+(++i)+(++i);
int k = (i++)+(i++)+(i++);
printf("i=%d,j=%d,k=%d\n",i,j,k);
//i=9,j=16,k=18
(++i)+(++i)+(++i)的执行过程:(++i);(++i);+;(++i);+
(i++)+(i++)+(i++)的执行过程:+;+;+;i++;i++;i++
int a=5,b=7,c;
c = a+++b;
printf("a=%d,b=%d,c=%d\n",a,b,c);
//a=6,b=7,c=12
int ii=3;
int jj;
jj = sizeof(++ii+ ++ii);
printf("ii=%d,jj=%d\n",ii,jj);
// ii=3,jj=4
sizeof(++ii+ ++ii),这里面就是考察队sizeof的理解,由于sizeof是在编译阶段就完成了,而不是在运行时执行,所以++ii不会被执行。即sizeof求的是对应类型的大小,而不执行其内的表达式。
6、逻辑运算符的短路问题
int i = 1;
int j = 0;
if ((i++>0) || (++j>0)){;}
printf("i=%d,j=%d\n",i,j);
//i=2,j=0
&&:前一个条件为假,后面的语句不会被执行。
||:前一个条件为真,后面的语句不会被执行。
7、对const关键字的理解
const关键字的作用?
const是一个类型修饰符,常见的数据类型修饰符有,short,long,unsigned,signed,static,auto,extern,register。
定义一个变量: 类型描述符 变量名;(类型描述符包括 类型修饰符以及数据类型)
① 类型描述符如果有多个关键字,它们出现的位置不影响它们对变量的限制,如int short i和short int i是等价的。
const int a;
int const a;//二者相同,都描述一个const的整型变量,只读。
const int *a;
int* const a;
int const* const a;
② 对指针变量类型的理解?从右向左理解指针
I. []()*在数据定义时与在表达式中优先级一样,例如:
int *a[10],因为[]的优先级高于*,因此是一个数组,每个成员类型为指针,指向int。
int (*a)[],因为()优先级高于[],因此先看()里面,是一个指针,指向的类型为一个数组。
II. 指针的判断是从右向左的
const int *a,a是变量名,*表示a是一个指针,剩下的是指向的类型,因此 a是一个指向const int的指针,即指向的数据不可修改,可理解为const int 修饰的是(*a)。
int * const a,*const一起修饰a,即a只读,a是一个只读的指针,指向的类型为int,a指向的数据是可以被修改的。
int const* const a,表示是指针,a的内容不可修改,a指向的数据内容也不可修改。
③ typedef和#define的区别
typedef void *VP,typedef用于定义一种数据类型,而#define只是简单的宏替换。VP表示类型的名称,void* 表示真实的类型,此时void*是一个整体。
const VP ptr,表示const void* ptr,或者 VP const ptr,即void* const ptr
const void *ptr和void* const ptr的存储位置不同。
④ const的作用
a,const向其他用户传递一种信息,该变量不可修改。
b,有可能让编译器产生更精简的代码。
c,合理的保护只读数据,避免不必要的错误。
d,可以定义常量防止被修改,修饰函数传递的参数,在C++中修饰类的成员函数。
注意:① C++中 使用const替代#define,原因有二,const可以对变量进行类型限定,而#define只是简单的替换不能进行类型限定;在有些编译器中,可以对const常量进行调试,而任何编译器都不能对#define进行调试。② C++中,若将const添加在成员函数之前,表示成员函数返回值应该是常量。③ C++中,若将const添加在成员函数之后,表示成员函数不能对成员变量进行修改或者调用非const成员函数。若必须使用const成员函数修改成员变量,需在成员变量前添加mutable关键字。
参考
1
http://edu.51cto.com/lesson/id-30060.html
2 《程序员面试宝典》(第四版)
2 《程序员面试宝典》(第四版)