大疆秋招笔试
单选
1.以下关于extern的说法,错误的是()
A. extern修饰的符号可以在其他模块使用 B. extern int foo();等同于int foo();
C. 匿名命名空间内定义的变量不可被extern D.exter "C"修饰的方法支持重载
解析:
A. extern修饰的符号可以在其他模块使用:正确。
extern
用于声明外部符号,使其在多个文件之间共享。B. extern int foo(); 等同于 int foo();:正确。
extern
声明的函数默认是全局可见的,与直接声明函数没有区别。C. 匿名命名空间内定义的变量不可被extern:正确。匿名命名空间内的符号只能在当前编译单元内访问,无法通过
extern
在其他地方引用。D. extern "C"修饰的方法支持重载:错误。
extern "C"
用于指定C语言链接方式,不支持C++的函数重载,因为C语言本身不支持重载。
2. 关于如下代码的说法,正确的是()
class A {
public: void func(){printf("hello world!");}
private:int a;
};int main(int argc, char *argv[]){A*p=nullptr;p->func();return 0;
}
A. 编译警告:空指针,运行崩溃
B. 编译通过,运行崩溃
C. 编译通过,可运行,控制台输出“hello world!”
解析:正确的说法是 B. 编译通过,运行崩溃。
编译通过:代码会成功编译,因为在C++中,调用成员函数不需要检查指针是否为空。编译器不会在编译阶段检查
p->func()
中p
是否为nullptr
。运行崩溃:代码中
p
被初始化为nullptr
,但是p->func()
调用了空指针对象的方法。虽然函数本身没有访问类的成员变量a
(如果访问成员变量,可能会更快导致崩溃),但在大多数系统上调用空指针成员函数仍然会导致运行时错误(崩溃)。
3. 64位系统下,对如下结构体取sizeof,结果应为()
struct DATA64{ int* a; char b; int c; int64_t d;
}
解析:24字节。由于不同成员有不同的大小,编译器会对结构体进行对齐。
int* a
: 指针类型,64位中占用8字节。char b
: 占用1字节,但为了对齐到下一个int c
的4字节边界,编译器会在b
后面填充3个字节,使得总长度为4字节。int c
: 占用4字节,刚好对齐。int64_t d
: 占用8字节,需要对齐到8字节边界,所以编译器可能会填充额外的字节。
4. 使用小端字节序时,对于int a=0x12345678,在内存中由低到高的布局是()
解析:在小端字节序(Little-Endian)中,数据的低字节存储在内存的低地址处,高字节存储在内存的高地址处。
对于
int a = 0x12345678
,其十六进制表示可以分为4个字节:
- 0x12(高字节)
- 0x34
- 0x56
- 0x78(低字节)
在小端字节序下,这4个字节在内存中的布局(从低地址到高地址)将是:
- 低地址 -> 0x78
- 接着 -> 0x56
- 接着 -> 0x34
- 高地址 -> 0x12
因此,内存中的布局由低到高依次为: 0x78 0x56 0x34 0x12
5. 不稳定的排序算法有:
- 快速排序(Quick Sort)
- 选择排序(Selection Sort)
- 堆排序(Heap Sort)
- 希尔排序(Shell Sort)
这些算法在排序过程中可能会改变具有相同值的元素的相对顺序,因此它们是不稳定的。
6. 以下对于内存管理的说法,错误的是()
A. 进程内存空间分为数据段、代码段、BSS、堆、栈
B. 当内存空间充足时,应用程序直接访问物理内存,当内存不足时,操作系统会分配虚拟内存给应用程序使用
C. 内存分段比内存分页的内存交换效率低
D. 内存分页机制下,内存分配的最小单位为一页
解析:
A. 正确。进程内存空间通常分为代码段、数据段、BSS段、堆、栈等不同区域。
B. 错误。应用程序无论内存是否充足,都不直接访问物理内存,而是通过操作系统的内存管理机制访问虚拟内存。操作系统会将虚拟内存映射到物理内存中,并根据需要使用分页或分段技术进行内存管理。当物理内存不足时,操作系统会将部分数据交换到硬盘上的交换空间或页面文件中。
C. 正确。内存分段的管理比分页复杂,且内存交换效率通常低于分页机制。
D. 正确。在内存分页机制下,内存分配的最小单位是“页”(page),通常是4KB大小。
多选
1. 以下关于static的说法,正确的是()
A. 设计和使用静态全局变量,静态局部变量的函数时,需要考虑重入问题
B. 全局static变量和局部static变量的存储区位置不同
C. static可以限制符号的可见范围
D. 静态全局变量过大时,可能会导致堆栈溢出
解析:
A. 正确。静态全局变量和静态局部变量在函数内部保持状态,如果函数是多线程访问的,则需要考虑重入问题和线程安全问题。
B. 错误。全局
static
变量存储在数据段中,而局部static
变量也存储在数据段中,但它的作用域仅限于所在的函数内部。它们的存储区位置相同,但作用域不同。C. 正确。
static
可以限制符号的可见范围。在函数内部声明static
变量会将其作用范围限制在函数内;在全局作用域声明static
变量则会限制其只在当前编译单元内可见。D. 错误。静态全局变量存储在数据段,而堆栈溢出通常是由于栈空间耗尽造成的,静态全局变量的大小不会影响栈空间。
2. 以下关于构造函数和析构函数的说法,错误的是()
A. 向std::vector中插入元素时,只涉及到对象的拷贝,所以不会调用移动构造函数
B. 将构造函数设计成虚函数,可实现不同类型的数据初始化多态
C. 将析构函数设计成虚函数,可实现不同类型的数据释放多态
D. 如果在拷贝构造函数中使用值传参,最大的问题是多一次数据拷贝。如果对象数据量很小,使用值传参也是可以接受的
解析:
A. 错误。向
std::vector
中插入元素时,如果支持移动语义(即对象有移动构造函数),且元素可以被移动,则可能会调用移动构造函数。并不总是只涉及到对象的拷贝。B. 错误。构造函数不能是虚函数。C++标准中规定构造函数不支持虚函数机制,因此无法通过将构造函数设计成虚函数来实现多态。
C. 正确。将析构函数设计成虚函数,可以确保在多态情况下正确调用派生类的析构函数,防止资源泄漏,达到多态释放资源的目的。
D. 正确。如果在拷贝构造函数中使用值传参,会导致多一次数据拷贝,这确实可能带来性能损失,但如果对象的数据量很小,代价较小,可以接受。
3. 以下关于虚函数的说法,正确的是()
A. C++的虚函数是在编译时期动态绑定的
B. C++的虚函数是在运行时期动态绑定的
C. 尽量将短小的虚函数定义为内联函数,可以加快程序编译速度
D. 使用多重继承后,编译器会添加多个虚表指针
解析:
A. 错误。C++的虚函数是在运行时期进行动态绑定的,这种机制使得调用哪个函数由对象的实际类型决定,而不是编译时决定。
B. 正确。虚函数的动态绑定是在运行时期进行的,这样在执行期间能够正确调用派生类的函数实现。
C. 正确。将短小的虚函数定义为内联函数可以减少函数调用的开销,并可能提高程序的性能,但编译器可能会根据情况决定是否实际进行内联化。
D. 正确。使用多重继承时,编译器会为每个基类创建一个虚表指针,因此会有多个虚表指针(vptr),以正确处理不同基类的虚函数。
4. 以下关于STL容器的说法,错误的是()
A. std::deque只能从最后面插入元素 B. std::list是一个单链表结构
C. 当删除std::vector内中间的一个元素时,所有迭代器都将失效
D. std::map基于哈希表实现
解析:
A. 错误。
std::deque
(双端队列)允许在前面和后面插入元素,而不仅仅是在最后面。B. 错误。
std::list
是一个双向链表(双向链表结构),不是单链表。C. 正确。在
std::vector
中删除中间的元素会导致元素后面的所有元素向前移动,这样所有指向这些元素的迭代器都会失效。D. 错误。
std::map
基于红黑树(或其他自平衡的二叉搜索树)实现,而不是哈希表。哈希表实现的容器是std::unordered_map
。
5. 以下关于多线程的说法,正确的是()
A. 线程之间栈是公有的 B. 线程之间堆是公有的
C. 读写锁可实现线程同步 D. 信号量可实现线程同步
解析:
A. 错误。线程之间的栈是私有的。每个线程都有自己的栈,用于存储局部变量和函数调用信息。
B. 正确。线程之间的堆是公有的。多个线程可以共享进程的堆区域,它们可以在堆中分配和访问共享的数据。
C. 正确。读写锁(如
std::shared_mutex
)可以实现线程同步,允许多个线程并行读访问,而写访问是互斥的。D. 正确。信号量(如
std::semaphore
)也可以实现线程同步,控制线程对共享资源的访问。
6. 数据结构中的线性结构有()
解析:
- 数组(Array):固定大小的顺序存储结构,可以通过索引直接访问元素。
- 链表(Linked List):由节点组成的链式存储结构,每个节点包含数据和指向下一个节点的指针。
- 单链表(Singly Linked List):每个节点只包含一个指向下一个节点的指针。
- 双链表(Doubly Linked List):每个节点包含两个指针,一个指向下一个节点,另一个指向前一个节点。
- 循环链表(Circular Linked List):链表的最后一个节点指向第一个节点,形成一个环。
- 栈(Stack):遵循“后进先出”(LIFO)原则的线性数据结构,支持在一端插入和删除元素。
- 队列(Queue):遵循“先进先出”(FIFO)原则的线性数据结构,支持在一端插入,在另一端删除元素。
- 双端队列(Deque):允许在两端插入和删除元素的队列。
其他
1. std::shared_ptr的引用计数是线程安全的
2. 对于任意一颗二叉树,如何确定这颗二叉树的结构:中序+前/后
编程
第一题:
解答:100%AC
#include <iostream>
#include <unordered_map>
#include <vector>
#include <algorithm>int main() {// 每个机场无人机的数量std::unordered_map<int, int> airportDrones;// 降落数int numLandings;std::cin >> numLandings;for (int i = 0; i < numLandings; ++i) {int airport;std::cin >> airport;// Increment the number of drones at the airportairportDrones[airport]++;}// 起飞数int numTakeoffs;std::cin >> numTakeoffs;for (int i = 0; i < numTakeoffs; ++i) {int airport;std::cin >> airport;// 把起飞对应的机场无人机数-1if (airportDrones[airport] > 0) {airportDrones[airport]--;// 如果机场没有无人机 移除if (airportDrones[airport] == 0) {airportDrones.erase(airport);}}}// Count the number of airports with each number of drones (1 to 15)std::vector<int> countDistribution(15, 0);for (const auto& pair : airportDrones) {int count = pair.second;if (count > 0 && count <= 15) {countDistribution[count - 1]++;}}// Output the resultfor (int i = 0; i < 15; ++i) {std::cout << countDistribution[i] << " ";}std::cout << std::endl;return 0;
}
第二题:
解答:75%AC
#include <iostream>
#include <deque>
#include <string>int main() {std::string s;std::cin >> s;std::deque<char> dq(s.begin(), s.end());int steps = 0;while (1) {bool replaced = false;std::deque<char> new_dq;while (!dq.empty()) {if (dq.size() >= 2 && dq[0] == '0' && dq[1] == '1') {new_dq.push_back('1');new_dq.push_back('0');dq.pop_front(); // Remove '0'dq.pop_front(); // Remove '1'replaced = true;} else {new_dq.push_back(dq.front());dq.pop_front();}}if (!replaced) {break;}dq = new_dq;steps++;}std::cout << steps << std::endl;return 0;
}