C/C++面试题(整理)

1.new、delete、malloc、free关系
答:new和delete,malloc和free都可以用来申请动态内存和释放内存。malloc和free是C/C++语言的标准库函数,new和delete是C++的运算符。new和delete在分配内存时会执行构造函数,delete在释放内存时会执行析构函数。

2.delete与delete []区别
答:delete只会调用一次析构函数,而delete[]会调用每个成员的析构函数。在More Effective C++中这样解释的:“当delete 操作符用于数组时,它为每个数组元素调用析构函数,然后调用operator delete来释放内存”。对于内建简单数据类型虽然没有析构函数delete和delete[]功能类似,但是为了安全以及程序的可读性,应该new对应delete,new[]对应delete[]。

3.C/C++ JAVA .NET区别
答:。。。。。。

4.继承、组合优缺点
答:优先使用组合。 


5.C++有哪些特质(面向对象特点)
答:数据抽象和封装、继承、多态(关键);
    数据抽象是一种依赖于接口和实现分离的编程技术。类设计者必须关心类是如何实现的,但使用该类的程序员仅需了解类型的接口,而不必具体地考虑该类型如何工作。
    封装也就是信息隐藏,通过封装对外界隐藏了对象的实现细节。
    继承是子类自动地共享基类中定义的数据和方法的机制。继承性使得用户在开发新的应用系统时不必完全从零开始,可以继承原有的相似系统的功能或者从类库中选取需要的类,再派生出新的类以实现所需要的功能。还可以用把已有的一般性的解加以具体化的办法,来达到软件重用的目地。

6.子类析构时要调用父类的析构函数吗
答:析构函数与构造函数的顺序相反。构造时,先调用父类的构造函数后调用派生类的构造函数;析构时,先析构子类后析构父类。

7.多态,虚函数,纯虚函数
答:http://blog.csdn.net/tujiaw/article/details/6753498 


8.求下面函数的返回值(微软)

int Func(int x)
{
	int count = 0;


	while (x)
	{
		count++;
		x = x & (x - 1);
	}


	return count;
}
答:将x转换为二进制,返回值为二进制数中1的个数。

9.什么是“引用”,声明和使用“引用”要注意哪些问题
答:“引用”就是某个目标变量的“别名”(alias),对“引用”的操作与对变量的操作效果完全相同。
    声明一个“引用”的时候切记要对其进行初始化。“引用”声明完毕后相当于目标变量有两个名称,即该目标原名和引用名。不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。不能建立数组的引用。

10.将“引用”作为函数参数有哪些特点
答:使用“引用”传递函数的参数在内存中并没有产生参数的副本,它是直接对实参操作;
而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
    使用指针作为函数参数虽然也能达到“引用”的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用“*指针变量名”(解引用)的形式进行运算,这很容易产生错误且程序的阅读性较差,另一方面,在调用时需要传递变量的地址作为实参,而“引用”更容易使用,更清晰。

11.在什么时候需要使用“常引用”
答:如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应该使用常引用,如下声明方式:
例1
int x;
const int &y = x;
y = 1; // error
x = 1; // ok
例2
string Foo();
void Bar(string &s);
调用时:
Bar(Foo()); // error
Bar("hello,world"); // error
这两个都是错误的,原因在于Foo和"hello,world"串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此试图将一个const类型的对象转换为非const类型这是非法的。所以,引用型参数应该在能被定义为const的情况下尽量定义为const。

