数据结构面试题

1、简述以下算法的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void BB(LNode *b,LNode *a)
{
LNode *t = b;
while(t->next != a)
{
t = t->next;
}
t->next = b;
}

void AA(LNode *a,LNode *b)
{
BB(a,b);
BB(b,a);
}

BB函数的功能是将

C语言

1.堆和栈的区别

  • C语言中堆和栈是两种用于管理内存的两个不同区域。

2.extern修饰的变量有什么作用,有哪些注意事项?

  1. 作用:声明外部变量或函数,用于在一个文件内声明在其他文件定义的全局变量或函数,以便于在此文件内使用。
  2. 注意事项:extern只是声明不是定义,不能在extern处初始化,需要在文件中定义变量或函数。

3.arr&arr的区别

  1. arr通常是一个数组名,而&arr是一个指针,指向的是数组第一个元素的地址。
  2. 使用sizeof计算大小时,arr得到整个数组的大小,&arr代表指针大小
  3. arr+1代表数组第二个元素,&arr+1是数组最后一个元素地址的下一个地址

4.一个结构体中可以包含指向自己的指针吗?

可以,例如链表

5.指针数组、数组指针、函数指针和指针函数的定义,以及他们应用于何种场景。

  1. 指针数组:int *a[n],本质上是一个数组,这个数组内存放的是指针类型的成员,适用于存储一组指针.
  2. 数组指针:int (*a)[n],本质上是一个指针,指向一个数组,适用于处理多维数组,或用来传递整个数组.
  3. 函数指针:int (*a)(int b)本质上是一个指针,指向一个函数,常用于作为回调函数,或者在运行时选择调用不同的函数.
  4. 指针函数:int *a(int b)本质上是一个函数,返回值是一个指针,例如返回动态分配的内存函数.

6.在scanf进行终端输入时,如何清空缓冲区内的所有内容?

scanf用于获取终端指定格式的内容,可以用getchar函数循环吸收垃圾字符.

7.#include <>#include ""有什么区别,在定义自己的头文件时,如何防止头文件的重复包含?

  1. <>代表默认在标准库里找头文件,而""代表优先在当前工程目录找头文件.
  2. 可以在头文件里加上#ifndef#define#endif等预处理指令来防止重复包含.

8.数组和指针的区别是什么

  1. 从数据类型上,数组时存储一串相同元素的集合,而指针是一种存储地址类型的变量
  2. 内存分配上,数组在声明时需要指定大小,编译时分配固定大小的内存。而指针可以在运行时动态分配内存。
  3. 地址操作上,数组名代表了数组首元素的地址,但数组名本身是常量,不能被修改。而指针变量存储的地址可以通过指针进行间接访问和修改所指对象的值。

9.当p是指针时,if(p)是合理的表达式吗?

合理的,用来判断p是否为NULL

10.gcc的分步编译

  1. 预处理:展开头文件,替换宏定义,删除注释
  2. 编译:检查语法正确性,生成汇编文件
  3. 汇编:将汇编文件生成目标文件
  4. 链接:链接到程序所需要的库,生成可执行文件

11.gotowhilefor循环的区别

goto语句一般用来跳转到程序某个指定位置,由于会造成程序混乱,所以很少使用,而whilefor是C语言实现循环的一种语法

12.auto修饰变量的用途

默认在函数内定义的局部变量就是auto类型的。

auto意味着当前变量的作用域为当前函数或代码的局部变量。

13.设计一个函数,判断计算机是否是大端存储

大端存储和小端存储是描述多字节数据在计算机内存中存储顺序的两种方式:

  • 大端存储(Big-Endian): 最高有效字节存储在最低地址,最低有效字节存储在最高地址。例如,对于16位整数 0x1234,大端存储的方式是地址0:12,地址1:34。
  • 小端存储(Little-Endian): 最低有效字节存储在最低地址,最高有效字节存储在最高地址。例如,对于16位整数 0x1234,小端存储的方式是地址0:34,地址1:12。

选择大端还是小端存储取决于计算机体系结构的设计。x86架构是小端存储的代表,而一些其他体系结构如SPARC、PowerPC等则使用大端存储。在网络通信和数据存储时,需要考虑不同机器之间的字节序问题,常用网络字节序(Big-Endian)来确保数据正确解释和处理。在C语言中,可以使用相关函数进行字节序的转换,以适应不同的系统。

1
2
3
4
5
6
7
8
9
10
11
12
13
void fun()
{
int a = 0x1234;
char *p = (char *)&a;
if(p == 0x34)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
}

14.段错误发生的原因

