《C 语言指针高级指南:字符、数组、函数指针的进阶攻略》

目录

一. 字符指针变量

二. 数组指针变量

三. 二维数组传参

3.1 二维数组的本质

3.2 访问方式与地址计算

3.3 二维数组的传参方式

3.4 深入解析 *(*(arr+i)+j) 与 arr[i][j] 的等价性

四. 函数指针变量

4.1 函数指针变量的创建

4.2 函数指针变量的使用

4.3 两段"有趣"代码的讲解

4.3.1 解析 (*(void(*)())0)();

4.3.2 解析void(* signal(int, void(*)(int)))(int);

4.4 typedef关键字讲解

4.4.1 基本语法

4.4.2 常见用法

4.4.3 结合 typedef 解析 signal 函数

4.4.4 typedef 的优点

4.4.5 总结

五. 函数指针数组

5.1 基本概念

5.2 详细用法

5.3 实际应用示例

六. 转移表


一. 字符指针变量

什么是字符指针变量呢?

字符指针变量是指向字符类型数据的指针,在C/C++中通常用于处理字符和字符串和字符数组。

例如以下代码  处理字符

int main()
{char ch = 'w';char* pc = &ch;  ///字符指针变量return 0;
}

pc存放的是字符w的地址

例如以下代码  处理字符串

int main()
{const char* pc = "Hello, World!";//常量字符串 不可被修改printf(" %c\n", *pc);printf(" %s", pc); //注意: 打印字符串的时候 需要的参数是字符串的起始地址return 0;
}

pc存放的是首字符H的地址  打印验证结果如下

注意: 打印字符串的时候 需要的参数是字符串的起始地址

注意:pc此时指向的是常量字符串 即*pc现在是一个左值 无法被修改

由于*pc现在是一个左值 无法被修改 使用我们使用const来修饰它

例如以下代码  处理字符数组

int main()
{char arr[] = "ABCDE";char* pc = arr;printf(" %c\n", *pc);printf(" %s", pc); //注意: 打印字符串的时候 需要的参数是字符串的起始地址return 0;
}

pc存放的是数组第一个元素a的地址 同理 运行结果如下

注意:pc此时指向的是数组  与指向字符串的区别是*pc现在可以被修改

现在让我们来认真阅读以下代码 运行结果会是什么呢?

int main()
{char str1[] = "Hello world";char str2[] = "Hello world";const char* str3 = "Hello world";const char* str4 = "Hello world";if (str1 == str2)printf("str1=str2\n");elseprintf("str1!=str2\n");if (str3 == str4)printf("str3=str4\n");elseprintf("str3!=str4\n");return 0;
}

运行结果如下

可以看出str1与str2不相等 而str3和str4却相等 这是为什么呢? 

原因分析:

  1. str1 和 str2 的比较 (str1 == str2)

    • str1 和 str2 是两个独立的字符数组,分别存储 "Hello world"
    • 数组名在比较时会被转换为指向数组首元素的指针(即 &str1[0] 和 &str2[0])。
    • 由于 str1 和 str2 是两个不同的数组,它们的地址不同,所以 str1 == str2 为 false,输出 str1!=str2
  2. str3 和 str4 的比较 (str3 == str4)

    • str3 和 str4 是指向字符串常量的指针,且它们的值都是 "Hello world"
    • 编译器会对相同的字符串常量进行优化(称为 字符串池化,String Interning),即多个相同的字符串常量在内存中只存储一份。
    • 因此,str3 和 str4 实际上指向同一个内存地址,所以 str3 == str4 为 true,输出 str3=str4

关键区别:

  • 字符数组 (char[]) 会分配独立的内存空间,即使内容相同,地址也不同。
  • 字符串常量 (const char*) 可能被优化为共享同一内存,因此相同内容的字符串常量可能指向同一地址。

而如果想比较字符串的内容是否相同,应该使用 strcmp 函数,而不是直接比较指针:

if (strcmp(str1, str2) == 0)  // 比较内容是否相同printf("str1 和 str2 内容相同\n");
elseprintf("str1 和 str2 内容不同\n");

strcmp函数简要功能如下 

具体了解请访问strcmp - C++ Reference

二. 数组指针变量

在学习数组指针前 让我们来回顾一下 字符指针 整型指针

字符指针: char* p   ----指向字符的指针 存放的是字符的地址 char *p=&ch;