12.将“引用”作为函数返回值类型的格式、好处和需要遵守的规则
答:格式:类型标识符 & 函数名(形参列表及类型说明){函数体}
好处:在内存中不产生函数返回值的副本,正是由于这点所以有以下注意事项:
1>不能返回局部变量的“引用”。局部变量会在函数返回后被销毁,因此被返回的引用就成为了“无所指”的引用,程序会进入未知状态;
2>不能返回函数内部new分配的内存的引用。虽然不存在局部变量的被动销毁问题,可对于这种情况又面临其它尴尬的局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由malloc或new分配)很可能就无法释放(如果是地址除非用文字明确的说明调用完这个函数后要手动释放),造成内存泄漏(memory leak)。
3>可以返回类成员的引用,但最好是const。原因是当对象的属性是与某种业务规则相关联的时候,其赋值常常与某些其他属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则中。如果其他对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。
4>流操作符重在返回值声明为“引用”的作用:
流操作符<< 和 >>,常常被希望用作连续输入和输出,如:cout << "hello" << endl;因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。其他的可选方案包括:返回一个流对象和返回一个流对象的指针。但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是唯一选择。
赋值操作符=可以连续使用,如:x = y = 10;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的唯一返回值选择。
例3
int val[10];
int error = -1;
int& Put(int i)
{
	if (i >= 0 && i <= 9)
	{
		return val[i];
	}
	else
	{
		return error;
	}
}
int main(char argc,char *argv)
{
	Put(0) = 10;
	Put(3) = 30;

	cout << val[0] << endl;
	cout << val[3] << endl;

	return 0;
}
5>在另外的一些操作符中却千万不能返回引用:+ - * /四则运算符,他们不能返回引用。原因是这四个操作符没有sideeffect。因此,它们必须构造一个对象作为返回值,可选方案包括:返回一个对象、返回一个局部变量的引用、返回一个new分配的对象的引用、返回一个静态对象的引用。根据前面提到的引用作为返回值的三个规则,第2,3两个方案都被否决了。静态对象的引用又因为((a+b) == (c+d))会永远为true而导致错误,所以可选的只剩下一个返回对象了。

13.“引用”与多态的关系
答:引用是除指针外另一个可以产生多态效果的手段。这意味着,一个基类的引用可以指向它的派生类实例。

例4
class A {	};
class B : public class A {	};

B b;
A& ref = b;

14.“引用”与指针的区别是什么
答:指针是通过某个指针变量指向一个对象后,对它所指向的变量进行间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。此外就是上面提到的对函数传ref和pointer的区别。

15.结构与联合的区别
答:结构和联合都是有多个不同的数据类型成员组成,但在任何同一时刻,联合中只存放了一个被选中的成员(所有成员共用一块地址空间),而结构的所有成员都存在(不同成员存放的地址不同)。
对联合的不同成员赋值将会对其他成员重写,原来成员的值就不存在了,而与对结构的不同成员赋值是互不影响的。

16.关于“联合”题目的输出结果
答:
1>
union
{
	int i;
	char x[2];
}a;

int main(char argc,char *argv)
{

	a.x[0] = 10; // 一个字节0a
	a.x[1] = 1;  // 一个字节01
	printf("%d", a.i);	// 四个字节0x0000010a

	return 0;
}
答案:266(低位低地址,高位高地址,内存占用情况0x0000010a)
2>
int main(char argc,char *argv)
{
	union
	{
		int i;
		struct
		{
			char first;
			char second;
		}half;
	}number;

	number.i = 0x4241;
	printf("%c, %c\n", number.half.first, number.half.second); // A, B 

	number.half.first = 'a';
	number.half.second = 'b';
	printf("%x\n", number.i); // 0x6261

	getchar();
	return 0;
}
答案:A, B (0x41是低位对应half中的first,0x42是高位对应half中的second);
     6261(number.i和number.half公用一块地址空间)。

17.关联、聚合(Aggregation)以及组合(Composition)的区别
答:关联表示两个类的对象之间存在某种语义上的联系。例如,作家使用计算机,人们就认为在作家和计算机之间存在某种语义连接,因此,在类图中应该在作家类和计算机类之间建立关联关系。
    聚合是关联的特例。聚合表示类与类之间的关系是整体与部分的关系。在陈述需求时使用的“包含”、“组成”。“分为......部分”等字句,往往意味着存在聚合关系。
    UML中泛化关系就是通常所说的继承关系,它是通过元素和具体元素之间的一种分类关系。具体完善完全拥有通用元素的信息,并且还可以附加一些其他信息。

18.重载(overload)和重写(overried,也叫做“覆盖”)的区别
答:重载是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
    重写是指子类重新定义父类虚函数的方法。
    重载在编译期间就已经确定了,是静态的,是早绑定。
    重写是和多态相关的。当子类重新实现了父类的虚函数之后,父类指针根据赋给它的不同子类指针,动态的调用子类中的函数,是运行期绑定的,是晚绑定。

19.多态的作用
答:隐藏实现细节,使得代码能够模块化:扩展代码模块,实现代码重用;
    接口重用,为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用。

