C++ 面试题 (21-30)

21、const和#define相比有什么不同?

解析
c++可以用const定义常量也可以用define定义常量,但是前者比后者有更多的优点:
(1)const常量有数据类型,宏常量没有。编译器可以对前者进行类型安全检查,而对后者只有字符替换,没有类型安全检查,并且字符替换中可能会产生意料不到的错误(边界效应)
列如:

#define N 2+3 //我们预想的N值是5,我们这样使用N
double a = N/2;  //我们预想的a的值是2.5,可实际上a的值是3.5

(2)有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。在C++程序中只使用const常量,而不使用宏常量,即const常量完全取代宏常量。 (不明所以)
(3)define是在编译的预处理阶段起作用,而const是在 编译、运行的时候起作用。
(4)空间占用而言:
例如:

#define PI 3.14     //预处理后 占用代码区空间
const float PI=3.14;    //本质上还是一个 float,占用常量空间

内存四驱

22、下列代码结果是

struct A
{
	short a1;
	short a2;
	short a3;
};
struct B
{
	long a1;
	short a2;
};
int main()
{
	char* ss1 = "0123456789";
	char ss2[] ="0123456789";
	char ss3[100]="0123456789";
	string ss4 = "0123456789"
	int ss4[100];
	char q1[] ="abc";
	char q2[] = "a\n";
	char* q3 = "a\n";
	char* str1 = (char*)malloc(100);
	void* str2 = (char*)malloc(100);
	cout << sizeof(*ss1) << endl;	// 1
	cout << sizeof(ss1) << endl;	// 4
	cout << sizeof(ss2) << endl;	// 11 
	cout << sizeof(ss3) << endl; 	// 100
	cout << sizeof(ss4) << endl; 	// 400
	cout << sizeof(ss5) << endl;	// 28
	cout << sizeof(q1) << endl;		// 4
	cout << sizeof(q2) << endl;		// 3
	cout << sizeof(q3) << endl;		// 4
	cout << sizeof(A) << endl;		// 6
	cout << sizeof(B) << endl;		// 8
	cout << sizeof(str1) << endl; 	// 4
	cout << sizeof(str2) << endl; 	// 4
}

解析
sizeof运算符返回一条表达式或一个类型名所占的字节数。

无论你的string或vectory里放多长的字符串,它的sizeof()都是固定的,字符串所占的空间是从堆中动态分配的,与sizeof()无关。

一.偏移量指的是结构体变量中成员的地址和结构体变量地址的差。结构体大小等于最后一个成员的偏移量加上最后一个成员的大小。

二.结构体在实际中,存储变量时地址要求对齐,编译器在编译程序时会遵循两条原则:

(1)结构体变量中成员的偏移量必须是成员大小的整数倍(0被认为是任何数的整数倍)
(2)结构体大小必须是所有成员大小的整数倍,也即所有成员大小的公倍数。

三.结构体大小等于最后一个成员的偏移量加上其大小

sizeof(结构体)

CPU优化原则,对于n字节的元素(n = 2,4,8…)他的首地址能够被n整除,才能获得最好的性能。 设计编译的时候遵循这个原则:对于每个变量,可以从当前位置向后找到第一个满足这个条件的地址,作为首地址。(就是上面的第二点)

23、下面代码的输出结果是?

#include <iostream>
using namespace std;

class A{

};

class A2{
	char d,e;
};

struct B{

};

struct C{
	char b,c;
};

struct D{
	int x,y;
};

int main(){
	cout <<sizeof(A)<< endl;	//1
	cout << sizeof(A2) << endl;	//2

	A* p1 = new A();
	A p2;
	A* p3;
	
	cout << sizeof(p1)<< endl;	//4
	cout << sizeof(p2)<< endl;	//1
	cout << sizeof(p3)<< endl;	//4
	cout << sizeof(B)<< endl;	//1
	cout << sizeof(C)<< endl;	//2
	cout << sizeof(D)<< endl;	//8

	return 0;

}

解析

每个实例在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类或空结构体(C++中结构体也可看为类)隐含的加一个字节,这样空类或空结构体在实例化后在内存得到了独一无二的地址,所以空类所占的内存大小是1个字节。

24、求解下面程序的结果

#include <iostream>

class A1
{
public:
	int a;
	static int b;

	A1();
	~A1();
};

class A2
{
public:
	int a;	
	char c;
	A2();
	~A2();
};

class A3
{
public:
	float a;
	char c;
	A3();
	~A3();
};

class A4
{
public:
	float a;
	int b;
	char c;
	A4();
	~A4();
};

class A5
{
public:
	double d;
	float a;
	int b;
	char c;
	A5();
	~A5();
};

int main()
{
	cout << sizeof(A1)<< endl;	//4
	cout << sizeof(A2)<< endl;	//8
	cout << sizeof(A3)<< endl;	//8
	cout << sizeof(A4)<< endl;	//12
	cout << sizeof(A5)<< endl;	//24
	return 0;
}

25、说明sizeof和strlen之间的区别

  1. sizeof操作符的结果类型是size_t,它在头文件中的typedef为unsigned int类型。该类型保证能容纳实现所建立的最大对象的字节大小。typeof用法
  2. sizeof是运算符,strlen是函数。
  3. sizeof可以用类型做参数,strlen只能用char*做参数,且必须以“\0”结尾的。sizeof还可以用函数做参数,比如 short f();printf("%d\n",sizeof(f()));
  4. 数组做sizeof的参数不退化,传递给strlen就退化为指针。
  5. 大部分编译程序在编译的时候就把sizeof计算过了,是类型或是变量的长度。这就是sizeof(x)可以用来定义数组维数的原因。
