目录一、C 基础核心5 题1. 简述 C 中const的作用2. new/delete 和 malloc/free 的区别3. 什么是浅拷贝和深拷贝如何避免浅拷贝问题4. C 中static关键字的作用5. 虚函数的作用纯虚函数是什么二、内存管理3 题6. C 程序内存分区有哪些7. 什么是内存泄漏如何避免8. delete和delete[]的区别三、并发与多线程4 题9. 进程和线程的区别10. 什么是线程安全如何实现线程安全11. 死锁的条件如何避免死锁12. 简述 C11 std::thread用法四、网络编程基础4 题13. TCP 和 UDP 的区别14. TCP 三次握手、四次挥手流程15. 简述 Socket 编程 TCP 服务端流程16. 什么是高并发C 服务器如何处理高并发五、数据结构 算法 Linux 基础4 题17. 数组和链表的区别18. 手写冒泡排序C19. 常用 Linux 命令服务器开发必备20. 什么是字节序网络字节序是什么前言在后端开发领域C 凭借高性能、高并发、低内存开销的特性长期占据游戏服务器、中间件、网络通信、金融交易等核心场景。对于初级 C 服务器工程师而言面试核心考察点集中在C 基础语法与特性、网络编程基础、多线程 / 并发、内存管理、数据结构与算法、Linux 基础、简单服务器实现等。本文整理了20 道高频面试题覆盖理论、代码、实战附带标准答案 可运行代码示例适合初级工程师面试准备、查漏补缺也适合企业面试官直接使用。所有题目均贴合企业真实招聘需求干货满满建议收藏一、C 基础核心5 题1. 简述 C 中const的作用标准答案修饰变量定义常量值不可修改编译器会进行编译期检查修饰指针分为常量指针const int* p指针指向的值不可改、指针常量int* const p指针本身地址不可改、常量指针常量const int* const p值和地址都不可改修饰函数参数防止函数内部修改传入的参数值修饰成员函数承诺函数不会修改类的成员变量const 对象只能调用 const 成员函数修饰函数返回值限制返回值被修改。代码示例cpp运行#include iostream using namespace std; class Test { int num 10; public: // const成员函数不能修改成员变量 int getNum() const { // num 20; 编译报错 return num; } }; int main() { // 1. const常量 const int a 10; // a 20; 编译报错 // 2. 常量指针值不可改地址可改 const int* p1 a; // *p1 20; 报错 int b 30; p1 b; // 3. 指针常量地址不可改值可改 int* const p2 b; *p2 40; // p2 a; 报错 Test t; cout t.getNum() endl; return 0; }2.new/delete和malloc/free的区别标准答案属性new/delete是 C关键字 / 运算符malloc/free是 C 标准库函数功能new会调用构造函数初始化对象delete会调用析构函数释放资源malloc/free仅分配 / 释放内存不调用构造 / 析构函数参数new无需指定内存大小编译器自动计算malloc需要手动指定字节数返回值new返回对应类型指针无需强转malloc返回void*必须强转内存分配失败new抛出bad_alloc异常malloc返回NULL重载new/delete可以重载malloc/free不可重载。代码示例cpp运行#include iostream #include cstdlib using namespace std; class Test { public: Test() { cout 构造函数调用 endl; } ~Test() { cout 析构函数调用 endl; } }; int main() { // new分配内存 调用构造函数 Test* t1 new Test(); // delete调用析构函数 释放内存 delete t1; cout --------- endl; // malloc仅分配内存 Test* t2 (Test*)malloc(sizeof(Test)); // free仅释放内存 free(t2); return 0; }3. 什么是浅拷贝和深拷贝如何避免浅拷贝问题标准答案浅拷贝默认拷贝构造函数 / 赋值运算符重载仅拷贝指针地址多个对象指向同一块内存会导致重复释放内存、野指针深拷贝重新分配内存拷贝指针指向的内容每个对象拥有独立内存避免内存冲突避免方案当类中有指针成员时必须手动实现拷贝构造函数 赋值运算符重载完成深拷贝。代码示例cpp运行#include iostream #include cstring using namespace std; class String { char* m_data; public: // 构造函数 String(const char* str ) { m_data new char[strlen(str) 1]; strcpy(m_data, str); } // 深拷贝拷贝构造函数 String(const String other) { m_data new char[strlen(other.m_data) 1]; strcpy(m_data, other.m_data); } // 深拷贝赋值运算符重载 String operator(const String other) { if (this other) return *this; // 释放原有内存 delete[] m_data; // 重新分配内存并拷贝 m_data new char[strlen(other.m_data) 1]; strcpy(m_data, other.m_data); return *this; } ~String() { delete[] m_data; } const char* c_str() const { return m_data; } }; int main() { String s1(hello); String s2 s1; // 深拷贝无内存问题 cout s2.c_str() endl; return 0; }4. C 中static关键字的作用标准答案静态全局变量作用域仅限当前文件其他文件无法访问生命周期为程序全程静态局部变量函数内定义仅初始化一次生命周期延长到程序结束作用域仅限函数内静态成员变量属于类不属于任何对象所有对象共享必须在类外初始化静态成员函数属于类无this指针只能访问静态成员变量 / 函数无需创建对象即可调用。代码示例cpp运行#include iostream using namespace std; class Test { public: static int count; // 静态成员变量 static void add() { count; } // 静态成员函数 }; // 类外初始化静态成员变量 int Test::count 0; int main() { Test::add(); // 直接调用静态函数 Test::add(); cout Test::count endl; // 输出2 return 0; }5. 虚函数的作用纯虚函数是什么标准答案虚函数使用virtual修饰的成员函数实现多态父类指针 / 引用指向子类对象调用子类重写的函数纯虚函数virtual 返回值类型 函数名() 0;没有函数体包含纯虚函数的类是抽象类不能实例化子类必须重写纯虚函数才能实例化虚函数原理类中生成虚函数表vtable对象中存储虚指针vptr运行时动态查找函数地址。代码示例cpp运行#include iostream using namespace std; // 抽象类含纯虚函数 class Animal { public: virtual void speak() 0; // 纯虚函数 }; class Dog : public Animal { public: void speak() override { cout 汪汪汪 endl; } }; class Cat : public Animal { public: void speak() override { cout 喵喵喵 endl; } }; int main() { Animal* a new Dog(); a-speak(); // 多态调用Dog的speak a new Cat(); a-speak(); delete a; return 0; }二、内存管理3 题6. C 程序内存分区有哪些标准答案栈区stack存放局部变量、函数参数由编译器自动分配释放内存连续速度快堆区heapnew/malloc分配的内存手动释放内存不连续易产生碎片全局 / 静态区存放全局变量、静态变量程序结束后系统释放常量区存放字符串常量、const 全局变量只读不可修改代码区存放程序二进制代码只读共享。7. 什么是内存泄漏如何避免标准答案内存泄漏堆区分配的内存使用后没有释放导致系统可用内存持续减少常见场景new后忘记delete、指针重新赋值导致原内存地址丢失、异常跳转导致释放代码未执行避免方案成对使用new/delete、new[]/delete[]使用智能指针unique_ptr/shared_ptr自动管理内存代码规范资源申请后立即写释放逻辑。代码示例智能指针避免泄漏cpp运行#include iostream #include memory // 智能指针头文件 using namespace std; int main() { // unique_ptr独占所有权自动释放 unique_ptrint p1 make_uniqueint(10); cout *p1 endl; // shared_ptr共享所有权引用计数为0自动释放 shared_ptrint p2 make_sharedint(20); shared_ptrint p3 p2; cout *p2 p2.use_count() endl; // 输出20 2 // 无需手动delete智能指针自动释放内存 return 0; }8.delete和delete[]的区别标准答案delete用于释放单个对象的堆内存调用一次析构函数delete[]用于释放数组对象的堆内存调用数组中所有元素的析构函数混用后果new[]用delete释放会导致内存泄漏、析构函数不完整调用。代码示例cpp运行#include iostream using namespace std; class Test { public: Test() { cout 构造 endl; } ~Test() { cout 析构 endl; } }; int main() { // 单个对象用delete Test* t1 new Test(); delete t1; // 对象数组用delete[] Test* t2 new Test[3]; delete[] t2; return 0; }三、并发与多线程4 题9. 进程和线程的区别标准答案进程资源分配的最小单位拥有独立的地址空间、文件描述符等进程间切换开销大线程CPU 调度的最小单位共享进程的资源内存、文件有独立栈空间线程切换开销小一个进程可以包含多个线程进程崩溃不影响其他进程线程崩溃会导致整个进程崩溃。10. 什么是线程安全如何实现线程安全标准答案线程安全多线程同时访问共享资源时不会出现数据混乱、逻辑错误实现方案互斥锁mutex同一时间只允许一个线程访问资源原子操作atomic类型无需加锁即可保证操作原子性条件变量线程间同步等待避免使用全局共享变量。代码示例互斥锁实现线程安全cpp运行#include iostream #include thread #include mutex using namespace std; int num 0; mutex mtx; // 互斥锁 void add() { for (int i 0; i 100000; i) { mtx.lock(); // 加锁 num; mtx.unlock(); // 解锁 } } int main() { thread t1(add); thread t2(add); t1.join(); t2.join(); cout num endl; // 线程安全输出200000 return 0; }11. 死锁的条件如何避免死锁标准答案死锁四个必要条件同时满足才会发生互斥条件资源独占请求保持持有资源并等待其他资源不可剥夺资源不能被强行抢占循环等待线程形成环形等待链。避免方案破坏循环等待统一锁的获取顺序避免持有锁时请求其他锁使用定时锁减少锁的粒度。12. 简述 C11std::thread用法标准答案包含头文件thread创建线程thread t(函数名, 参数)join()主线程等待子线程执行完毕再继续detach()子线程分离主线程不等待子线程后台运行注意不要访问主线程已释放的资源。代码示例cpp运行#include iostream #include thread using namespace std; void func(int a, string s) { cout 线程执行 a s endl; } int main() { // 创建线程 thread t(func, 10, hello); // 等待线程结束 t.join(); cout 主线程结束 endl; return 0; }四、网络编程基础4 题13. TCP 和 UDP 的区别标准答案表格特性TCPUDP连接性面向连接无连接可靠性可靠传输确认、重传、有序不可靠不保证到达、有序传输效率慢开销大快开销小适用场景文件传输、HTTP、服务器通信视频直播、游戏、DNS数据单位字节流数据报14. TCP 三次握手、四次挥手流程标准答案三次握手建立连接客户端 → 服务端SYN 包请求连接服务端 → 客户端SYNACK 包同意连接客户端 → 服务端ACK 包确认连接作用确保双方收发能力正常避免无效连接。四次挥手断开连接客户端 → 服务端FIN 包请求关闭服务端 → 客户端ACK 包确认关闭服务端 → 客户端FIN 包服务端关闭客户端 → 服务端ACK 包确认关闭原因TCP 是全双工双方都需要单独关闭。15. 简述 Socket 编程 TCP 服务端流程标准答案创建套接字socket()绑定 IP 和端口bind()监听连接listen()接受客户端连接accept()收发数据recv()/send()关闭套接字close()。代码示例Linux 下 TCP 简易服务端cpp运行#include iostream #include sys/socket.h #include netinet/in.h #include unistd.h using namespace std; int main() { // 1. 创建socket int server_fd socket(AF_INET, SOCK_STREAM, 0); // 2. 绑定IP和端口 struct sockaddr_in addr; addr.sin_family AF_INET; addr.sin_port htons(8888); addr.sin_addr.s_addr INADDR_ANY; bind(server_fd, (struct sockaddr*)addr, sizeof(addr)); // 3. 监听 listen(server_fd, 5); cout 等待客户端连接... endl; // 4. 接受连接 int client_fd accept(server_fd, nullptr, nullptr); cout 客户端已连接 endl; // 5. 收发数据 char buffer[1024] {0}; recv(client_fd, buffer, 1024, 0); cout 收到 buffer endl; send(client_fd, hello client, 12, 0); // 6. 关闭 close(client_fd); close(server_fd); return 0; }16. 什么是高并发C 服务器如何处理高并发标准答案高并发服务器同时处理大量客户端连接请求处理方案I/O 多路复用select/poll/epollLinux 首选 epoll线程池 / 进程池避免频繁创建销毁线程事件驱动模型Reactor无锁编程、内存池、对象池优化性能。五、数据结构 算法 Linux 基础4 题17. 数组和链表的区别标准答案数组内存连续支持随机访问查询快O (1)插入 / 删除慢O (n)大小固定链表内存不连续不支持随机访问查询慢O (n)插入 / 删除快O (1)大小动态扩展。18. 手写冒泡排序C标准答案 代码cpp运行#include iostream using namespace std; // 冒泡排序 void bubbleSort(int arr[], int n) { for (int i 0; i n-1; i) { for (int j 0; j n-i-1; j) { if (arr[j] arr[j1]) { // 交换 int temp arr[j]; arr[j] arr[j1]; arr[j1] temp; } } } } int main() { int arr[] {3,1,4,2,5}; int n sizeof(arr)/sizeof(arr[0]); bubbleSort(arr, n); for(int i0; in; i) cout arr[i] ; return 0; }19. 常用 Linux 命令服务器开发必备标准答案进程ps -ef查看进程、top实时进程、kill杀死进程网络netstat -anp查看端口、ifconfig网卡、telnet测试端口文件ls、cd、rm、grep查找、tar解压权限chmod、chown日志tail -f 日志文件实时查看日志。20. 什么是字节序网络字节序是什么标准答案字节序多字节数据在内存中的存储顺序大端序高字节存低地址网络字节序小端序低字节存低地址主机常用网络传输必须使用网络字节序大端C 使用htons/htonl转换为主机序ntohs/ntohl转回网络序。