1.下面C++代码输出结果是什么?
int i=1;
void main()
{
int i=i;
}
答案:main()里的i是一个未定义的值。
2.下面程序的结果是多少?
#include<iostream>
using namespace std;
int main()
{
int x=2,y,z;
x*=(y=z=5);cout<<x<<endl;
z=3;
x==(y=z);cout<<x<<endl;
x=(y==z);cout<<x<<endl;
x=(y&z);cout<<x<<endl;
x=(y&&z);cout<<x<<endl;
y=4;
x=(y|z);cout<<x<<endl;
x=(y||z);cout<<x<<endl;
return 0;
}
答案:10 10 1 3 1 7 1
表达式 | 详细解释 | x值 |
x*=(y=z=5) | 5赋值给z,z再赋值给y,x=x*y | 10 |
x==(y=z) | z赋值给y,然后再看想,x,y是否相等。不管相等与否x值不变 | 10 |
x=(y==z) | 首先判断y、z是否相等,相等则返回一个布尔值1 | 1 |
x=(y&z) | y、z按位与运算 | 3 |
x=(y&&z) | &&是指如果y为真,z为真,则(y&&z)为真,返回一个布尔值1 | 1 |
x=(y|z) | 按位或运算 | 7 |
x=(y||z) | ||表示有一个为真,则y||z为真,返回一个布尔值1 | 1 |
&与运算
y | 0 | 0 | 1 | 1 |
z | 0 | 0 | 1 | 1 |
y&z | 0 | 0 | 1 | 1 |
|或运算
y | 0 | 1 | 0 | 0 |
z | 0 | 0 | 1 | 1 |
y|z | 0 | 1 | 1 | 1 |
^异或运算
y | 1 | 1 | 0 | 0 |
z | 1 | 0 | 0 | 1 |
y^z | 0 | 1 | 0 | 1 |
移位运算>> <<
<<1 | 左移一位表示乘以2 |
>>1 | 右移一位表示除以2 |
3.以下代码的结果是多少?
#include <iostream>
using namespace std;
int func(int x)
{
int count=0;
while(x)
{
count++;
x=x&(x-1);
}
return count;
}
int main()
{
count<<func(9999)<<endl;
return 0;
}
答案:8
详细解析:func函数返回值是形参x转换成二进制后包含1的个数,9999的二进制是10011100001111
拓展题:用一个表达式,判断一个数x是否是2^N次方(2,4,8……),不可用循环语句
解析:2、4、6、8转化成二进制是10、100、1000、10000如果x减1后与x做与运算,答案若是0,则是2^N次方
答案:!(x&(x-1))
4.关于sizeof的一系列题目
4.1下面代码的输出结果是什么?(一维数组、结构体、指针)
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
struct{
short a1;
short a2;
short a3;
}A;
struct{
long a1;
short a2;
}B;
int main()
{
char* ss1="0123456789";
char ss2[]="0123456789";
char ss3[100]="0123456789";
int ss4[100];
char q1[]="abc";
char q2[]="a\n";
char* q3="a\n";
char *str1=(char *)malloc(100);
void *str2=(void *)malloc(100);
cout<<sizeof(ss1);
cout<<sizeof(ss2);
cout<<sizeof(ss3);
cout<<sizeof(ss4);
cout<<sizeof(q1);
cout<<sizeof(q2);
cout<<sizeof(q3);
cout<<sizeof(A);
cout<<sizeof(B);
cout<<sizeof(str1);
cout<<sizeof(str2);
return 0;
}
答案:4,11,100,400,4,3,4,6,8,4,4
xxx | 详细解释 | sizeof(xxx) |
ss1 | 字符指针,指针大小就是一个定值,就是4字节 | 4 |
ss2 | 最初未定大小的字符数组,10个字节再加上隐含的"\0"(10+1) | 11 |
ss3 | 字符数组开始预分配100,大小1*100 | 100 |
ss4 | 整形数组开始预分配100,大小4*100 | 400 |
q1 | 类似ss2 3+1 | 4 |
q2 | 同上 | 3 |
q3 | 字符指针就是定值为4 | 4 |
A | 关于结构大小参看博客2 | 6 |
B | 同上 | 8 |
str1 | 字符指针就是定值为4 | 4 |
str2 | 字符指针就是定值为4 | 4 |
4.2关于sizeof和strlen差异的题目。
(1)char ss[100]="0123456789";
sizeof(ss)=1*100
strlen(ss)=10;//它的内部实现是用一个循环计算字符串的长度,直到"\0"为止。
(2)int ss[100]="0123456789";
sizeof(ss)=4*100
strlen(ss)报错,注意strlen的参数只能是char*
4.3下面代码输出结果是多少?
char var[10];
int test(char var[])
{
return sizeof(var);
};
A:10B:9C:11D:4
4.4(1)int **a[3][4]数组占据多大空间?(二维数组、unsigned、自定义类型)答案:D
解析:因为var[]等价于*var,已经退化成一个指针了,大小为4
拓展:数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址,如fun(char[8])、fun(char [])都等价于fun(char *);;对函数使用sizeof,在编译阶段会被函数返回类型取代如:
int f1(){return 0;}
cout<<sizeof(f1())<<endl;//f1返回值为int,因此被认为是int
sizeof(a)=3*4*4=48
(2) unsigned影响只是最高位bit的意义(正负),数据长度不会被改变的。所以:
sizeof(unsigned int)==sizeof(int)
(3)自定义类型的sizeof取值等同于它的类型原型
typedef short WORD;
sizeof(short)==sizeof(WORD)
4.5找出下面程序的错误,并解释它为什么是错的?
#include<iostream>
#include<string>
using namespace std;
int main(int argc,char *argv[])
{
//为输出"TrendMicroSoftUSCN"
string strArr1[]={"Trend","Micro","Soft"};
string *pStrArr1=new string[2];
`pStrArr1[0]="US";
pStrArr1[1]="CN";
for(int i=0;i<sizeof(strArr1)/sizeof(string);i++)
cout<<strArr1[i];
for(int j=0;j<sizeof(pStrArr1)/sizeof(string);j++)
cout<<pStrArr1[j];
return 0;
}
程序运行后输出:TrendMicroSoftUS
原因:sizeof(strArr1)=12;sizeof(pStrArr1)=4即指针大小当然不能正常输出;
应该为:for(int j=0;j<sizeof(pStrArr1)*2/sizeof(string);j++)
4.6类、继承、虚函数等中的sizeof问题
(1)写出下面sizeof的答案。
#include<iostream>
#include<complex>
using namespace std;
class Base
{
public:
Base(){cout<<"Base-ctor"<<endl;}
~Base(){cout<<"Base-dtor"<<endl;}
virtual void f(int){cout<<"Base::f(int)"<<endl;}
virtual void f(double){cout<<"Base::f(double)"<<endl;}
virtual void g(int i=10){cout<<"Base::g()"<<i<<endl;}
void g2(int i=10){cout<<"Base::g2()"<<i<<endl;}
};
class Derived:public Base
{
public:
Derived(){cout<<"Derived-ctor"<<endl;}
~Derived(){cout<<"Derived-dtor"<<endl;}
void f(complex<double>){cout<<"Derived::f(complex)"<<endl;}
virtual void g(int i=20){cout<<"Derived::g()"<<i<<endl;}
};
int main()
{
Base b;
Derived d;
Base* pb=new Derived;
cout<<sizeof(Base)<<endl;//答案:4
cout<<sizeof(Derived)<<endl;//答案:4
}
(2)一个空类占多少空间?多重继承的空类呢?
#include<iostream>
#include<memery.h>
#include<assert.h>
using namespace std;
class A
{
};
class A2
{
};
class B:public A
{
};
class C:public virtual B
{
};
class D:public A,public A2
{
};
int main()
{
cout<<sizeof(A)<<endl;//1
cout<<sizeof(B)<<endl;//1
cout<<sizeof(C)<<endl;//4
cout<<sizeof(D)<<endl;//1
}
空类所占空间是1,单一继承1,多重继承1,虚函数涉及虚指针大小为4
5.下面两段代码输出结果有什么不同?
第1段:
#include<iostream>
using namespace std;
int main()
{
int a,x;
for(a=0,x=0;a<=1&&!x++;a++)
{
a++;
}
cout<<a<<x<<endl;
return 0;
}
第2段:
#include<iostream>
using namespace std;
int main()
{
int a,x;
for(a=0,x=0;a<=1&&!x++;)
{
a++;
}
cout<<a<<x<<endl;
return 0;
}
第1段输出结果:21
第2段输出结果:12
详细解析:
执行 | 第1段代码 | 第2段代码 |
第一步 | 初始化定义a=0,x=0 | 初始化定义a=0,x=0 |
第二步 | a小于等于1,x的非为1,符合循环条件 | a小于等于1,x的非为1,符合循环条件 |
第三步 | x++后x自增为1 | x++后x自增为1 |
第四步 | 进去循环体,a++,a自增为1 | 进去循环体,a++,a自增为1 |
第五步 | 执行for(a=0,x=0;a<=1&&!x++;a++)中a++,a自增为2 | a现在是1符合小于等于1条件,x现在是1,x的非为0,不执行循环体,但x++依然执行自增为2 |
第六步 | a现在是2,已经不符合小于等于1条件了“&&”后的“!x++”不执行x还是1不执行循环体 | 打印12 |
6.面试笔试中两个数a和b的问题
(1)有两个变量a和b,不用“if”,"?:","switch"或其他判断语句,找出两个数中比较大的数。
答案:int max=((a+b)+abs(a-b))/2
(2)将a和b的值进行交换,并且不使用任何中间变量。
答案:a=a^b;
b=a^b;
a=a^b;
亦可以这么做不过会有a=a+b超界问题:
a=a+b;
b=a-b;
a=a-b;
7.笔试面试中需要注意的一些常识性问题
(1).在C++程序中调用C编译器编译后函数,为什么要加extern “C”?
答:C++提供了C连接交换指定符号extern “C”解决了名字匹配问题
原因:C++支持函数重载,C不支持函数重载。函数被C++编译后在库中名字与C语言不同。假设某个函数原型为
void foo(int x,int y),该函数被C编译器编译后在库中名字为_foo,而在C++编译器中编译则会产 生像_foo_int_int之类的名字
(2).头文件中的ifndef/define/endif是干什么用的?
答:防止头文件被重复引用。
(3)*(ptr++)+=123;等价于*ptr=*ptr+123;ptr++;
(4)const那些事儿:
可以用const修饰对象、数据成员和成员函数;常数据成员只能由构造函数由初始化列表对其进行初始化;常对象只能调用类的常成员函数,不能调用非常成员函数;
const有什么用途?
答案:(1)可以定义const变量(2)const可以修饰函数参数和返回值,甚至函数定义体。被const修饰的东西都受到了强制保护,可以预防意外变动,能提高程序健壮性。
注意:const常量赋值时必需同时初始化。const double di=10.0;//正确
const double di;//错误没有初始化
拓展:const与#define相比有什么不同
C++程序中只使用const常量而不使用宏常量C迫使程序员在预处理里使用#define,前者比后者有更多的优点:(1)const有数据类型,而宏常量没有数据类型(2)有的调试工具可以对const常量进行调试,但不能对宏常量进行调试。另外,在const成员函数中,用mutable修饰成员变量名后,就可以修改类的成员变量了。
(5)内联函数和宏的差别是什么?
答案:内联函数和普通函数相比可以加快程序的运行速度,因为不需要中断调用,在编译的时候内联函数可以直接被镶嵌到目标代码中。而宏只是一个简单地替换。
拓展:inline函数一般只用于如下情况:
(1)一个函数不断被重复调用
(2)函数只有简单地几行,且函数内不包含for、while、switch语句
(6)指针和引用的差异。
答案:1.非空区别。声明一个引用,但引用不能为空,必须同时初始化,不可以使用指向空值引用,因此代码效率比使用指针高。
例如:int iv; int &reiv=iv;//正确!
int &reiv;//错误用法 声明一个引用必须进行初始化。
2.合法性区别。使用引用不需要测试合法性。
3.可修改区别。指针可以被重新赋值指向另一个对象。
(7)C++有了malloc/free,为什么还要用new/delete?
答案:malloc与free是C/C++标准库函数,new/delete是C++运算符。他们都可以申请动态内存和释放内存。对非内部数据类型的对象而言,只用malloc/free无法满足动态对象的要求。
new/delete知多少:
运算符new可以用来动态创建对象和对象数组;使用new创建对象时会调用类的构造函数,也可以用delete析构;
8.面试中预处理问题
8.1用一个宏定义FIND求一个结构体struc里某个变量相对struc的偏移量
{
int a;
char b[20];
double ccc;
}
则:FIND(student,a);//等于0
FIND(student,b);//等于4
答案:#define FIND(struc,e) (size_t)&(((struc*)0)->e)
解析:其中(struc*)0表示将常量0强制转化为struc*型指针所指向的地址;&(((struc*)0)->e)表示取结构体(struc*)0的成员e的地址,因为该结构体的首地址为0,所以其实就是得到了成员e距离结构体首地址的偏移量。(size_t)是一种数据类型,为了便于不同系统之间移植,最好定义为一种无符号型数据,一般为unsigned int.
8.2写一个标准宏定义MIN,这个宏定义输入两个参数并返回较小的一个。
答案:#define MIN(A,B) ((A)<=(B)?(A):(B))
8.3下面程序的运行结果是什么?
#include<stdio.h>
# define ADD(x) x+x
void main()
{
int m=1,n=2,k=3;
int sum=ADD(m+n)*k;
printf("%d",sum);
}
输出结果:10
注意:宏定义表达式最好都加上括号,只是简单的代码兑换;本代码中ADD(m+n)替换为m+n+m+n,没有括号就是m+n+m+n*k=10