C/C++面试题回顾

1、实现C语言atoi函数的功能,实际我的函数功能要比atoi强大

int my_atoi(const char* str)
{
	if (str == nullptr)
		return 0;
	bool negative_flag = false;
	bool first_num_flag = false;
	int first_num_pos, last_num_pos;
	int i = 0;
	while (str[i] != 0)
	{
		if (first_num_flag)
		{
			if (str[i] >= '0' && str[i] <= '9')
				last_num_pos = i;
			else
				break;
		}
		else
		{
			if (str[i] == '-')
				negative_flag = true;
			else if (str[i] >= '1' && str[i] <= '9')
			{
				first_num_flag = true;
				first_num_pos = last_num_pos = i;
			}
			else if (negative_flag)
				negative_flag = false;
		}
		i++;
	}
	if (first_num_flag)
	{
		int sum = 0;
		int n = last_num_pos - first_num_pos;
		for (int i = first_num_pos; i <= last_num_pos; i++)
			sum += (str[i] - 48)*pow(10.0, n--);//如果预先知道数据有多大,则可以设置权值,不调用pow函数以提升性能
		if (negative_flag)
			sum = -1 * sum;
		return sum;
	}
	else
		return 0;
}

2、已知char str[16];数组中元素的值为0或1,为了节省内存,将str中的元素存放在unsigned char arr[2];中,比如str[0]存放在arr[0]中的第0位,str[1]存放在arr[0]中的第1位,str[8]存放在arr[1]中的第0位。

char str[16] = {0,1,1,1,0,1,1,1,1,1,0,1,0,0,1,0};
unsigned char arr[2];
int i = 0, j = 0;
memset(arr, 0, sizeof(arr));
unsigned char tmp;
while (i < sizeof(str))
{
	tmp = str[i];
	tmp <<= i % 8;
	arr[j] |= tmp;
	i++;
	if (i % 8 == 0)
		j++;
}

3、注意以下表达式的用法

int a = 88;
a /= 1 + 2 * 3 + 4;
printf("a=%d\n",a);//输出结果为:a=8
a = 1;
a <<= 1 + 1;
printf("a=%d\n", a);//输出结果为:a=4

4、结构体对齐与限定结构体成员所使用的位数

#include <stdio.h>

#pragma pack(1)
struct A
{
	char c : 3;//c只用到了3位
	int i : 31;//i只用到了31位
};

void main()
{
	printf("%d\n",sizeof(A));//1字节+4字节=5字节
	A a;
	a.c = 11;//11用二进制表示为1011,由于c占3位,所以只存储011,所以c的值为3
	printf("%d\n", a.c);
	a.c = 15;//15用二进制表示为1111,由于c占3位,所以只存储111,所以c的值为-1;
	printf("%d\n", a.c);
	getchar();
}

5、判断当前机器是大端模式还是小端模式,Intel处理器都是小端模式。

#include <stdio.h>
void main()
{
	union Test
	{
		unsigned char c;
		unsigned short s;
	}t = { 0x1234 };
	if (t.c == 0x34)
		printf("Little-Endian\n");
	else
		printf("Big-Endian\n");
}

6、C/C++ static的用法

/*
以下代码输出结果为:
1
1
原因在于,static int i=++n;这种语句只会执行一次;
如果将static int i=++n;语句改为:
static int i;
i=++n;
则输出结果为:
5
5
由此说明,如果在WM_PAINT消息处理中执行:
static HDC hdcMem=CreateCompatibleDC(hdc);
则CreateCompatibleDC函数只会被执行一次。
*/
#include <iostream>
using namespace std;

int func(int& n)
{
	static int i = ++n;
	return i;
}

void main()
{
	int n = 0, count = 0, ret = 0;
	while (count++ < 5)
		ret = func(n);
	cout << n << endl;
	cout << ret << endl;
	getchar();
}

7、C++动态分配连续存储空间的矩阵及其释放
无论是静态分配还是动态分配,矩阵的列数必须被指定。

int(*p1)[3];//声明了一个指向3维数组的指针变量
int a1[3] = {1,2,3};
p1 = &a1;
printf("%p\n%p\n",p1,(p1+1));

