指针基础知识(笔记)
文章目录
- 1. 概念理解
- 2. 空指针和野指针
- 3. 计算
- 4. 小结
- 5. size_t
- 6. 案例一: 指针查找并返回指定元素索引
- 7. 指针访问多维数组(涉及 int (*ptr)[3]解析)
- 8. 指针数组
- 9. 函数的值传递与地址引用传递
- ① 函数的值传递(pass by value)
- ② 地址传递(pass by reference)
- 10. 案例二:指针作为函数返回值
- 11. 案例三:游戏案例
- 12. 案例四:更新分数
- 13. 案例五:收藏奖杯(char*可以保存字符串)
- 14. 内容出处
1. 概念理解
① 指针的作用:外部服务的提供者
② 我们总是希望外部提供给我们良好的服务,我们只需要提供给ta地址(或金钱等), ta就可以帮我们解决一切困扰,省去我们亲自动手的麻烦。
③ 问:直接building_floor[2] = 106,不是更快么, 为什么还要引入指针?
答: 此处将数组与指针联系在一起只是为了方便理解。当我们解决更复杂的生活问题时,指针的妙用就会凸显。例如:我们点外卖或者网购时,我们当然也可以自己到店里或者生产地去取,可是为了方便,我们会选择向骑手或者网店付运费,让人家帮我们送到家门口。这种情况下,骑手和网店也可以理解为指针,我们向ta们付了钱,ta们给我们提供服务,省去我们来回奔波的麻烦。
④ windows的快捷方式是指针的一个最典型、最简单的应用。创建快捷方式相当于把程序的绝对地址赋值给那个指针变量(假如是 char *ptr = …);双击该快捷方式的图标相当于 *ptr; 程序打开的过程相当于把 *ptr作为一个参数传递给程序启动函数
#include<stdio.h>
int main(){int building_floor[5] = {101, 102, 103, 104, 105};//建群for(int i = 0; i < 5; i++){printf("%d : my house number is %d\n", i, building_floor[i]);}//群里发福利,一人一袋大米,103住户不在,请别人帮忙放门口int target_floor = 103;for(int i = 0; i < 5; i++){if(building_floor[i] == target_floor){printf("Ok!\n");break;}}//都在忙,请了个快递代拿,这个时候需要告诉人家准确地址int *courier = &building_floor[2];printf("%p\n", (void *)courier);printf("%p\n", &building_floor[2]);//推荐快递员for(int i = 0; i < 5; i++){printf("%d 's address is %p\n", building_floor[i], (void *)&building_floor[i]);}//我搬家了,搬到了106int *company = courier;*company = 106;printf("%d\n", building_floor[2]);//搬回来了*courier = 103;printf("%d\n", building_floor[2]);printf("%p\n", (void *)courier);printf("%d\n", *courier);courier += 1;printf("%d\n", *courier);int *courier2 = &building_floor[3];printf("%d\n", *courier2);return 0;
}
2. 空指针和野指针
#include<stdio.h>
int main(){//空指针: 还没有指向一个有效内存地址int *ptr = NULL;printf("%p \n", (void *)ptr);// 指向实际地址int x = 100;ptr = &x;printf("%p %d\n", &x, x);printf("%p %d\n", (void *)ptr, *ptr);// 野指针: 指向一个无效的内存地址或者已经被释放的内存地址// 野指针指向了一个无法描述的内存空间,可能产生无法预测的行为// 野指针非常危险, 程序员必须解决这个问题{int y = 104;ptr = &y;printf("%p %d\n", (void *)ptr, *ptr); // 1}// 脱离了上述作用域之后,y这个变量就不复存在了, t它相应的内存空间也会被释放. 无法确定*ptr所占空间是否有了新的数据, 贸然使用可能会导致程序崩溃// 此时的ptr就是一个野指针//printf("%p %d\n", &y, y);printf("%p %d\n", (void *)ptr, *ptr); // 2. 哪怕1、2输出结果相同也不行return 0;
}
3. 计算
#include<stdio.h>
int main(){int numbers[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};int *ptr = numbers;// ptr存的是数组首地址,因为数组在内存中是back to back 的printf("%p \n", (void *)ptr);printf("%p \n", (void *) &numbers[0]);printf("%d \n", *ptr);// 可以不写数组大小的原因// sizeof(numbers): 统计整个数组所占内存空间大小,单位字节// sizeof(numbers[0]); 统计第一个元素所占内存空间的大小int size = sizeof(numbers) / sizeof(numbers[0]);printf("%d %d\n", sizeof(numbers), sizeof(numbers[0]));printf("%d \n", size);/*地址之间的加减法跳跃元素*/ // 指针加法int *start_ptr = &numbers[0];printf("%d \n", *(start_ptr + 1));int offset = 2;printf("%p \n", start_ptr + offset); // 实际增加的字节数是offset *4printf("%p \n", &numbers[0] + offset);// 如果想要+1代表加一个字节,需要用到强制类型转换,强制转换成(char *)printf("%d \n", *(start_ptr + offset));// 不过转换增加了代码复杂度,可读性也差,因而实际编码过程中不常见for(int i = 0; i < 10; i++){printf("%d: %p\n", i + 1, (void *)&numbers[i]);}// 指针减法.访问第二个元素printf("%d \n", *(start_ptr + offset - 1));// 计算指针首尾地址之间的距离. // 微软专门给了一个数据类型ptrdiff_t(有符号整型)来表示指针之间的距离(在32位系统中它通常是int类型, 64位-long int) ,便于跨平台使用int *end_ptr = &numbers[size - 1];printf("%td \n", end_ptr - start_ptr);// 通过指针比较元素位置前后if(start_ptr < end_ptr){printf("start_ptr is ahead of end_ptr\n");}else{printf("start_ptr is behind end_ptr\n");}int x = 78;int y = 120;int *ptr_x = &x;int *ptr_y = &y;printf("x: %p \n", (void *)ptr_x);printf("y: %p \n", (void *)ptr_y);if(ptr_x < ptr_y){printf("ptr_x is ahead of ptr_y\n");}else{printf("ptr_x is behind ptr_y\n");}// 指针顺序遍历for(int *i = start_ptr; i <= end_ptr; i++){printf("%d ", *i);}printf("\n");// 指针逆序遍历for(int *i = end_ptr; i >= start_ptr; i--){printf("%d ", *i);}printf("\n");
}
4. 小结
上述都是指针与数组的关系(数组名作为指针的使用)
5. size_t
size_t:一个无符号的整数类型,专门用来表示大小、长度和索引。
c语言设计它的初衷:提高程序在不同平台(跨平台)的可移植性和安全性。
i++与++i:相同点:效果相同。差异:对于整型循环器而言,两者在性能上差异不大。但如果使用迭代器的话, ++i 在性能上可能会有一些微小的优势。(从定义上来讲,后缀递增 i++需要先去存储原始的值,然后再去返回它,在理论上可能会多一点开销)
#include<stdio.h>
#include<inttypes.h>
int main(){int numbers[] = {10, 20, 30, 40};size_t numbers_size = sizeof(numbers) / sizeof(numbers[0]);int *ptr = numbers;// 输出数组元素原来的值for(size_t i = 0; i < numbers_size; ++i){printf("%" PRId32 " ", *(ptr + i));}printf("\n");// 将数组里每个元素的值都加5, 输出改变后数组元素的值for(size_t i = 0; i < numbers_size; ++i){*(ptr + i) += 5;printf("%" PRId32 " ", *(ptr + i));}return 0;
}
6. 案例一: 指针查找并返回指定元素索引
#include<stdio.h>
#include<inttypes.h>
#include<stdbool.h>
int main(){int32_t values[] = {10, 20, 30, 40, 50};int32_t *ptr = values;int32_t target_value = 30;int32_t *target_ptr = NULL;size_t values_size = sizeof(values) / sizeof(values[0]);size_t index = 0;bool found = false;// 与<stdbool.h>配套使用for(size_t i = 0; i < values_size; ++i){if(*(ptr + i) == target_value){index = i;target_ptr = ptr + i;found = true;break;}}// 专门输出size_t类型的数据if(found){printf("element %" PRId32 " 's index is: %zu \n ", target_value, index);}else{printf("element %" PRId32 " not found!\n", target_value);}return 0;
}
7. 指针访问多维数组(涉及 int (*ptr)[3]解析)
定义一个指针操作一个多维数组
#include<stdio.h>
#include<inttypes.h>
#include<stdbool.h>
int main(){int building_floor[2][3] = {{1, 2, 3}, {4, 5, 6}};// (*ptr)[3]:ptr是一个指针,它指向一个包含3个int元素(数组长度为3)的一维数组// 一维数组指向数组首地址,二维数组指向第一行(二维数组的首地址就是它的第一行数据)// int *ptr[3]:声明了三个指针(即声明了一个包含三个元素的指针数组)int (*ptr)[3] = building_floor;// printf("%p \n", (void *)ptr);// printf("%p \n", &building_floor[0][0]);// printf("%p \n", &building_floor[0][1]);for(int i = 0; i < 2; ++i){for(int j = 0; j < 3; ++j){//不带*的原因:ptr[i]已经相当于地址了,它代表让指针跳到第i行printf("%d ", ptr[i][j]);}printf("\n");}return 0;
}
8. 指针数组
定义多个指针操作不同的数组
#include<stdio.h>
#include<inttypes.h>
int main(){int32_t department1[] = {1001, 1002, 1003};int32_t department2[] = {2001, 2002, 2003, 2004};int32_t department3[] = {3001, 3002, 3003, 3004, 3005};int32_t* department_ptrs[] = {department1, department2, department3};size_t department_sizes[] = {sizeof(department1) / sizeof(int), sizeof(department2) / sizeof(int), sizeof(department3) / sizeof(department3[0])};for(size_t i = 0; i < 3; ++i){printf("department%zu: ", i + 1);// 此处的变量类型老是搞混for(size_t j = 0; j < department_sizes[i]; ++j){//printf("%" PRId32 " ", *(department_ptrs[i] + j));printf("%" PRId32 " ", department_ptrs[i][j]);}printf("\n");}return 0;
}
9. 函数的值传递与地址引用传递
① 函数的值传递(pass by value)
必须借助返回值才能改变数值
#include<stdio.h>
#include<inttypes.h>
void add_five(int32_t value);
int add_five_2(int32_t value);
int main(){int32_t my_value = 10;printf("original value: %" PRId32 "\n", my_value);add_five(my_value);printf("present value: %" PRId32 "\n", my_value); // 输出结果为10(与函数的作用域有关)int32_t number = add_five_2(my_value);printf("present value: %" PRId32 "\n", number); // 输出结果为15my_value = add_five_2(my_value);printf("present value: %" PRId32 "\n", my_value); // 输出结果为15my_value = add_five_2(my_value);printf("present value: %" PRId32 "\n", my_value); // 输出结果为20return 0;
}
void add_five(int32_t value){value += 5;
}int add_five_2(int32_t value){return value += 5;
}
② 地址传递(pass by reference)
指针作为函数参数使用。函数相当于提供外部服务的东西(真正的外部服务–函数),所以指针在函数里写的是最多的
#include<stdio.h>
#include<inttypes.h>
void add_five(int32_t* value);
int main(){int32_t my_value = 10;printf("original value: %" PRId32 "\n", my_value);add_five(&my_value);printf("present value: %" PRId32 "\n", my_value); // 输出结果为15return 0;}
void add_five(int32_t* value){*value += 5;
}
10. 案例二:指针作为函数返回值
① 需考虑函数的参数类型以及返回值类型是否需要写成指针形式。这个其实我还是拿捏不准。
② 仿写过程中出现的问题:习惯指针的写法以后,很难返回之前的模式。例如 void print_salary(uint32_t* salary) 这个函数打印数组的数值时,会下意识写成printf(“%” PRIu32 " “, *(salary + i))这种形式,printf(”%" PRIu32 " ", salary[i])原先的这种写法就算后续在脑子里一闪而过,也会因为想到指针而怀疑这种写法的正确性。之前听到指针就头疼,现在反而觉得(就上述例子而言),指针的写法好像比 salary[i]更容易理解。
③ 学到现在突然有点懵:为什么 salary[i] 和 *(salary + i)是等效的? 上述函数中salary保存的是数组首地址,为什么访问数组元素时 salary[i] 不需要加 * 号?
答(gpt): *(salary + i):对指针 salary 加上索引 i,然后解引用它,可以得到数组的第 i 个元素; salary[i]:在 salary[i] 中,salary 已经是一个指向 uint32_t 的指针,用于访问数组的第 i 个元素。在这种语法中,编译器会自动进行指针解引用,所以你不需要手动使用 * 运算符来解引用 salary。
#include<stdio.h>
#include<inttypes.h>
#define EMPLOYEE_COUNT 5// 集体涨工资
void increase_salary(uint32_t* salary, uint32_t increment);
// 打印工资
void print_salary(uint32_t* salary);
// 返回工资最高的员工
uint32_t* find_highest_salary(uint32_t* salary);
// 返回年终奖
// 因为这个数据用到的不多,因此函数的参数也可以不用写成指针形式
uint32_t cal_bonus(uint32_t* salary);
int main(){uint32_t salary[EMPLOYEE_COUNT] = {3000, 3100, 3200, 3300, 3400};printf("original salary: ");print_salary(salary);uint32_t increment = 10000;increase_salary(salary, increment);printf("present salary: ");print_salary(salary);printf("highest salary: %" PRIu32 "\n", *find_highest_salary(salary));printf("highest bonus: %" PRIu32, cal_bonus(find_highest_salary(salary)));
}
void increase_salary(uint32_t* salary, uint32_t increment){for(size_t i = 0; i < EMPLOYEE_COUNT; ++i){//*(salary + i) += increment;salary[i] += increment;}
}void print_salary(uint32_t* salary){for(size_t i = 0; i < EMPLOYEE_COUNT; ++i){//printf("%" PRIu32 " ", *(salary + i));printf("%" PRIu32 " ", salary[i]);}printf("\n");
}uint32_t* find_highest_salary(uint32_t* salary){uint32_t* highest = salary;for(size_t i = 0; i < EMPLOYEE_COUNT; ++i){// if(*(salary + i) > *highest){// highest = salary + i;// }if(salary[i] > *highest){*highest = salary[i];}}return (uint32_t*) highest;
}uint32_t cal_bonus(uint32_t* salary){return (*salary) / 10;
}
11. 案例三:游戏案例
// 玩家通过解决谜题来提升等级, 指针和函数搭配使用去更新玩家的经验值和等级
/* 游戏开发的基本概念:玩家属性:exp(经验值)、level(等级)解谜:每当玩家解开一个谜题,ta就可以获得一定数量的经验值等级提升:当玩家通过解谜获得的经验值达到一定的极限,就让ta等级提升游戏目标:玩家游戏等级达到一定等级时,会给ta相应奖励(例如:一个奖杯)
*/
/*代码实现应该有三个函数:1. 更新玩家经验值2. 检查是否有足够的经验值确保玩家升级3. 玩家每提升一级,给ta相应的礼品
*/
/*设计思路:1. 从main函数入手,写一些基础性的内容(例如:定义玩家初始经验值和等级)2. 考虑函数如何设计(根据实际需求明确函数是否需要返回值以及返回值类型、函数参数)3. 设计函数过程中,完善代码逻辑(例如:多少经验值可以升级?游戏最高等级是多少?宝藏提示的数量有多少?)4. 宝藏提示函数(用到了表驱动法):所有玩家在升到相同等级时,获得的宝藏全都相同,因此函数前面可以写一个const函数里用static的原因:函数里所有的东西都早已固定好,不需要每次升级都把函数重新定义一遍
*/#include<stdio.h>
#include<inttypes.h>
#include<stdbool.h>#define MAX_LEVEL 10
#define EXP_PER_LEVEL 100
#define HINTS_COUNT 10void increase_exp(int32_t* exp, int32_t increment);
bool check_level_up(int32_t* exp, int32_t* level);
const char* get_treasure_hint (int32_t level);
int main() {// 初始经验值和等级int32_t player_exp = 0;int32_t player_level = 1;// 玩家解谜获得经验值increase_exp(&player_exp, 75);increase_exp(&player_exp, 100);// 当前玩家是否可以升级(其实每次经验值增加后都应该进行这个判断)if (check_level_up(&player_exp, &player_level)) {// 获得相应提示printf("%s\n", get_treasure_hint(player_level));}else {printf("经验值不足,无法升级!\n");}return 0;}
void increase_exp(int32_t* exp, int32_t amount) {*exp += amount;
}bool check_level_up(int32_t* exp, int32_t* level) {while (*exp >= EXP_PER_LEVEL && *level < MAX_LEVEL) {*exp -= EXP_PER_LEVEL;(*level)++;printf("恭喜您的等级升到了%" PRId32 "级\n", *level);return true;}return false;
}const char* get_treasure_hint(int32_t level) {static const char* hints[HINTS_COUNT] = {"提示一:宝藏在...","提示二:宝藏在...","提示三:宝藏在...",// ......};if (level > 0 && level <= HINTS_COUNT) {return hints[(size_t)level - 1];}return "未知提示:请确保你的等级在有效范围内才能解锁宝藏提示";
}
12. 案例四:更新分数
游戏里经常会有一些模块:更新分数、比较分数、分数倍增等
仿写时出现的问题:分数比较模块返回值写成 “return (int32_t)(*player1_score > *player2_score) ? *player1_score : *player2_score” ,多加了 * 号
#include<stdio.h>
#include<inttypes.h>void update_score(int32_t* player_score, int32_t points);
int32_t* compare_score(int32_t* player1_score, int32_t* player2_score);
void double_score(int32_t* player_score);int main(void){int32_t player1_score = 100;int32_t player2_score = 150;// 更新分数update_score(&player1_score, 20);// 比较分数int32_t* higher_score = compare_score(&player1_score, &player2_score);printf("higher score: %" PRId32 "\n", *higher_score);// 分数倍增double_score(&player2_score);return 0;
}void update_score(int32_t* player_score, int32_t points){*player_score += points;printf("updated score: %" PRId32 "\n", *player_score);
}int32_t* compare_score(int32_t* player1_score, int32_t* player2_score){return (int32_t)(*player1_score > *player2_score) ? player1_score : player2_score;
}void double_score(int32_t* player_score){*player_score *= 2;printf("score after multiplication: %" PRId32 "\n", *player_score);
}
13. 案例五:收藏奖杯(char*可以保存字符串)
① 游戏中的用途:添加成就、显示所有成就效果
② const在函数参数中的用途:防止函数内部被有意或无意地修改(变量被声明为const意为该变量在今后仅可读,不可以被修改,即可读不可写);保护传入的数据
③ 指针变量不仅可以保存地址,也可以保存字符串
#include<stdio.h>#define MAX_ACHIEVEMENT_COUNT 10
// 此处往数组里添加元素的方式类似于面向对象编程里的接口设计
const char* achievements[MAX_ACHIEVEMENT_COUNT];
void add_achievement(const char* new_achievement);
size_t achievement_count = 0;void print_achievements();int main(void) {add_achievement("完成第一个挑战奖杯");add_achievement("找到了所有的隐藏宝箱");print_achievements();return 0;
}void add_achievement(const char* new_achievement) {if (achievement_count < MAX_ACHIEVEMENT_COUNT) {achievements[achievement_count++] = new_achievement;printf("已添加新成就:%s\n", new_achievement);}else {printf("成就数组已满,无法添加新的成就!\n");}
}void print_achievements() {printf("显示所有已有成就:\n");for (size_t i = 0; i < achievement_count; ++i) {printf("%zu: %s\n", i + 1, achievements[i]);}
}
14. 内容出处
我是从p165地址这一小结开始看的指针这一部分
虽然很早就学过c语言了,但是学的效果用四个字评价就是“一坨狗屎”。前几天我在听frank的数据结构的课程,听完了链表这一部分,原本想着理解了它的内核及应用场景后,代码部分应该也就不成问题了,其实一切本该如此,但是我突然发现我还是写不出来,基础知识部分太差劲了,例如:我只是知道有指针这个东西,但是让我具体解释,我真的会傻眼。虽然之前在学c语言的时候,也专门听过指针这部分的网课,但怎么说呢,跟着up主把代码敲了一遍(那代码我现在还有,但是那次写完之后再也没看过🤣),该不懂还是不懂,甚至于当时刷题看见指针这个标签就会直接跳过。
相关文章:

指针基础知识(笔记)
文章目录 1. 概念理解2. 空指针和野指针3. 计算4. 小结5. size_t6. 案例一: 指针查找并返回指定元素索引7. 指针访问多维数组(涉及 int (*ptr)[3]解析)8. 指针数组9. 函数的值传递与地址引用传递① 函数的值传递(pass by value)② 地址传递(pass by reference) 10. 案例二&…...

[Python学习日记-3] 编程前选择一个好用的编程工具
[Python学习日记-3] 编程前选择一个好用的编程工具 简介 PyCharm IDE的安装 PyCharm IDE安装后的一些常规使用 简介 在踏上 Python 编程的精彩旅程之前,选择一款得心应手的编程工具无疑是至关重要的一步。这就如同战士在出征前精心挑选趁手的武器,它将…...

智能化的Facebook未来:AI如何重塑社交网络的面貌?
随着人工智能(AI)技术的飞速发展,社交网络的面貌正在经历深刻的变革。Facebook(现Meta Platforms)作为全球最大的社交媒体平台之一,正积极探索如何利用AI技术来提升用户体验、优化内容管理并推动平台创新。…...
安全启动的原理
安全启动(Secure Boot)是一种用于确保设备只运行经过认证的软件的安全机制。其核心原理和步骤如下: 1. **硬件信任根(Root of Trust, RoT)**: - 安全启动过程始于硬件信任根,通常是设备上的…...

【ML】pre-train model 是什么如何微调它,如何预训练
【ML】pre-train model 是什么如何微调它,如何预训练 0. 预训练模型(Pre-trained Model)0.1 预训练模型的预训练过程0.2 如何微调预训练模型0.3 总结 1. Contextualized word Embedding2. 怎么 让 bert 模型变小3. 如何微调模型 0. 预训练模型…...
leetcode代码练习——Java的数据结构(具体使用)
注:Java中所有的泛型必须是引用类型 如<Integer>而不是<int> java提供的数学方法: 求最大值Math.max(10,15),最小值Math.min(10,15) 看取值范围: int范围:-2^31-2^31-1 double范围:-2^63-2^63-1 long范围:-2^63-2…...

sqlserver导出数据脚本
文章目录 sqlserver导出数据脚本任务-生成脚本 sqlserver导出数据脚本 任务-生成脚本...

html+css 实现hover中间展开背景
前言:哈喽,大家好,今天给大家分享htmlcss 绚丽效果!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目…...
Java 怎么获取支付宝Open ID
在Java中获取支付宝用户的OpenID,通常是通过支付宝的开放平台API来完成的。OpenID是支付宝用于唯一标识一个支付宝用户的字符串,它在OAuth授权流程中被用来获取用户的身份和权限。 下面我将给出一个基于Java使用Spring Boot框架和支付宝开放平台SDK来获…...

Web-server日志分析命令
https://gist.github.com/hvelarde/ceac345c662429447959625e6feb2b47 通过状态码获取请求总数 awk {print $9} /var/log/apache2/access.log | sort | uniq -c | sort –rn按照IP的请求数量排序 awk {print $1} /var/log/apache2/access.log | sort | uniq -c | sort -rn |…...
Typora的markdown笔记使用说明
个人感觉Typora是一款很适合记录编程学习的软件 目录 个人感觉Typora是一款很适合记录编程学习的软件 一、标题 二、段落 1、换行 2、分割线 三、文字显示 1、字体 2、上下标 四、列表 1、无序列表 2、有序列表 3、任务列表 五、区块显示 六、代码显示 1、行内…...

前端如何做单元测试? 看这篇就入门了
前言 对于现在的前端工程,一个标准完整的项目,通常情况单元测试是非常必要的。但很多时候我们只是完成了项目而忽略了项目测试。我认为其中一个很大的原因是很多人对单元测试认知不够,因此我写了这边文章,一方面期望通过这篇文章…...

Chainlit快速实现AI对话应用的聊天记录如何持久性保存
前言 Chainlit 可以设置聊天记录用户搜索和浏览过去的对话。 如何实现 要启用聊天历史记录,您需要启用: 数据持久性身份验证恢复对话 为了让用户继续持久对话,请使用cl.on_chat_resume 生命周期钩子 装饰器使用户能够继续对话。需要同时启用数据持久性和身份验证。 该…...

【探索数据结构与算法】——深入了解双向链表(图文详解)
目录 一、双向链表的基本概念 二、双向链表的结构 三、双向链表的基本操作实现方法 1.双向链表的初始化 2.双向链表的头插 3.双向链表的尾插 6.查找节点 7.在指定位置之前插入节点 8.删除指定位置节点 9.打印链表数据 10.双向链表销毁 四、完整代码实现 …...
linux常用命令备忘录
一、常用命令 查看被占用进程:ps ef|grep 11612 查看当前目录:pwd 查看文件的md5: (linux)md5sum 文件名 (windows)certutil -hashfile some_file MD5 查看当前目录的文件大小:…...

【C++进阶学习】第十二弹——C++ 异常处理:深入解析与实践应用
前言: 在C编程语言中,异常处理是一种重要的机制,它允许程序员在运行时捕获和处理错误或异常情况。本文将详细介绍C异常处理的相关知识点,包括异常的定义、抛出与捕获、异常处理的原则、以及在实际编程中的应用。 目录 1. 异常处理…...
《算法竞赛进阶指南》0x23剪枝
剪枝,就是减少搜索树的规模、尽可能排除搜索书中不必要的分支的一种手段。形象地看,就好像剪掉了搜索树的枝条,故被称为“剪枝”。在深度优先搜索中,有以下常见的剪枝方法。 1.优化搜索顺序 在一些搜索问题中,搜索树的…...

同态加密和SEAL库的介绍(三)BFV - Batch Encoder
写在前面: 在上一篇中展示了如何使用 BFV 方案执行一个非常简单的计算。该计算在 plain_modulus 参数下进行,并且仅使用了 BFV 明文多项式中的一个系数。这种方法有两个显著的问题: 实际应用通常使用整数或实数运算,而不是模运算…...

Docker 环境下使用 Traefik v3 和 MinIO 快速搭建私有化对象存储服务
上一篇文章中,我们使用 Traefik 新版本完成了本地服务网关的搭建。接下来,来使用 Traefik 的能力,进行一系列相关的基础设施搭建吧。 本篇文章,聊聊 MinIO 的单独使用,以及结合 Traefik 完成私有化 S3 服务的基础搭建…...

玛雅房产系统源码开发与技术功能解析
引言 随着房地产市场的蓬勃发展,房产管理系统(Real Estate Management System, REMS)作为提升行业效率、优化资源配置的关键工具,其重要性日益凸显。房产系统源码开发不仅涉及复杂的业务逻辑处理,还融合了先进的软件开…...

基于5G下行信号的模糊函数分析matlab仿真,对比速度模糊函数和距离模糊函数
目录 1.引言 2.算法仿真效果演示 3.数据集格式或算法参数简介 4.MATLAB部分程序 5.算法涉及理论知识概要 6.参考文献 7.完整算法代码文件获得 1.引言 模糊函数(Ambiguity Function, AF)是信号处理领域用于分析信号时频分辨能力的核心工具…...

ModBus总线协议
一、知识点 1. 什么是Modbus协议? Modbus 是一种工业通信协议,最早由 Modicon 公司在1979年提出,目的是用于 PLC(可编程逻辑控制器)之间的数据通信。它是主从式通信,即一个主机(主设备…...

NumPy 比较、掩码与布尔逻辑
文章目录 比较、掩码与布尔逻辑示例:统计下雨天数作为通用函数(Ufuncs)的比较运算符使用布尔数组计数条目布尔运算符 布尔数组作为掩码使用关键字 and/or 与运算符 &/| 的区别 比较、掩码与布尔逻辑 本文介绍如何使用布尔掩码来检查和操…...
XHR / Fetch / Axios 请求的取消请求与请求重试
XHR / Fetch / Axios 请求的取消请求与请求重试是前端性能优化与稳定性处理的重点,也是面试高频内容。下面是这三种方式的详解封装方案(可直接复用)。 ✅ 一、Axios 取消请求与请求重试封装 1. 安装依赖(可选,用于扩展…...

【设计模式-3.4】结构型——代理模式
说明:说明:本文介绍结构型设计模式之一的代理模式 定义 代理模式(Proxy Pattern)指为其他对象提供一种代理,以控制对这个对象的访问,属于结构型设计模式。(引自《设计模式就该这样学》P158&am…...

Python----目标检测(《YOLOv3:AnIncrementalImprovement》和YOLO-V3的原理与网络结构)
一、《YOLOv3:AnIncrementalImprovement》 1.1、基本信息 标题:YOLOv3: An Incremental Improvement 作者:Joseph Redmon, Ali Farhadi 机构:华盛顿大学(University of Washington) 发表时间:2018年 代…...
Neo4j 认证与授权:原理、技术与最佳实践深度解析
Neo4j 作为领先的图数据库,其安全机制——认证(Authentication)与授权(Authorization)——是保障数据资产的核心防线。本文将深入剖析其工作原理、关键技术、实用技巧及行业最佳实践,助您构建坚不可摧的图数据安全体系。 Neo4j 提供了强大且灵活的认证授权框架,涵盖从基…...
Vue3实现拖拽改变元素大小
代码实现 整体页面结构通过一个 dragResize-wrapper 包含左右两个区域,左侧区域包含一个可拖拽的边界。以下是关键代码 HTML 部分 <template><div class"dragResize-wrapper"><div class"dragResize-left"><div class&…...
Linux 云服务器部署 Flask 项目(含后台运行与 systemd 开机自启)
一、准备工作 在开始正式部署之前,请确认以下前提条件已经准备好: 你有一台运行 Linux 系统(CentOS 或 Ubuntu)的服务器; 服务器有公网 IP,本例中使用:111.229.204.102; 你拥有该服务器的管理员权限(可以使用 sudo); 打算使用 Flask 构建一个简单的 Web 接口; 服务…...
POP3、IMAP、SMTP:三大邮件协议核心差异与应用场景解析
## 一、协议概述与核心功能 电子邮件系统的运行依赖三大核心协议:**POP3**(Post Office Protocol 3)、**IMAP**(Internet Message Access Protocol)和**SMTP**(Simple Mail Transfer Protocol)…...