20.Ado与Ado.net的相同与不同
答:除了“能够让应用程序处理存储于DBMS中的数据”这一基本相似点外,两者没有噢太多共同之处。Ado使用OLE DB接口并基于微软的COM技术,而Ado.net拥有自己的Ado.net接口并且基于微软的.net体系架构。众所周知.net体系不同于COM体系,Ado.net接口也就完全不同于Ado和OLE DB接口,这也就是说Ado.net和Ado是两种数据访问方式。Ado.net提供对XML的支持。

21.#define DOUBLE(x) x+x, i = 5 * DOUBLE(5); i 是多少
答:5 * 5 + 5 等于30.

22.有哪几种情况只能用initialization list而不能用assignment
答:没有默认构造函数的的类类型的成员,以及const或引用类型的成员。初始化的那个对象其本身还不存在,而赋值表示对象本身已经存在了。

23.C++是不是类型安全的
答:不是。两个不同类型的指针之间可以强制转换(用reinterpret_caset)。C#是类型安全的。

24.main函数执行以前,还会执行什么代码
答:全局对象的构造函数。

25.描述内存分配方式以及它们的区别
答:全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的(初始化的全局变量和静态变量在一块区域,未初始化的全局变量与静态变量在相邻的另一块区域,同时未被初始化的对象存储区可以通过void*来访问和操纵,程序结束后由系统自行释放),在C++里面没有这个区分了,他们共同占用同一块内存区。
    常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多)
    栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变量、函数参数等。
    自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的,但是在部分编译器的实现上这两块内存都是同一种管理方式。
    堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。堆可以动态地扩展和收缩。

26.struct和class的区别
答:成员的默认访问权限。class的成员默认是private权限,struct默认是public权限;
    默认继承权限。如果不明确指定,来自class的继承按照private继承处理,来自struct的继承按照public继承处理。

27.当一个类A中没有任何成员变量与成员函数,这是sizeof(A)的值是多少
答:1。编译器为这样没有成员的结构体隐形定义了一个一字节(char)的成员,目的是为了能用来标识类实例之间的不同。这里可以这样理解,如果没有这一个字节,那么所有结构体的实例都是空,也就不占内存,那么他们的实例是没有区别的,因此C++的编译器不允许这么做,从而隐式增加了一个字节。

28.在8086汇编下,逻辑地址和物理地址是怎样转换的(Interl)
答:通用寄存器给出的地址是段内偏移地址,相应段寄存器地址 * 10H + 通用寄存器内地址,就得到了真正要访问的地址。

29.比较C++中的4中类型转换方式
答:const_cast用来剥除类型中的const修饰符;
    static_cast将父类转换为子类,如果转换不成立的话,编译器会提示错误,强制类型转换则不会有提示。
    reinterpret_cast一般用于指针之间的转换,通常只是将基类指针假装成一个派生类指针而不改变其值,而static_cast则将执行正确的地址操作。
    dynamic_cast通常用于执行从指向基类的指针安全地向下转型为指向派生类的指针。不同于static_cast的是, dynamic_cast仅用于对多态类型进行向下转型(也就是说,被转型的表达式的类型,必须是一个指向带有虚函数的类类型的指针),并且执行运行期检查工作,来判定转型的正确性。这种转型是需要付出代价的而static_cast则不需要。
详细请看: http://blog.csdn.net/tujiaw/article/details/6101393

30.分别写出BOOL, int, float, 指针类型的变量a与“零”的比较语句
答:
BOOL: if (!a)	or	if (a);
int:	if (a == 0);
float	:	const EXPRESSION EXP = 0.000001; // 允许的精度,即误差
if (a < EXP && a > - EXP)
pointer: if ( a != NULL) or if (a == NULL)

31.请说出const与#define相比,有何有点
答:const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
    const作用:定义常量、修饰函数参数、修饰函数返回值,被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。