段错误就是程序在运行时不小心触碰到了一些不该碰的地方,比如访问了不存在的内存或者使用了已经被释放的内存。这通常发生在代码中有一些错误,比如使用了空指针、访问了数组越界、释放了同一块内存两次等等。要解决段错误,就需要检查代码,确保在访问内存时不会越界、使用空指针,以及确保内存的分配和释放是正确的。简而言之,段错误就是程序在操作内存时搞错了,需要通过仔细检查代码来找到并修复问题。

15.简述函数传参时,值传递和地址传递的区别

值传递: 当你把一个东西(比如一个数字)给函数时,实际上函数得到的是这个东西的一个复制品。就好像你借给朋友一本书,你自己的书还在那,朋友得到的只是一本一样的书。

地址传递: 当你把一个东西(比如一个数字)的地址给函数时,函数可以直接去那个地址找到这个东西。这就好像你告诉朋友你书的存放位置,朋友可以直接去那里找到这本书。

总的来说,值传递是传递东西的复制,而地址传递是传递东西的位置。

C高级

1. 什么是 shell 脚本:

Shell脚本是一种由一系列Shell命令组成的文本文件,它通过Shell解释器执行。这些脚本可以包含变量、条件语句、循环等,用于自动化执行一系列的命令,操作文件系统,以及实现系统管理和任务。

2. Shell 脚本执行的三种方式:

  1. 交互式执行:

    • 用户逐行输入Shell命令,由Shell即时解释执行。
  2. 批处理执行:

    • 将一系列Shell命令保存到文件中,通过执行脚本文件实现自动化操作。
  3. 在命令行中执行:

    • 直接在命令行中输入Shell命令,实时执行。

3. 以上执行脚本的三种方式的区别:

  • 交互式执行:

    • 逐行输入,即时执行。
  • 批处理执行:

    • 通过脚本文件一次性执行一系列命令,可实现脚本的复用。
  • 在命令行中执行:

    • 可以逐行输入或复制粘贴执行命令,适合快速验证和调试。

4. 如何修改 Linux 系统下 PATH 的路径:

可以通过在用户的~/.bashrc~/.bash_profile文件中添加如下行来修改PATH的路径:

1
export PATH=/new/path:$PATH

然后执行 source ~/.bashrcsource ~/.bash_profile 使修改生效。

5. 什么是 Makefile

Makefile是一个用于管理程序编译的文件,其中包含了编译规则、依赖关系和执行命令等信息。它通常与make工具一起使用,用于自动构建和更新项目。

6. 在编写 Makefile 时有哪些注意事项:

  • 使用Tab键缩进,而不是空格。
  • 明确指定目标、依赖和命令的关系。
  • 使用变量提高可维护性。
  • 尽量避免使用绝对路径。
  • 利用自动变量简化规则。

7. Makefile 编译文件的原理:

Makefile通过检查目标文件和依赖关系的时间戳来确定哪些文件需要重新编译。如果目标文件不存在或者其依赖文件的时间戳较新,那么make会执行相应的命令进行编译。

8. Shell 中实现算数运算的方式:

在Shell中,可以使用$((expression))进行算术运算,例如:

1
2
result=$((3 + 5))
echo $result

9. Shell 中如何获取来自终端的输入:

可以使用read命令,例如:

1
2
3
echo "请输入姓名:"
read name
echo "你的姓名是:$name"

10. Shell 中命令置换符的使用:

可以使用反引号(`)或$()将命令置于其中,执行该命令并将结果返回。例如:

1
2
3
result=`ls`
# 或者
result=$(ls)

11. 简述一下 inode:

inode是文件系统中的一个数据结构,包含有关文件或目录的元数据信息,如文件大小、所有者、权限、时间戳等。每个文件或目录在文件系统中都有一个唯一的inode号。

12. cut 和 grep 在 Shell 中的作用:

  • cut用于从文本行中剪切出指定的字段,常用于处理文本数据。
  • grep用于在文本中搜索匹配的模式,可用于过滤和查找文本内容。

13. typedef 和 #define 的区别:

  • typedef用于创建新的数据类型别名,提高代码可读性。例如:typedef int Integer;
  • #define用于创建预处理器宏,将标识符替换为特定的文本。例如:#define MAX_SIZE 100typedef在编译时创建新类型,而#define只是简单的文本替换。

C++面试题

1.C++中函数参数传递的方式有哪些?

  1. 按值传递,意味着我们将参数的值传递给函数,这样做的话,函数内对参数的任何修改都不会反应到调用它的地方。
  2. 按引用传递,传递的是参数的引用,允许函数修改调用者传递的变量,这种方式可以修改原始数据

2.C++中的动态内存分配和 C 语言中的动态内存分配区别

new、delete和malloc、free的区别

3. C++中的结构体和 C 语言的结构体的区别:

在C++中,结构体与C语言中的结构体相比,有以下区别:

  • 成员函数:
    • C++中的结构体可以包含成员函数,使其更接近于类的概念。
    • C语言的结构体只能包含数据成员,不能包含成员函数。
  • 访问控制:
    • 在C++中,结构体的成员可以具有访问控制关键字(public、private、protected),以实现封装。
    • 在C语言中,结构体的成员默认是公有的,没有访问控制的概念。
  • 继承:
    • 在C++中的结构体可以通过继承来扩展和复用代码。
    • 在C语言中,结构体无法继承其他结构体。

4. 深拷贝和浅拷贝:

  • 浅拷贝:
    • 浅拷贝是指将一个对象的值复制到另一个对象,如果对象包含指针类型的成员变量,仅复制指针而不复制指针指向的数据。
    • 可能导致两个对象共享相同的内存,一方修改数据会影响另一方。
  • 深拷贝:
    • 深拷贝是指将一个对象的值复制到另一个对象,并且递归地复制所有指针指向的数据。
    • 确保两个对象彼此独立,修改其中一个不会影响另一个。

5. C++中模板的实现机制:

C++中的模板是一种泛型编程的方式,通过模板可以编写与数据类型无关的代码。模板的实现机制包括两个主要部分:

  • 编译时实例化: 在编译时,编译器根据模板生成实际的代码。这样可以提高执行效率,同时保留了泛型的灵活性。
  • 模板特化和部分特化: 允许程序员为特定类型提供特定的实现,以覆盖模板的通用实现。

6. C++中实现多态的条件:

实现多态的条件包括:

  • 继承: 派生类继承基类。
  • 虚函数: 在基类中声明虚函数,并在派生类中重写它们。
  • 指针或引用: 使用基类指针或引用来引用派生类对象。
  • 运行时绑定: 使用虚函数时,调用的函数在运行时确定,而不是在编译时。

7. 菱形继承问题以及解决方案:

菱形继承问题指的是一个类同时继承自两个拥有共同基类的类,形成类似菱形的继承结构,可能导致二义性和资源浪费。解决方案包括:

  • 虚继承: 在继承路径中使用virtual关键字,确保共同的基类只被继承一次,解决二义性和资源浪费问题。

8. 面向对象的三大特征:

面向对象的三大特征包括:

  • 封装(Encapsulation): 将数据和操作封装在一个单元内,实现数据隐藏和访问控制。
  • 继承(Inheritance): 允许一个类继承另一个类的属性和方法,实现代码的重用和扩展。
  • 多态(Polymorphism): 允许不同类的对象对同一消息作出不同的响应,包括编译时多态和运行时多态。

Linux系统

1.什么是链接?符号链接与硬链接的区别是什么?

2.请简述gcc的分步编译,以及Makefile的分布编译

3.现有目录dir、dir1,文件file、file1,请实现归档和拆包(使用三种压缩方式)

4.请简述GDB调试二进制文件的过程。

5.shell中的算数运算符有哪些?

6.什么是内存泄漏?内存溢出?如何避免?

内存泄漏是指当程序申请内存后,没有释放已经申请的内存空间。内存溢出是指当程序申请内存时,系统没有足够的内存空间供其使用,导致无法申请内存。

  • 内存泄漏(Memory Leak)是指程序在申请内存后,无法释放已申请的内存空间。在程序运行过程中,如果持续产生内存泄漏,会导致系统内存的浪费,从而减慢程序运行速度,甚至可能导致系统崩溃。内存泄漏通常是由于程序员的错误导致程序不能释放不再使用的内存。
  • 内存溢出(Out of Memory)是指程序在申请内存时,没有足够的内存空间供其使用,导致无法申请到所需内存。内存溢出可能是由于请求的内存量超过了系统能提供的内存量,或是堆内存中对象数量超过了最大堆容量限制。内存溢出的解决通常涉及增加程序可用的内存量或优化程序以减少内存需求.

数据结构

1.数据结构中查找有哪些,是怎么实现的?

1.栈和队列的区别

  • 栈是一种先进后出的结构,而队列是一种先进先出的结构
  • 栈只有一端可以操作,队列两端都可以操作

2.逻辑结构和物理结构有哪些

  • 逻辑结构:集合结构,线性结构,树结构、图结构
  • 物理结构:顺序结构、链式结构、散列结构、索引存储

3.递归结构

4.链表和顺序表的区别

链表和顺序表都是逻辑连续的存储结构,顺序表的物理结构也是连续的,但是顺序表不一定连续。链表每个节点有两个域,一个数据域一个指针域

IO进程线程

1.进程和线程的区别

  • 进程和线程的颗粒度不一样,线程是任务分配的最小单位,进程是资源分配的最小单位
  • 创建方式不一样,进程用fork函数创建,而线程是用pthreat创建
  • 他们都能实现多任务的并发执行,方式都是时间片轮询
  • 每个进程的资源是相互独立的,而在同一个进程里的线程他们的资源是共享的

2.什么是标准IO,什么是文件IO,他们的区别是什么?

  • 标准IO是依赖于标准库的一系列输入输出函数集合,里面封装了一个缓冲区,当达到刷新缓冲区条件时,标准IO会调用内核相关函数,由于有缓冲区的存在,标准IO的效率高
  • 文件IO是系统内核提供的函数,直接调用系统内核,效率比较低
  • 标准IO=文件IO+缓冲区

3.为什么要引入线程的同步互斥机制?如何实现同步互斥?

  • 由于存在临界资源,每个线程对临界资源进行操作时,会产生竞争,为了保护临界资源就引入了同步互斥机制

  • 同步的实现方式有:无名信号量、条件变量读写锁

    • 无名信号量:本质上也是一个临界资源,维护了一个value值,当每个线程执行时,先申请这个value值,申请成功则value值减一,如果申请时value值为0,则线程在申请处阻塞,直到其他线程将value值增加。线程同步机制多用于生产者-消费者模型。
    • 条件变量:本质上也是一个临界资源,维护了一个队列,当每个线程执行时先进入等待队列,直到生产者唤醒才能执行。
  • 互斥的实现方式有:互斥锁

4.请画出进程状态的转换图,并且标注上转换原因

进程主要的状态一共有五种:创建态、就绪态、运行态、阻塞态、死亡态

image-20240111101215202

5.进程间通信方式有哪些?分别作详细描述

  1. 通过管道:

    • 管道是在内核空间创建的一个特殊文件
    • 管道分为有名管道和无名管道,无名管道只能在情缘进程间通信
    • 管道里面的数据是一次性的,读过一次后就没有了
    • 通过一个管道实现的通信是半双工通信
    • 管道遵循先进先出的原则
    • 管道创建后有两端,读端和写端,当两端都关闭后,管道文件就消失了
    • 管道由于存在于内核空间,所以只能用文件IO来操作
  2. 通过信号

  • 信号是软件模拟的一种中断
  • 进程和进程之间可以发信号,终端可以向进程发信号,内核也可以向进程发送信号
  • 信号只能起到通知的作用,不能传输信息
  1. System V通信
    • 消息队列:内核空间维护的一个队列,所有进程都能写和读里面的内容
    • 共享内存:在内核空间映射的一块物理内存,通过这种方式进行的通信时效性最快
    • 信号量(信号灯集):实现了进程间的同步,将多个信号量放入一个集合中,每个信号量控制一个进程

6.什么是僵尸进程、孤儿进程、守护进程?

  1. 僵尸进程是一个进程结束了,但是其父进程没有进行资源回收的进程
  2. 孤儿进程是,一个进程还没有结束时,其父进程已经结束了的进程,会被init进程收养
  3. 守护进程是脱离终端存在的进程,随着系统的启动而运行,随着系统的关闭而结束

7.描述一下创建子进程时的写时拷贝技术

当父进程刚创建子进程时,父子进程公用同一块内存空间,当父子进程对数据进行操作时,子进程会单独开辟一块内存空间

8.谈谈你对并发和并行的理解

并发是指同一个处理器处理多个任务,并行是指多个处理器同时处理多个任务。

9.IO多路复用实现的思路?

10.selectpollepoll区别?

网络通信

1.简述OSI七层网络体系结构

物理层:负责在物理媒介上传输比特流,例如电缆、光纤

数据层:负责在直连的节点之间传输数据帧,提供物理地址定位、错误检测和纠正功能

网络层:负责处理网络上逻辑寻址和路由选择,将数据从源节点传到目标节点

传输层:负责对数据进行分段和重组,并提供端到端的可靠数据传输,如TCP、UDP协议

会话层:负责建立、管理和终止会话。

表示层:负责数据格式转换,加密和解密、压缩和解压缩等功能

应用层:为用户提供各种网络应用接口,如文件传输、电子邮件、远程登录等

2.网络结构分层的好处

  1. 各层之间独立
  2. 稳定,灵活性好
  3. 易于实现和维护
  4. 促进标准化工作
  5. 结构上不可分开,每层都可以采用最适合的技术来实现

3.简述TCP/IP四层网络体系结构

  1. 链路层(网络接口和物理层)
  2. 网络层
  3. 传输层
  4. 应用层

4.每一层中重要的协议有哪些?

5.路由器工作在哪一层?交换机呢?

6.TCP和UDP的区别

7.简述TCP服务器端的实现

8.ISO七层网络通信结构和TCP/IP四层网络通信结构

9.tcp通信的优缺点

10.udp通信的优缺点

11.pool与select的区别

12.io模型有哪几种

13.如何实现tcp并发服务器

14.网络超时检测的本质和实现方式

15.tcp网络编程流程

16.udp网络编程流程

17.udp本地通信需要注意哪些方面

18.怎么修改文件描述符的标志位

sglite数据库的基本使用,包括增删改查

基于udp的聊天室如何实现数据群发

在线词典如何实现查询单词

TCP和UDP的区别

OSI七层网络模式,每层的主要作用,主要的协议

OSI的四层和五层网终模型

TCP的三次握手和四次挥手分别作用,主要做什么

如何实现并发服务器,并发服务器的实现方式以及有什么异同

image-20240122162307073

image-20240123163908522

程序题:

1
2
3
4
5
6
7
8
9
10
11
12
int f(int x)
{
int num = 0;
while (x)
{
num++;
x = x & (x - 1);
}

return num;
}
那么,f(9475)的输出结果为____.

TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)是两种不同的传输层协议,用于在计算机网络中进行数据传输。 以下是它们之间的一些主要区别:

连接性:

TCP: 提供面向连接的服务。 在数据传输之前,必须先建立一个连接,然后进行数据传输,最后再释放连接。 它确保数据的可靠性和有序性。
UDP: 提供无连接的服务。 数据可以直接发送到目标,而不需要事先建立连接。 它不保证数据的可靠性和有序性。
可靠性:

TCP: 提供可靠的数据传输。 它使用确认机制来确保数据的正确传输,如果某个数据包丢失,将会重新发送。
UDP: 不提供可靠性保证。 数据发送后,不会收到确认,也不会重新发送丢失的数据包。
有序性:

TCP: 提供有序的数据传输。 发送的数据包将按照顺序被接收和传递。
UDP: 不保证数据包的有序性。 发送的数据包的顺序不一定会按照发送的顺序被接收。
流量控制:

TCP: 提供流量控制机制,以确保发送方不会以过快的速度向接收方发送数据,防止网络拥塞。
UDP: 没有内建的流量控制机制,发送方可以以任意速度发送数据。
适用场景:

TCP: 适用于需要可靠数据传输的应用,如文件传输、网页浏览等。
UDP: 适用于对实时性要求较高,能够容忍一定数据丢失的应用,如语音通话、视频流等。changj

select 函数是一种多路复用的机制,通常用于同时监听多个文件描述符(sockets)的可读、可写和异常等事件。它的原理基于操作系统提供的 I/O 复用机制,允许一个进程同时监视多个文件描述符的状态,当其中某个文件描述符就绪时,就通知程序进行相应的处理。

以下是 select 函数的基本原理:

  1. 文件描述符集合的准备:

    • 调用者需要准备三个文件描述符集合,分别用于监视可读、可写和异常事件。
    • 通过 FD_ZEROFD_SET 等宏来初始化和设置这些文件描述符集合。
  2. 调用 select 函数:

    • 调用 select 函数并传入最大文件描述符值加一(通常是最大的文件描述符加一),以及要监视的三个文件描述符集合。
    • select 函数将阻塞等待,直到有文件描述符就绪或超时。
  3. 检查文件描述符状态:

    • select 函数返回后,通过检查文件描述符集合的状态来确定哪些文件描述符已经就绪。
    • 如果一个文件描述符在可读集合中,表示该文件描述符上有数据可读。
    • 如果在可写集合中,表示该文件描述符可以接受数据。
    • 如果在异常集合中,表示该文件描述符发生了异常。
  4. 处理就绪的文件描述符:

    • 根据就绪的文件描述符进行相应的处理,比如读取数据、写入数据或处理异常。
    • 如果使用非阻塞模式,可以通过 errno 的返回值来判断是否阻塞。
  5. 循环调用 select

    • 可以在需要的情况下循环调用 select,以继续监听文件描述符的状态变化。

多路复用的好处在于能够在一个线程中同时处理多个 I/O 操作,而不需要为每个文件描述符创建一个独立的线程。这样可以更有效地利用系统资源,并提高程序的性能。 select 是一种比较传统的多路复用方法,在一些现代系统中也有其他更高效的替代方案,如 pollepoll(在Linux系统中)等。