整形指针: int* p      ----指向整型的指针 存放的是整形的地址  int a=10;  p=&a;

数组指针:               ----指向数组的指针  存放的是数组的地址

即数组指针变量是指向数组的指针,且在C/C++中用于处理多维数组和动态数组操作。

而数组的地址我们也曾经遇见过 同学们不妨通过以下代码回想一下

int main()
{int arr[10] = { 0 };arr;  //首元素的地址&arr[0];&arr;//取出的是整个数组的地址---数组的地址return 0;
}

相信已经有同学分不清了  现在让我们来区别一下指针数组和数组指针

特性数组指针 (int (*p)[N])指针数组 (int *p[N])
本质一个指针,指向整个数组一个数组,元素全是指针
声明方式int (*p)[5];int *p[5];
内存占用指针大小(通常8字节)N个指针的大小(如5个指针=40字节)
存储内容存储数组的首地址存储多个指针(地址)
典型用途处理二维数组存储多个字符串/动态数组
sizeof结果sizeof(p)=指针大小sizeof(p)=N×指针大小

直观理解:

数组指针 → 指向数组的指针

int arr[3][4];
int (*p)[4] = arr;  // p指向arr的第一行(一个包含4个int的数组)
  • p+1会跳过整个子数组(移动4*sizeof(int)字节)

指针数组 → 存放指针的数组

char *strs[3] = {"Hello", "World", "!"};
  • strs[1]返回第二个字符串的地址("World"的首地址)

关键区别: 

操作数组指针 (int (*p)[4])指针数组 (int *p[4])
定义指向int[4]的指针包含4个int*的数组
p+1的偏移量16字节(假设int=4字节)8字节(指针大小)

记忆口诀

星号括起来是指针,星号不括是数组

  • int (*p)[N] → 星号被括号括住,强调是指针
  • int *p[N] → 星号没被括,强调是数组

如何使用数组指针来打印数组里的值呢? 如下

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{int arr[] = {1,2,3,4,5,6,7,8,9};int (*p)[9] = arr;//(*p)得到arr的地址 [i]表示调用arr里的第几个元素for (int i = 0;i < 9;i++){printf("%d  ", (*p)[i]);}
}

但这种写法似乎更加复杂了 并没有什么优势

但其实我们并不会在这种情况使用数组指针 下面让我们继续深入学习

三. 二维数组传参

3.1 二维数组的本质

二维数组是 “数组的数组”,在内存中仍然是连续存储的线性结构。例如:

int arr[3][4] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}
};

内存布局:

[1][2][3][4]  [5][6][7][8]  [9][10][11][12]
  • 每行 arr[i] 是一个一维数组,类型是 int[4]
  • arr 本身是 “指向int[4]的指针”(即 int (*)[4]

3.2 访问方式与地址计算

  • arr[i][j] 的地址&arr[0][0] + i * 4 + j
    (假设 int 占4字节,4是列数)
  • 行指针 arr[i]:等价于 *(arr + i)
  • 元素 arr[i][j]:等价于 *(*(arr + i) + j)

3.3 二维数组的传参方式

(1) 标准方式(必须指定列数)

void print(int arr[][4], int rows){for(int i=0; i<rows; i++){for(int j=0; j<4; j++){printf("%d ", arr[i][j]);}printf("\n");}
}

本质arr[][4] 会被编译器转换为 int (*)[4]

(2) 数组指针方式

void print(int (*arr)[4], int rows) {// 与上述代码完全等价
}

关键点

  • arr+1 会跳过 16字节(4个int
  • 必须指定列数,否则无法计算步长

(3) 错误方式

void print(int **arr, int rows, int cols) { // 错误!静态二维数组不是二级指针
}

下面让我们具体运行来观察一下二维数组传参

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
text(int(*arr)[5], int r,int c)
{for (int i = 0;i < r;i++){for (int j = 0;j< c;j++){printf("%d  ", *(*(arr + i)+j));}}
}
int main()
{int arr[3][5] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};text(arr, 3, 5);return 0;
}

注意:

*(arr+i) = arr[i] 因为二维数组的数组名表示第一行的地址  +i表示跳过 i 行  并且*(*(arr+i)+j) = arr[ i ][ j ]  

二维数组传参本质上也是传递了地址 传递的是第一行这个一维数组的地址 

3.4 深入解析 *(*(arr+i)+j) 与 arr[i][j] 的等价性

1. 关键概念拆解

表达式类型含义
arrint (*)[3]指向第一行({1,2,3})的指针
arr + iint (*)[3]指向第i行的指针
*(arr + i)int *第i行的首元素地址(退化成一维)
*(arr+i) + jint *第i行第j个元素的地址
*(*(arr+i)+j)int第i行第j个元素的值

2. 与下标访问的对应关系

arr[i][j]  ≡  *(*(arr + i) + j)

编译器实际处理
所有 arr[i][j] 最终都会被转换为指针运算形式。

3. 为什么需要列数?

  • arr + i 的步长取决于列数(sizeof(int[N])
  • 若未指定列数(如 int arr[][]),编译器无法计算 arr + i 的偏移量

4. 典型考题示例

题目:以下代码输出什么?

int arr[2][3] = {{1,2,3}, {4,5,6}};
printf("%d\n", *(*(arr + 1) + 2));

答案6
解析
*(arr + 1) 指向第二行 {4,5,6}*(arr + 1) + 2 指向 6,解引用后得到值 6

掌握这个核心等价关系,就能彻底理解二维数组的指针运算! 🎯

四. 函数指针变量

4.1 函数指针变量的创建

让我们回忆一下之前的指针内容

字符指针: 存放的是字符的地址 指向的就是字符变量

整型指针: 存放的是整型的地址 指向的是整行变量

数组指针: 存放的是数组的地址 指向的是数组

函数指针: 存放的是函数的地址 指向的是函数

那函数的地址怎么得到的呢?

我们知道数组的地址是通过&+数组名得到的 那函数的地址是通过&+函数名得到的吗?

答案是 是的 函数的地址就是通过&+函数名获得

可以看到 地区打印了函数的地址

那函数和数组一样吗? 数组名代表了数组首元素的地址  那函数名代表什么呢? 让我们来试试

可以看到 打印出来的一模一样 那函数名是首函数的地址吗?  显然没有这个说法 

他们俩所得到的都是函数的地址 并没有什么区别 

那什么是函数指针变量呢?

函数指针变量是一个指向函数的指针,它存储了函数的地址,可以通过该指针间接调用函数。函数指针的类型由函数的返回类型参数列表决定。

1. 函数指针的声明

函数指针的声明语法如下:

返回类型 (*指针变量名)(参数类型1, 参数类型2, ...);

示例

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{int(*pf)(int,int) = &Add;//pf就是函数指针变量return 0;
}

那存放下面这个函数的函数指针变量该如何写呢?

int* text(int n, char* p)
{}

通过类比 不难写出

int* (*pf)(int, char*)=&text;//pf就是函数指针变量

4.2 函数指针变量的使用

我们知道通过解引用操作符 可以通过地址来找到存放的变量 那解引用函数指针变量是否就可以使用函数了呢?  答案是 是的 例子如下

可以看到 我通过*pf 实现了对函数的调用 并且传入参数( 3 , 5 )用r来接受 因为函数返回值是int类型 所以r也是int类型

其次 我们知道我们可以通过函数名来调用函数 即Add( 3 , 5 ) 那函数指针变量存放的又是函数名

那我们是否可以直接通过函数指针变量来使用函数呢?  不妨让我们试试

可以看到 无论是函数名调用 还是函数指针变量调用  还是解引用函数指针变量调用 都可以实现对函数的使用

4.3 两段"有趣"代码的讲解

那让我们来思考思考下面这段代码的含义是什么呢?

(*(void(*)())0)();

相信大家看了之后都会感觉 浑身不自在吧 现在让我们一起来解读一下这段代码

4.3.1 解析 (*(void(*)())0)();

这个表达式看起来复杂,但它实际上是一个 函数指针强制转换 + 调用 的典型例子。我们可以一步步拆解它的含义。


1. 表达式拆解

(*(void(*)())0)();

可以分解为:

  1. (void(*)())0:将 0 强制转换为一个 函数指针
  2. *(void(*)())0解引用 这个函数指针,得到函数本身。
  3. (*(void(*)())0)();调用 这个函数。

2. 详细分析

(1) void(*)() 是什么?

  • void(*)() 是一个 函数指针类型,表示:
    • 返回类型:void(无返回值)
    • 参数列表:()(无参数)
  • 所以,void(*)() 是一个 指向无参无返回值函数的指针

(2) (void(*)())0:将 0 强制转换为函数指针

  • 0 是一个整数,代表 内存地址 0x0(NULL 指针)。
  • (void(*)())0 表示 把 0 强制转换为一个函数指针,即:
    void (*func_ptr)() = (void(*)())0;  // 现在 func_ptr 指向地址 0
    

(3) *(void(*)())0:解引用函数指针

  • *(void(*)())0 相当于:
    void (*func_ptr)() = (void(*)())0;
    *func_ptr;  // 解引用,得到函数本身
    
  • 在 C 语言中,函数指针解引用后仍然是函数,所以 *func_ptr 和 func_ptr 是等价的(见上一节分析)。

(4) (*(void(*)())0)();:调用这个函数

  • 最终,(*(void(*)())0)(); 相当于:
    void (*func_ptr)() = (void(*)())0;
    (*func_ptr)();  // 调用地址 0 处的函数
    
  • 或者更简单的写法(因为 func_ptr() 和 (*func_ptr)() 等价):
    ((void(*)())0)();  // 直接调用
4.3.2 解析void(* signal(int, void(*)(int)))(int);

这个声明 void(* signal(int, void(*)(int)))(int); 是一个函数声明,它定义了一个名为 signal 的函数。为了理解这个声明,我们可以逐步解析它:

  1. 最内层部分 void(*)(int):

    • 这是一个函数指针类型,指向一个接受 int 参数并返回 void 的函数。

    • 例如,void handler(int sig); 这样的函数可以匹配这个指针类型。

  2. 中间部分 signal(int, void(*)(int)):

    • signal 是一个函数,它接受两个参数:

      • 第一个参数是 int 类型。

      • 第二个参数是 void(*)(int) 类型(即上述的函数指针)。

    • 因此,signal 的函数原型可以理解为:
      void (*signal(int sig, void (*handler)(int)))(int);
      
  3. 最外层部分 void(* ... )(int):

    • signal 函数的返回值也是一个函数指针,类型为 void(*)(int)

    • 也就是说,signal 函数返回一个指向“接受 int 参数并返回 void 的函数”的指针。

简化理解:

  • signal 是一个函数,它接受一个 int 和一个函数指针,并返回一个同类型的函数指针。

总结:

void(* signal(int, void(*)(int)))(int); 声明了一个函数 signal,它:

  1. 接受两个参数:int 和 void(*)(int)(函数指针)。
  2. 返回一个 void(*)(int) 类型的函数指针。

这种写法在 C 语言中很常见,尤其是在处理回调函数或函数指针时。

4.4 typedef关键字讲解

typedef 关键字解释

typedef 是 C/C++ 中的一个关键字,用于为现有的数据类型(包括基本类型、结构体、联合体、枚举、函数指针等)定义一个新的别名,使代码更易读、更简洁。


4.4.1 基本语法
typedef <原类型> <新别名>;
  • <原类型>:可以是 intfloatcharstructunionenum 或函数指针等。
  • <新别名>:你给这个类型取的新名字。

4.4.2 常见用法

(1) 为基本类型定义别名

typedef unsigned int uint;  // 定义 uint 代替 unsigned int
typedef float real;         // 定义 real 代替 floatuint age = 25;              // 等同于 unsigned int age = 25;
real weight = 65.5f;        // 等同于 float weight = 65.5f;

(2) 为结构体定义别名

传统写法(需要 struct 关键字)

struct Point {int x;int y;
};struct Point p1;  // 必须写 struct Point

使用 typedef 简化

typedef struct {int x;int y;
} Point;  // 定义 Point 代替 struct { ... }Point p1;  // 直接使用 Point,不需要写 struct

(3) 为指针类型定义别名

typedef int* IntPtr;  // IntPtr 是 int* 的别名int a = 10;
IntPtr p = &a;        // 等同于 int* p = &a;

(4) 为函数指针定义别名

原始写法(复杂)

void (*funcPtr)(int);  // funcPtr 是一个指向 void(int) 函数的指针

使用 typedef 简化

typedef void (*FuncPtr)(int);  // FuncPtr 是 void(*)(int) 的别名void foo(int x) { printf("%d\n", x); }FuncPtr fp = foo;  // 等同于 void (*fp)(int) = foo;
fp(10);            // 调用 foo(10)

4.4.3 结合 typedef 解析 signal 函数

原声明:

void (*signal(int, void(*)(int)))(int);

使用 typedef 简化:

typedef void (*SignalHandler)(int);  // 定义 SignalHandler 代替 void(*)(int)SignalHandler signal(int sig, SignalHandler handler);  // 更清晰的声明
  • SignalHandler 是一个函数指针类型,指向 void(int) 函数。
  • signal 是一个函数,接受 int 和 SignalHandler,并返回 SignalHandler

4.4.4 typedef 的优点
  1. 提高可读性:复杂的类型(如函数指针)可以用更直观的名字表示。
  2. 减少重复代码:避免反复写冗长的类型声明。
  3. 便于维护:修改类型时只需改 typedef 定义,而不需要修改所有使用的地方。

4.4.5 总结
用途示例
基本类型别名typedef int Int32;
结构体别名typedef struct { ... } Point;
指针别名typedef int* IntPtr;
函数指针别名typedef void (*FuncPtr)(int);

typedef 是 C/C++ 中非常重要的关键字,能显著提升代码的可读性和可维护性,尤其是在处理复杂类型(如函数指针)时非常有用。

五. 函数指针数组

首先我们要明白什么是函数指针数组 

5.1 基本概念

1. 函数指针

函数指针是指向函数的指针变量。声明一个函数指针需要指定它指向的函数的返回类型和参数类型。

// 函数原型
int add(int a, int b);
int subtract(int a, int b);// 函数指针声明
int (*funcPtr)(int, int);// 指向add函数
funcPtr = add;

2. 函数指针数组

函数指针数组是存储多个函数指针的数组。

// 声明一个包含两个函数指针的数组
int (*funcArray[2])(int, int);// 初始化数组
funcArray[0] = add;
funcArray[1] = subtract;

5.2 详细用法

1. 声明函数指针数组

// 返回类型 (*数组名[数组大小])(参数列表)
double (*operations[4])(double, double);

2. 初始化函数指针数组

double add(double a, double b) { return a + b; }
double sub(double a, double b) { return a - b; }
double mul(double a, double b) { return a * b; }
double div(double a, double b) { return a / b; }// 初始化数组
operations[0] = add;
operations[1] = sub;
operations[2] = mul;
operations[3] = div;

3. 使用函数指针数组

double result;
int choice = 2; // 假设用户选择乘法
double x = 5.0, y = 3.0;// 通过索引调用函数
result = operations[choice](x, y);
printf("结果: %.2f\n", result); // 输出: 15.00

5.3 实际应用示例

1. 计算器实现

#include <stdio.h>// 定义运算函数
double add(double a, double b) { return a + b; }
double sub(double a, double b) { return a - b; }
double mul(double a, double b) { return a * b; }
double div(double a, double b) { return a / b; }int main() {// 声明并初始化函数指针数组double (*ops[4])(double, double) = {add, sub, mul, div};int choice;double x, y;printf("选择运算:\n0. 加\n1. 减\n2. 乘\n3. 除\n");scanf("%d", &choice);printf("输入两个数字: ");scanf("%lf %lf", &x, &y);// 调用选中的函数double result = ops[choice](x, y);printf("结果: %.2f\n", result);return 0;
}

运行结果如下:

六. 转移表

转移表(Jump Table)

转移表(也称为跳转表)是一种使用函数指针数组来实现多路分支的技术,它比传统的switch-case语句更高效、更灵活。

基本概念

转移表本质上是一个函数指针数组,通过数组索引来选择和调用不同的函数,避免了冗长的条件判断。

转移表 vs switch-case

switch-case实现

void handleCommand(int cmd) {switch(cmd) {case 0: cmd0(); break;case 1: cmd1(); break;case 2: cmd2(); break;// ...default: defaultHandler();}
}

转移表实现

// 定义命令处理函数
void cmd0() { /* ... */ }
void cmd1() { /* ... */ }
void cmd2() { /* ... */ }
void defaultHandler() { /* ... */ }// 创建转移表
typedef void (*CommandHandler)(void);
CommandHandler jumpTable[] = {cmd0, cmd1, cmd2};void handleCommand(int cmd) {if (cmd >= 0 && cmd < sizeof(jumpTable)/sizeof(jumpTable[0])) {jumpTable[cmd]();} else {defaultHandler();}
}

转移表优势

  1. 效率更高:直接通过索引访问,时间复杂度O(1),而switch-case可能需要多次比较

  2. 代码更简洁:特别是当分支很多时

  3. 更易维护:添加新功能只需扩展数组,不需要修改逻辑结构

  4. 动态性:可以在运行时修改函数指针

实际应用示例

1. 简单计算器

#include <stdio.h>// 运算函数
double add(double a, double b) { return a + b; }
double sub(double a, double b) { return a - b; }
double mul(double a, double b) { return a * b; }
double div(double a, double b) { return a / b; }// 定义转移表
typedef double (*Operation)(double, double);
Operation operations[] = {add, sub, mul, div};int main() {int choice;double x, y;printf("选择运算(0-3): ");scanf("%d", &choice);printf("输入两个数字: ");scanf("%lf %lf", &x, &y);if (choice >= 0 && choice < sizeof(operations)/sizeof(operations[0])) {double result = operations[choice](x, y);printf("结果: %.2f\n", result);} else {printf("无效选择\n");}return 0;
}

以上就是本篇内容 希望能对你有所帮助

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/905732.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity:场景管理系统 —— SceneManagement 模块

目录 &#x1f3ac; 什么是 Scene&#xff08;场景&#xff09;&#xff1f; Unity 项目中的 Scene 通常负责什么&#xff1f; &#x1f30d; 一个 Scene 包含哪些元素&#xff1f; Scene 的切换与管理 &#x1f4c1; 如何创建与管理 Scenes&#xff1f; 什么是Scene Man…

内容中台重构企业知识管理路径

智能元数据驱动知识治理 现代企业知识管理的核心挑战在于海量非结构化数据的有效治理。通过智能元数据分类引擎&#xff0c;系统可自动识别文档属性并生成多维标签体系&#xff0c;例如将技术手册按产品版本、功能模块、适用场景进行动态标注。这种动态元数据框架不仅支持跨部…

Vue3:脚手架

工程环境配置 1.安装nodejs 这里我已经安装过了&#xff0c;只需要打开链接Node.js — Run JavaScript Everywhere直接下载nodejs&#xff0c;安装直接一直下一步下一步 安装完成之后我们来使用电脑的命令行窗口检查一下版本 查看npm源 这里npm源的地址是淘宝的源&#xff0…

悦数图数据库一体机发布,让复杂关联计算开箱即用

在金融风控、政务治理、能源监测等关键领域&#xff0c;复杂数据关联分析已成为业务决策的核心需求。然而&#xff0c;信创场景的特殊性——全栈自主可控、海量实时计算、系统高可用性——对传统技术架构提出了近乎苛刻的要求。悦数图数据库一体机应运而生&#xff0c;以软硬协…

收放卷“材料停机减速距离“计算FC(算法公式+ST源代码+C++代码)

PLC运动控制基础系列之梯形速度曲线 PLC运动控制基础系列之梯形速度曲线_三菱运动控制模块梯形加减速-CSDN博客文章浏览阅读3.2k次,点赞3次,收藏7次。本文是关于PLC运动控制的基础教程,重点介绍了梯形速度曲线的概念、计算和应用。讨论了梯形加减速在启动和停止阶段的作用,…

Centos7系统(最小化安装)安装zabbix7版本详细文章、nginx源代码配置、php源代码、mysql-yum安装

zabbix官网链接下载zabbix源代码安装包 选择zabbix版本&#xff08;此文章使用zabbix7.0版本&#xff09; 安装之前由于是最小化安装centos7安装一些开发环境和工具包 文章使用国内阿里源 cd /etc/yum.repos.d/;curl -O https://mirrors.aliyun.com/repo/epel-7.repo;curl -…

描述性统计图表

一、核心图表类型与用途 1、直方图(Histogram) (1)定义:用连续矩形表示数据分布,横轴为数据区间,纵轴为频数或频率。 (2)用途:展示数据分布形态(对称、偏态)、识别离群值。 (3)适用场景:分析连续型变量的分布特征,如收入分布、考试成绩分布。 2、箱线图(Box P…

ThinkPad X250电池换电池芯(理论技术储备)

参考&#xff1a;笔记本电池换电芯的经验与心得分享 - 经典ThinkPad专区 - 专门网 换电池芯&#xff0c;需要克服以下问题&#xff1a; 1 拆电池。由于是超声波焊接&#xff0c;拆解比较费力&#xff0c;如果暴力撬&#xff0c;有可能导致电池壳变形... 2 替换电池芯的时候如…

Java(基础) day01 初识Java

目录 一、运行Java程序 二、基本数据类型 1、整数类型 ​编辑2、浮点型 3、字符型 4、布尔类型 一、运行Java程序 Java是一门半编译型、半解释型语言。先通过javac编译程序把xxx.java源文件进行编译&#xff0c;编译后生成的.class文件是由字节码组成的平台无关、面向JVM的文…

【美团】Java后端一面复盘|网络+线程+MySQL+Redis+设计模式+手撕算法

&#x1f4cd; 面试公司&#xff1a;美团 &#x1f3af; 面试岗位&#xff1a;Java后端开发工程师 &#x1f4de; 面试形式&#xff1a;电话面试 &#x1f552; 面试时长&#xff1a;约 50 分钟 &#x1f501; 面试轮次&#xff1a;第一轮技术面 ✨ 面试整体节奏&#xff1a; …

Go语言八股文之Mysql锁详解

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…

实战案例:采集 51job 企业招聘信息

本文将带你从零开始&#xff0c;借助 Feapder 快速搭建一个企业级招聘信息数据管道。在“基础概念”部分&#xff0c;我们先了解什么是数据管道和 Feapder&#xff1b;“生动比喻”用日常场景帮助你快速理解爬虫组件&#xff1b;“技术场景”介绍本项目中如何使用代理等采集策略…

GMT之Bash语言使用

GMT的操作有自己的逻辑和“命令”&#xff0c;但GMT是可以用Bash语言控制的&#xff0c;所以常常以.sh为后缀写GMT程序。 GMT程序运行步骤如下&#xff1a; 采用cd &#xff0c;定位到指定文件夹&#xff1b;以sh ***.sh运行GMT&#xff0c;得到结果。 另外&#xff0c;遇到…

整合Redis

整合Redis 引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency><groupId>org.apache.commons</groupId><art…

Vue3——Watch侦听器

目录 手动指定监听对象 侦听ref对象 侦听ref对象中的某个属性 reactive写法 watchEffect 自动侦听 多源侦听 一次性侦听器 watch 是⼀个⽤于观察和响应Vue响应式系统中数据变化的⽅法。它允许你指定⼀个数据源&#xff08;可以是 响应式引⽤、计算属性、组件的属性等&#xf…

1、数据结构与算法(Python版-啃书)-绪论

1.1 计算机问题求解 一般而言&#xff0c;人们需要的不是解决一个具体问题的程序&#xff0c;而是解决一类问题的程序。 对于求平方根这样的简单问题&#xff0c;人们希望的也不是专用于求某个数(例如2)的平方根的函数&#xff0c;而是能求任何数的平方根的函数。 用计算机解…

微信小程序之将轮播图设计为组件

在components文件夹上点右键&#xff0c;新建component&#xff0c;命名为swiper 然后将我们之前的代码都拷贝到对应文件中&#xff0c; 然后我们的页面要引用这个组件&#xff0c; 在pages\index\index.json中引入&#xff1a; { "usingComponents": {"van…

【视频】解决FFmpeg将RTSP转RTMP流时,出现的卡死、出错等问题

【视频】郭老二博文之:图像视频汇总 1、简述 如果不修改图像内容,可以使用FFmpeg命令来将RTSP转RTMP流。 SRS视频服务器就是这么干的,它没有使用FFmpeg接口,而是直接使用FFmpeg命令来转流。 但是在使用中,约到了一些问题,比如转流时卡死、转流出错等等,下面描述怎么解…

报销单业务笔记

文章目录 业务点业务点-对公对私业务点-多系统标志 特殊业务入参入参报文 出参出参报文中间的逻辑多对多关系 其他应该是整体成功还是可以部分成功这种多对多关多关系有没有优雅的判断方式 报销单是个通用场景&#xff0c;有通用逻辑&#xff0c;在此基础上进行适度定制&#x…

25软考【软件评测师】:10天极限冲刺攻略(附知识点解析+冲刺攻略)

距离2025上半年“软件评测师”考试已经只剩最后一周多了&#xff0c;还没有准备好的小伙伴赶紧行动起来。为了帮助大家更好的冲刺学习&#xff0c;特此提供一份考前冲刺攻略。本指南包括考情分析、冲刺攻略两个部分&#xff0c;可以参考此指南进行最后的复习要领&#xff0c;相…