32.简述数组与指针的区别
答:数组要么在静态存储区被创建(如:全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。
1>
char a[] = "hello";
a[0] = 'x';
char *p = "world";	// p指向常量字符串
p[0] = 'x';			// 编译器不能发现该错误,运行时会报错
2>用运算符sizeof可以计算出数组的容量(字节数)。sizeof(p), p为指针变量的字节数,而不是p所指的内存容量。C/C++语言没有办法知道指针所指的内存容量,除非在申请内存是记住它。sizeof(a)可以求出数组的大小,但是如果数组最为函数参数,该数组会退化为指针,求出来的大小就是指针变量的字节数了。

33.类成员函数的重载、覆盖和隐藏的区别
答:重载,发生在同一个类中,函数名相同,参数不同,virtual关键字可有可无;
    覆盖,分别位于派生类与基类中,函数名相同,参数相同,基类函数必须有virtual关键字。
    隐藏,派生类的函数屏蔽了与其同名的基类函数。规则如下:
如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数被隐藏(注意别与重载混淆)。
    如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。

34.求出两个数中的较大者
   There are two int variables:a and b, dou’t use “if”, “?:”, “switch” or other judegement statements, find out the biggest one of the two numbers.
答:((a + b) + abs(a – b)) / 2;

35.如何打印出当前源文件的文件名以及源文件的当前行号
答:cout << __FILE__;
cout << __LINE__;
__FILE__和__LINE__是系统预定义宏,这种宏并不是在某个文件中定义的,而是由编译器定义的。

35.main主函数执行完毕后,是否可能会再执行一段代码,给出说明
答:。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

36.如何判断一段程序是由C编译的还是由C++编译的
答:#ifdef __cplusplus
cout << "c++" << endl;
#else
cout << "c" << endl;
#endif


37.文件中有一组整数,要求排序后输出到另一个文件中
答:
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;

template<class T>
void Swap(T &x, T &y)
{
	T t;
	t = x;	x = y;	y = t;
}

bool Order(vector<int> &arr_data)
{
	if (arr_data.empty())
	{
		return false;
	}

	int count = arr_data.size();

	for (int i = 0; i < count - 1; i++)
	{
		for (int j = i + 1; j < count; j++)
		{
			if (arr_data[j] < arr_data[i])
			{
				Swap<int>(arr_data[j], arr_data[i]);
			}
		}
	}

	return true;
}

int main(int argc, char *argv[])
{
	ifstream in("one.txt");
	ofstream out("two.txt");

	vector<int> vi_array;
	vector<int>::iterator iter;
	int num;

	while (!in.eof())
	{
		in >> num;
		vi_array.push_back(num);
	}

	if (Order(vi_array))
	{
		for (iter = vi_array.begin(); iter != vi_array.end(); ++iter)
		{
			out << *iter << " ";
		}
	}

	in.close();
	out.close();
	return 0;
}

38.链表题,一个链表的节点结构
答: http://blog.csdn.net/tujiaw/article/details/6682227 


39.分析一下这段程序的输出(Autodesk)
答:
class B
{
public:
	B() { cout << "default constructor" << endl; }
	B(int i) : data(i) { cout << "constructed by parameter " << data << endl; }
	~B() { cout << "destructed" << endl; }

private:
	int data;
};

B Play( B b)
{
	return b;
}

int main(int argc, char *argv[])
{
	B t1 = Play(5);
	B t2 = Play(t1);
	// constructed by parameter 5
	// destructed
	// destructed

	B t3 = Play(5);
	B t4 = Play(10);
	// constructed by parameter 5
	// destructed
	// constructed by parameter 10
	// destructed

	cin.get();
	return 0;
}

40.写一个函数找出一个整数数组中第二大的数(microsoft)
答:
const int MIN_NUMBER = -32767;
int FindSecondNumber(const int data[], int count)
{
	int max_number = data[0];
	int second_max_number = MIN_NUMBER;
	for (int i = 1; i < count; i++)
	{
		if (data[i] > max_number)
		{
			second_max_number = max_number;
			max_number = data[i];
		}
		else if (data[i] > second_max_number)
		{
			second_max_number = data[i];
		}
	}

	return second_max_number;
}

41.写一个在一个字符串(n)中寻找一个字串(m)第一个位置的函数
答: KMP算法

42.多重继承的内存分配问题
答:

43.如何判断一个单链表是有环的(注意不能用标志位,最多只能用两个额外指针)
答:
typedef struct _Node
{
	char val;
	struct _Node *next;
}Node;


bool CheckRing(const Node *head)
{
	if (head == NULL)
	{
		return false;
	}


	Node *slow = head;
	Node *fast = head->next;
	while (fast != NULL && fast->next != NULL)
	{
		slow = slow->next;
		fast = fast->next->next;
		if (slow == fast)
		{
			return true;
		}
	}
	return false;
}

44.指针找错题
答:
void test1()
{
	char str[10];
	char *str1 = "0123456789";
	strcpy(str, str1);
}
// str需要11个元素才能存放得下str1,其中包含一个'\0'

void test2()
{
	char str[10], str1[10];

	for (int i = 0; i < 10; i++)
	{
		str1 = 'a';
	}
	strcpy(str, str1);
}
// str1不能在数组内结束(末尾无'\0');
// 所复制的字节数具有不确定性;
// strcpy所复制的字符串是以'\0'结束的。

void test3(char *str1)
{
	char str[10];
	if (strlen(str1) <= 10)
	{
		strcpy(str, str1);
	}
}
// 由于strlen没有把'\0'计算在内,所以当str1有10个元素的时候,情况如test1,
// 将<=改为<

45.编写一个标准的strcpy和strlen函数
答:
char* StrcpyEx(char *dest, const char *src)
{
	assert((dest != NULL) && (src != NULL));


	char *ret = dest;
	while ((*dest++ = *src++) != '\0');


	return ret;
}


int StrlenEx(const char *src)
{
	assert(src != NULL);


	int len = 0;
	while ((*src++) != '\0')
	{
		len++;
	}


	return len;
}

46.头文件中的ifndef/define/endif的作用
答:防止该头文件被重复引用。

47.#include <file.h>与#include "file.h"的区别
答:前者是从Standard Library的路径寻找和引用file.h,而后者是从当前工作路径搜寻并引用file.h。

48.在C++程序中调用被C编译器编译后的函数,为什么要加extern "C"
答:作为一种面向对象的语言,C++支持函数重载,而过程式语言C不支持。函数被C++编译后在符号库中的名字与C语言的不同,而用extern "C"修饰的变量和函数是按照C语言方式编译和链接的。所以它的作用是实现C++与C以及其它语言的混合编程。
用法:
#ifdef __cplusplus
extern "C" {
#endif
	...
		...
		...
#ifdef __cplusplus
}
#endif

49.String的具体实现
答: http://blog.csdn.net/tujiaw/article/details/6103609

50.What is displayed when Func() is called given the code:
class Number
{
public:
	string type;
	Number() : type("void") { }
	explicit Number(short) : type("short") { }
	Number(int) : type("int") { }
};


void Show(const Number &n) { cout << n.type << endl;}


void Func()
{
	short s = 42;
	Show(s);
}
a) void b)short      c)int      d)None of the above
答:c, 之所以传一个int型的实参就会调用到Number(int)构造函数,
是因为单参数构造函数会自动类型转换的,也就是说Number num = 42,
相当于Number num(42);在单参数构造函数前面加explicit表示禁止这种转换。

51.Which is the correct output for the following code
double dArray[2] = {4, 8};
double *p, *q;
p = &dArray[0];
q = p + 1;


cout << q - p << endl;
cout << (int)q - (int)p << endl;
a) 1 and 8     b) 8 and 4     c) 4 and 8     d) 8 and 1
答:a,第一个指针加减只跟类型位置有关,由于4与8之间只差一个位置所以为1;
第二个是指针转换为实际值的加减,由于4与8之间隔一个double,所以相差8个字节的地址。


52.Sony笔试题
1>完成下列程序
*
*.*.
*..*..*..
*...*...*...*...
*....*....*....*....*....
*.....*.....*.....*.....*.....*.....
*......*......*......*......*......*......*......
*.......*.......*.......*.......*.......*.......*.......*.......
答:
#include <iostream>
using namespace std;

#define NUM 8

int main(void)
{
	int i, j, k;

	/
	for (i = 1; i <= NUM; i++)
	{
		for (j = 1; j <= i; j++)
		{
			cout << '*';
			for (k = 1; k < i; k++)
			{
				cout << '.';
			}
		}
		cout << endl;
	}
	/

	cin.get();
	return 0;
}
53.将字符串中的单词进行倒序

54.将一个长度为n的字符串向左循环移动m位
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值