360
12. 函数指针怎么使用 有什么作用
函数指针是指向函数的指针变量。它可以用来存储函数的地址,使得我们可以通过函数指针来调用对应的函数。
要声明一个函数指针,可以使用以下语法:
返回类型 (*指针变量名)(参数列表);
例如,假设有一个名为 add 的函数,它接受两个整数参数并返回它们的和。我们可以声明一个与该函数匹配的函数指针如下:
int (*ptr)(int, int);
然后,我们可以将 add 函数的地址赋给这个函数指针:
ptr = &add;
或者简写为:
ptr = add;
现在,我们就可以通过函数指针来调用 add 函数了:
int result = ptr(3, 4); // 调用 add 函数
使用函数指针的好处之一是它允许动态地选择和调用不同的函数。这在编程中很有用,特别是当需要根据运行时条件决定执行哪个具体的函数时。
此外,函数指针也常用于作为回调机制,在某些情况下允许将一个函数传递给另一个函数作为参数,并在需要时进行调用。
#define和const的区别:
#define 是预处理指令,用于文本替换,没有类型安全,不占用存储空间,作用范围广。
const 是类型安全的常量定义,有具体的类型,遵循作用域规则,编译时进行检查,占用存储空间。
在现代C++编程中,推荐使用 const 以利用类型安全和作用域管理的优势.
静态全局变量 vs 静态局部变量
作用域:
静态全局变量的作用域限于声明它的文件内,其他文件不能访问。
静态局部变量的作用域限于声明它的代码块内,但它的生命周期贯穿程序执行期间。
存储期:
两者都拥有静态存储期,即它们的生命周期从程序执行开始直到程序执行结束。
初始化:
两者在程序启动时进行初始化,如果没有显式初始化,会被自动初始化为0。
访问权限:
静态全局变量只能被同一源文件内的函数访问。
静态局部变量只能被其所在的函数访问,但是每次调用该函数时,都能访问到同一个静态局部变量的实例
静态局部变量 vs 普通局部变量
存储期:
静态局部变量在程序的整个运行期间都存在。
普通局部变量只在其定义的代码块执行期间存在。
初始化:
静态局部变量在程序启动时初始化一次。
普通局部变量每次进入其定义的代码块时初始化。
存储位置:
静态局部变量存储在程序的数据段中。
普通局部变量通常存储在栈上。
用途和行为:
静态局部变量用于在函数调用之间保持状态,而无需使用全局变量。
普通局部变量用于临时存储数据,每次函数调用时独立于其他调用。
总结:静态全局变量和静态局部变量主要区别在于作用域和访问权限。静态局部变量与普通局部变量的主要区别在于生命周期和初始化行为,静态局部变量能在函数调用之间保持其值。
常量指针(指向常量的指针)和指针常量(常指针)的区别
常量指针不能通过指针修改所指向的内容,但可以改变指向;指针常量的指向不能改变,但可以通过它修改所指向地址的内容。
常量指针(指向常量的指针):
语法:const 类型名 * 指针名 或 类型名 const * 指针名;
特点:这种指针指向一个常量,不能通过这个指针来修改它指向的值,但是指针本身的指向可以修改,即可以让它指向另外的地址。
示例:const int *ptr; 这里 ptr 可以指向不同的 int 类型的常量,但不能通过 ptr 修改这些 int 常量的值。
指针常量(常指针):
语法:类型名 * const 指针名;
特点:指针本身是个常量,一旦初始化之后,就不能再指向其他地址,但是可以通过这个指针来修改它指向地址上的值。
示例:int *const ptr; 一旦 ptr 被初始化指向一个整数,它就不能再指向其他地址了,但是可以修改 ptr 指向的那个整数的值。
什么是哈希冲突
哈希冲突(Hash Collision)是指当两个不同的输入值在经过哈希函数处理后得到了相同的哈希值的情况。哈希函数的设计目的是尽可能将输入数据映射到一个较大的(通常是固定大小的)值域空间中,以便快速访问数据。然而,由于输出值域的有限性相对于可能的输入值域,理论上总会存在不同输入映射到相同输出值的情况,即发生哈希冲突。
哈希冲突的影响
性能下降:哈希冲突会降低哈希表的效率。在没有冲突的理想情况下,哈希表的查找、插入和删除操作的时间复杂度可以达到O(1)。但是冲突的发生会迫使哈希表通过链表等结构来解决冲突,增加了操作的时间复杂度
内存分布,有哪几个区?
代码区(Text Segment):
存放程序的执行代码,也称为文本段或代码段。
通常是只读的,防止程序的代码被意外修改。
数据区:
分为初始化的数据区(初始化全局变量和静态变量,Data Segment)和未初始化的数据区(BSS Segment,Block Started by Symbol)。
初始化数据区存放程序中明确赋了初值的全局变量和静态变量。
未初始化数据区存放未初始化的全局变量和静态变量。出于性能考虑,这些变量在程序启动时统一被初始化为零。
堆区(Heap):
用于动态内存分配,由程序在运行时通过malloc、new等函数分配的内存块。
堆的大小不固定,可以动态扩展或缩减。当程序请求更多内存时,堆可以增长,释放内存时可以缩小。
栈区(Stack):
用于存放函数内的局部变量、函数参数、返回数据、返回地址等。
栈具有后进先出的特性。每当调用新的函数时,其相关信息(如局部变量)会被推入栈中,函数返回时被移除。
栈的大小通常有限制,过深的函数调用会导致栈溢出。
常量区:
存放常量字符串和其他类型的常量数据。
这部分区域的数据不可修改。
命令行参数区和环境变量区:
存放启动程序时传递给程序的命令行参数和环境变量。
数组与链表
内存分配:
数组分配的是连续的内存空间。这意味着数组的元素在内存中紧密排列,可以通过索引直接访问。
链表由一系列节点组成,每个节点包含数据和指向下一个节点的指针,节点在内存中可以是非连续的。
访问元素:
数组可以实现随机访问,即可以直接通过索引在常数时间内访问任何元素。
链表访问特定元素需要从头开始遍历,访问时间随链表长度增加而增加。
插入和删除操作:
数组的插入和删除操作相对较慢,因为可能需要移动元素以保持元素的连续性。
链表的插入和删除操作较快,只需改变相邻节点的指针即可,不需要移动其他节点。
内存利用:
数组在定义时就确定了大小,可能会造成内存浪费或空间不足的问题。
链表可以根据需要动态地分配内存,更加灵活。
栈与队列
数据处理方式:
栈遵循后进先出(LIFO, Last In First Out)的原则。最后一个添加到栈的元素将是第一个被移除的。
队列遵循先进先出(FIFO, First In First Out)的原则。第一个添加到队列的元素将是第一个被移除的。
操作:
栈主要操作有两种:push(在顶部添加元素)和pop(移除顶部元素)。
队列主要操作有两种:enqueue(在尾部添加元素)和dequeue(从头部移除元素)。
应用场景:
栈常用于解决如递归、后缀表达式求值、"撤销"功能在内的问题。
队列常用于解决如任务调度、缓冲处理、宽度优先搜索(BFS)等问题。
TCP/IP网络模型一共几层? 哪几层?
TCP/IP网络模型通常被描述为一个四层架构,这些层次从下到上分别是:
网络接口层(Link Layer):也称为网络接入层或链路层,负责在网络媒体上发送和接收数据帧。它涉及到如以太网(Ethernet)、Wi-Fi等物理和数据链路协议的实现,负责与具体的物理设备通信。
互联网层(Internet Layer):该层主要负责数据包从源到目的地的传输和路由,无论这些目标位于局域网内还是跨越多个网络。最著名的协议是互联网协议(IP),它定义了IP地址和路由行为。其他协议如ICMP(Internet Control Message Protocol)和IGMP(Internet Group Management Protocol)也在这一层工作。
传输层(Transport Layer):该层提供了端到端的数据传输服务,可以是可靠的或不可靠的。主要协议包括TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)。TCP提供可靠的、面向连接的服务,而UDP提供简单的、不可靠的服务。
应用层(Application Layer):这一层为应用软件提供网络服务。它定义了用于各种类型网络应用的协议,例如HTTP(Hypertext Transfer Protocol)、FTP(File Transfer Protocol)、SMTP(Simple Mail Transfer Protocol)和DNS(Domain Name System)等。
TCP/IP模型简化了原来的OSI七层模型,将其合并为四层,以更实用和简洁的方式描述网络通信过程。