文章目录
- 前言
- 一、switch能不能用浮点数
- 二、指针函数和函数指针
- 三、如何防止重复引用头文件
- 四、如何写一个函数可以在main之前执行
- 五、栈和队列区别及应用场景
- 六、linux上查看磁盘内存占用率的命令
- 七、什么是磁盘碎片
- 八、内存泄露是怎么产生的
- 九、发生了coredump怎么解决
- 总结
前言
本篇文章继续刷题!
一、switch能不能用浮点数
switch语句是不支持直接使用浮点型(即浮点数)作为判断条件的。switch语句通常用于基于离散的整数值或枚举类型进行条件判断。
二、指针函数和函数指针
指针函数(Pointer to a Function)和函数指针(Function Pointer)是两个不同的概念,尽管它们都涉及到指针和函数。
函数指针是一个指针变量,它存储了函数的地址。通过函数指针,可以像调用普通函数一样调用被指向的函数。函数指针的声明类似于函数原型,只是在函数名前加上了一个指针操作符(*)。例如,以下是一个函数指针的声明:
int (*funcPtr)(int, int);
指针函数是一种函数类型,它返回一个指针。与普通函数不同的是,指针函数的返回类型是一个指针类型,用于指向特定类型的数据。例如,以下是一个指针函数的声明:
int* myFunction(int);
三、如何防止重复引用头文件
1.使用条件编译指令:
在头文件的开头和结尾添加条件编译指令,可以防止头文件被重复引用。
#ifndef HEADER_FILE_NAME_H
#define HEADER_FILE_NAME_H
// 头文件内容
#endif // HEADER_FILE_NAME_H
这种方式会在编译时检查是否已定义了HEADER_FILE_NAME_H宏,如果未定义则执行#define指令将其定义,然后执行头文件内容;如果已定义,则跳过头文件内容。这样可以确保同一个头文件只会被引用一次。
2.使用#pragma once指令:
#pragma once指令是一种非标准的方法,但被大多数编译器所支持。它可以在头文件的开头使用,用于确保头文件只会被引用一次。
#pragma once
// 头文件内容
这种方式与条件编译指令类似,但更简洁,不需要显式定义和判断宏。
四、如何写一个函数可以在main之前执行
要在main函数执行之前调用某个函数,可以利用C/C++的特性——构造函数,以及全局对象的初始化过程来实现。
在C++中,全局变量和静态变量的初始化在main函数之前进行,而利用全局变量或静态变量的构造函数,可以在初始化阶段执行自定义的代码。
#include <iostream>
void preMainFunction() {
std::cout << "This function is called before main!" << std::endl;
}
struct PreMainCaller {
PreMainCaller() {
preMainFunction();
}
};
PreMainCaller preMainCaller; // 全局对象,构造函数会在main之前自动调用
int main() {
std::cout << "This is the main function." << std::endl;
return 0;
}
五、栈和队列区别及应用场景
1.区别:
存储方式:栈是一种后进先出(LIFO,Last-In-First-Out)的数据结构,新的元素插入到栈的顶部,称为栈顶,而元素的删除也是从栈顶开始。队列是一种先进先出(FIFO,First-In-First-Out)的数据结构,新的元素插入到队列的末尾,称为队尾,而元素的删除是从队列的开头进行。
操作方式:栈支持两种基本操作,即压入(Push)和弹出(Pop)。压入将新元素放入栈顶,弹出将栈顶的元素移除。栈的访问是单一方向的,只能操作或访问栈顶元素。队列支持三种基本操作,即入队(Enqueue)、出队(Dequeue)和查看队头元素。入队将新元素放入队列的末尾,出队将队列开头的元素移除,而查看队头元素是获取队列开头元素的值而不移除。
2.应用场景:
栈的应用场景:
括号匹配:用栈可以检查表达式中的括号是否匹配,或者在编译器中判断代码的语法是否合法。
函数调用:函数的调用过程中使用栈来存储局部变量、参数等信息。
后缀表达式计算:使用栈可以实现后缀表达式(逆波兰表达式)的计算。
撤销操作:在软件应用中,可以使用栈来实现撤销(Undo)操作的功能。
队列的应用场景:
广度优先搜索:在图的遍历中,使用队列来实现广度优先搜索算法。
缓存管理:可以使用队列来实现缓存,按照先进先出的原则管理缓存数据。
多线程任务调度:使用队列可以实现任务队列,多个线程从队列中获取任务执行。
打印队列:打印机通常使用队列来管理打印任务,保证按顺序进行打印。
六、linux上查看磁盘内存占用率的命令
查看磁盘占用率:
1.使用 df 命令可以查看文件系统的磁盘占用情况。运行 df -h 可以以人类可读的格式显示磁盘空间使用情况,包括磁盘大小、已用空间、可用空间和挂载点。
2.使用 du 命令可以查看指定目录的磁盘使用情况。运行 du -sh <目录路径> 可以显示指定目录的总磁盘使用情况。
七、什么是磁盘碎片
磁盘碎片(Disk Fragmentation)是指磁盘上文件存储位置的不连续和不规则分布。
当文件被存储或删除时,操作系统将文件分成一系列的数据块(或称为簇、扇区),这些数据块会被分散地分配到磁盘的不同位置。随着文件的频繁操作,特别是文件的修改、增加和删除,磁盘上的文件数据会变得分散,导致出现磁盘碎片。
八、内存泄露是怎么产生的
内存泄露的产生通常是由以下原因导致:
1.动态内存分配未释放:在程序中使用动态内存分配的函数(如malloc()、new等)申请内存空间,但在后续的程序执行中忘记或错误地释放这些内存。这会导致这些内存空间无法被再次使用,从而造成内存泄露。
#include <iostream>
void memoryLeak() {
int* ptr = new int(5); // 动态分配一个int类型的内存,赋值为5
// 没有释放这块内存,造成内存泄漏
}
int main() {
memoryLeak();
// 这里没有释放内存的机会,造成内存泄漏
return 0;
}
2.引用计数错误:在使用引用计数内存管理机制时,如果存在引用计数计算错误或管理错误,就会导致某些内存块的引用计数无法正确减少到零,从而无法被回收。
#include <stdio.h>
struct Object {
int data;
int ref_count;
};
void add_reference(struct Object* obj) {
obj->ref_count++;
}
void remove_reference(struct Object* obj) {
obj->ref_count--;
if (obj->ref_count == 0) {
// 错误的引用计数判断,没有释放内存
// 错误示例:free(obj);
}
}
int main() {
struct Object* obj = (struct Object*)malloc(sizeof(struct Object));
obj->data = 10;
obj->ref_count = 1;
add_reference(obj);
remove_reference(obj);
// 这里没有正确释放内存,引用计数错误导致内存泄露
return 0;
}
3.被遗漏的数据结构释放:如果程序中存在数据结构的操作不正确,比如删除一个数据结构元素时没有正确释放相关内存空间,就会导致这些内存空间无法被回收。
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node* next;
};
void deleteNode(struct Node* node) {
// 错误的删除操作,没有释放相关内存
// 错误示例:free(node);
}
int main() {
struct Node* head = (struct Node*)malloc(sizeof(struct Node));
struct Node* node1 = (struct Node*)malloc(sizeof(struct Node));
struct Node* node2 = (struct Node*)malloc(sizeof(struct Node));
head->next = node1;
node1->next = node2;
node2->next = NULL;
// 删除node1节点,但没有释放其相关内存
deleteNode(node1);
free(head); // 释放头节点内存
// 错误的释放操作,忽略了node1相关内存的释放
return 0;
}
4.循环引用:当两个或多个对象之间形成相互引用,且没有外部引用指向它们时,这些对象就会形成循环引用。如果这些对象使用了动态内存分配,但无法被访问到,就会导致内存泄露。
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node* next;
};
int main() {
struct Node* node1 = (struct Node*)malloc(sizeof(struct Node));
struct Node* node2 = (struct Node*)malloc(sizeof(struct Node));
node1->data = 1;
node1->next = node2;
node2->data = 2;
node2->next = node1; // 形成循环引用
// 循环引用的节点无法被访问,导致内存泄露
// 这里没有写释放内存的代码
return 0;
}
九、发生了coredump怎么解决
1.使用GDB
2.使用核心分析工具:一些操作系统提供了专门的核心分析工具,如Linux的crash命令和Solaris的mdb命令。这些工具可以用于分析核心转储文件,提供有关崩溃时的堆栈跟踪、寄存器值和其他信息。您可以研究相应操作系统的文档以了解如何使用这些工具。
3.使用符号化堆栈跟踪服务:有一些在线服务可以接收核心转储文件并提供符号化的堆栈跟踪信息。您可以上传核心转储文件,并获得包含函数调用链的易读输出,以帮助您定位问题。一些常见的服务包括Backtrace、Sentry和Bugsnag。
4.代码审查和日志分析:如果您拥有程序的源代码和日志文件,您可以通过仔细审查代码和分析日志来定位问题。在崩溃发生之前和之后的日志中查找错误消息、异常或其他指示性信息,以了解可能的问题所在。
5.使用内存分析工具:一些工具可以帮助您分析程序的内存使用情况,以确定内存泄漏、越界访问或其他内存相关问题。常用的内存分析工具包括Valgrind、AddressSanitizer和Memory Analyzer(MAT)。
总结
本篇文章就讲解到这里。