上面代码段在x86机器上的输出结果为:
0x0030FEE0
0x0030FEEC
可见两个地址相差12字节,说明p1指向的是一个3维数组而不是一个int型数据。

int(*p1)[3];//声明了一个指向3维数组的指针变量
p1=new int[2][3];//动态分配了一个2行3列的矩阵
delete[]p1;//释放内存

8、将一个字符串逆序输出

//将字符串逆序输出(递归实现)
void reverse_output(const char* str)
{
	if (!str)
		return;
	if (!*str)
		return;
	reverse_output(str + 1);
	putchar(*str);
}

9、将字符串反转后再返回字符串

//反转字符串
char* reverse(char* str)
{
	if (!str)
		return NULL;
	int i = 0, j = 0;
	while (str[i++]);//求出字符串长度
	i -= 2;
	while (j < i)
	{
		char tmp = str[i];
		str[i] = str[j];
		str[j] = tmp;
		i--;
		j++;
	}
	return str;
}

10、约瑟夫环问题

//约瑟夫环问题,n代表人数,k代表数到多少后出列,依次输出出列人员的编号
void number(int n, int k)
{
	if (n > 0 && k > 0)
	{
		int* pArr = (int*)malloc(n*sizeof(int));//pArr数组用于保存每个人的编号
		for (int i = 0; i < n; i++)
			pArr[i] = i + 1;
		int total = n;//total存放总人数
		int count = 0;//存放报数
		int i = 0;//数组下标
		while (total > 1)
		{
			if ((count = (count + 1) % k) == 0)//如果数到k,就出列
			{
				printf("%d\n",pArr[i]);//出列
				pArr[i] = 0;
				total--;//总人数减1
			}
			i = (i + 1) % n;
			while (pArr[i] == 0)
				i = (i + 1) % n;
		}
		printf("%d\n", pArr[i]);//最后一个人出列
		free(pArr);
	}
}

11、判断单链表是否有环

//判断单链表是否有环
typedef struct tagNode
{
	int data;
	Node* next;
}Node;
//头指针表示一个单链表,返回1表示有环;返回0表示无环
int HasCircle(Node* head)
{
	Node *slow, *fast;
	slow = fast = head;
	while (slow != NULL && fast->next != NULL)
	{
		slow = slow->next;
		fast = fast->next->next;
		if (slow == fast)
			return 1;
	}
	return 0;
}

12、对于一个频繁使用的短小函数,在C语言中用宏实现,在C++中用inline实现。

13、可编程内存在基本上分为这样的几大部分:静态存储区、堆区和栈区。他们的功能不同,对他们使用方式也就不同。

静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量。

栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

堆区:亦称动态内存分配。程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在适当的时候用free或delete释放内存。动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存。 但是,良好的编程习惯是:如果某动态内存不再使用,需要将其释放掉,否则,我们认为发生了内存泄漏现象。

C语言中,static 修饰的变量都位于静态存储区,static修饰的全局变量只能在本源程序文件中被访问,不能在其它源程序文件中被访问,所以,C语言是允许不同源文件定义同名的全局变量的,但是要求最多只能有一个变量没有用static修饰。

14、new:先分配内存,再构造对象(调用构造函数);
delete:先销毁对象(调用析构函数),再释放内存。
15、C++类中的const成员变量只能用参数列表来初始化,不能在构造函数体内对其赋值。
16、explicit关键字的用法:

#include <iostream>
using namespace std;

class A
{
public:
	explicit A(int x) { i = x; }
	int i;
};

int main()
{
	A a = 123;//因为加了explicit关键字,所以这里编译出错
	getchar();
	return 0;
}

17、void* operator new、void operator delete这两个重载函数,前者运行在对象构造之前,后者运行在对象析构之后,所以这两个函数如果定义为类成员函数,则必须是静态的,只有静态函数才不能访问对象的属性(数据成员)。如果没有使用static修饰,默认就是static的。
18、C++中,对野指针的操作可能导致程序崩溃,也可能程序不崩溃,视这个野指针指向的内存是否为只读或系统的核心。

猜你喜欢

转载自blog.csdn.net/csdn_gddf102384398/article/details/87392067