char str[20] = "0123456789";
int a = strlen(str);//a=10;
int b = sizeof(str);//b = 20;
  1. strlen的结果是在运行的时候才能计算出来,用来计算字符串的长度,而不是类型占内存的大小。
  2. sizeof后如果是类型必须加括号,如果是变量名可以不加括号。这是因为sizeof是个操作符而不是个函数。
  3. 当使用了一个结果类型或变量时,sizeof返回实际的大小。当使用静态的空间数组时,sizeof返回全部数组的尺寸。sizeof操作符不能返回被动态分配的数组或外部的数组的尺寸。
  4. 数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址,如:fun(char[8])、fun(char[])都等价于fun(char*).在C++里传递数组永远都是传递指向数组首元素的指针,编译器不知道数组的大小。如果想在函数内知道数组的大小,需要这样做:进入函数后用memcpy将数组拷贝出来,长度由另一个形参传进去。代码如下:
func( unsigned char* p1,int len)
{
	unsigned char* buf = new unsigned char(len+1);
	memcpy(buf,p1,len);
}
  1. 计算结构变量的大小就必须讨论数据对齐问题。为了使CPU存取的速度最快(这同CPU取数操作有关,详细的介绍可以参考一些计算机原理方面的书),C++在处理数据时经常把结构变量中的成员的大小按照4或8的倍数计算,这就叫数据对齐。这样做可能会浪费一些内存,但是在理论上CPU的速度会快。当然,这样设置会在读写一些别的应用程序生成的数据文件或交换数据时带来不便。 MS VC++ 中的对齐设定,有时候sizeof得到的与实际不等。一般在VC++中加上#pragma pack(n)的设定即可。或者如果要按字符存储,而不进行数据对其,可以在Options对话框中修改Advanced Complier选项卡中的“Data Alignment”为按字节对齐。
    Pragma Pack(n)的详细讲解
  2. sizeof操作符不能用于函数类型、不完全类型或位字段。不完全类型指具有未知存储大小数据的数据类型,如未知存储大小的数组类型、未知内容的结构或联合类型、void类型等。

26、说明sizeof的使用场合。

  1. sizeof操作符的一个主要用途是与存储分配和I/O系统那样的例程进行通信,例如:
void* malloc(size_t size),
size_t fread(void* ptr,size_t size,size_t nmemb,FILE* stream)

功能–从给定流 stream 读取数据到 ptr 所指向的数组中。
ptr – 这是指向带有最小尺寸 size * nmemb 字节的内存块的指针。
size – 这是要读取的每个元素的大小,以字节为单位。
nmemb – 这是元素的个数,每个元素的大小为 size 字节。
stream – 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流。
返回值–成功读取的元素总数会以 size_t 对象返回,
size_t 对象是一个整型数据类型。

  1. 用它可以看看某种类型的对象在内存中所占的单元字节。例如:
void* memset(void* s,int c,sizeof(s))

作用是将某一块内存中的内容全部设置为指定的值, 这个函数通常为新申请的内存做初始化工作。

  1. 在动态分配一对象时,可以让系统知道要分配多少内存。
  2. 便于一些类型的扩充。在Windows中有很多结构类型就有一个专用的字段用来存放该类型的字节大小。
  3. 由于操作数的字节数在实现时可能出现变化,建议在涉及到操作数字节大小时用sizeof代替常量计算。
  4. 如果操作数是函数中的数组形参或函数类型的形参,sizeof给出其指针的大小。

27、int **a[3][4]这个数组占多大空间。

解析 3 * 4 * 4 = 48

28、找出下面程序的错误,并解释为什么

#include <iostream>
#include <string>

using name space std;

int main(int argc,char* argv[])
{
	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;
}

解析
sizeof(pStrArr1)得到的是指针的大小4,不能正确输出 USCN
正确答案↓

#include <iostream>
#include <string>

using namespace std;

int main(int argc,char* argv[])
{
	string strArr1[] = {"Trend","Micro","Soft"};
	string *pStrArr1 = new string[2];
	pStrArr1[0] = "US";
	pStrArr1[1] ="CN";
	
	cout << sizeof(strArr1)<< endl; // 28 * 3
	cout << sizeof(string)<< endl;	// 28
	for(int i = 0;i < sizeof(strArr1)/sizeof(string);i++)
	{
		cout << strArr1[i];
	}

	for(int j = 0;j < sizeof(string)*2/sizeof(string);j++)
	{
		cout << pStrArr1[j];
	}

	return 0;
}

29、写出下面sizeof的答案

#include <iostream>
#include <complex>

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 Dervied : public Base
{
public:
	Dervied(){cout <<"Dervied-ctor"<< endl;}
	~Dervied(){cout <<"Dervied-dtor"<< endl;}
	void f(){complex<double>){cout <<"Dervied::f(complex)"<< endl;
	virtual void g(int i = 20){cout <<"Dervied::g()" << i << endl;}
};

int main()
{
	Base b;
	Dervied d;
	Base* pb = new Dervied();
	cout << sizeof(Base) << "tt" << endl;	//4
	cout << sizeof(Dervied)<<"bb"<< endl;	//4
}

复数类

虚函数会在对象占用内存开始处增加4个字节(无论虚函数有多少个)。这4个字节是指向“虚函数表”的指针,即指向一个函数地址表,函数地址再指向对应的函数实现。

31下面代码的输出结果是

char var[10];
int test(char var[])
{
	return sizeof(var);
}

解析
var[]等价于*var,已经退化成一个指针了,所以大小是4;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值