【C语言】指针篇

news2025/3/13 13:35:36

目录

  • C 语言指针概述
    • 指针的声明和初始化
      • 声明指针
      • 初始化指针
    • 指针的操作
      • 解引用操作
      • 指针算术运算
    • 指针的用途
      • 动态内存分配
      • 作为函数参数
  • 指针与数组
    • 数组名作为指针
    • 通过指针访问数组元素
    • 指针算术和数组
    • 数组作为函数参数
    • 指针数组和数组指针
      • 指针数组
      • 数组指针
  • 函数指针
    • 函数指针的定义和声明
    • 函数指针的初始化和使用
    • 函数指针作为函数参数(回调函数)
    • 函数指针数组
  • 动态内存分配
    • 概念
    • 动态内存分配函数
      • malloc 函数
      • calloc 函数
      • realloc 函数
      • free 函数
    • 示例代码
    • 注意事项
  • 常见错误与规避
    • 内存泄漏(Memory Leak)
    • 空指针引用(Null Pointer Dereference)
    • 重复释放内存(Double Free)
    • 越界访问(Buffer Overflow)
    • realloc 使用不当

C 语言指针概述

在 C 语言中,指针是一个非常重要且强大的概念。它是一个变量,其值为另一个变量的地址,即内存位置的直接地址。可以把指针想象成一个特殊的变量,它存储的不是普通的数据,而是内存中某个变量的地址。通过指针,我们可以直接访问和操作该内存地址上存储的数据。

指针的声明和初始化

声明指针

在 C 语言中,声明指针的一般语法如下:

数据类型 *指针变量名;

其中,数据类型 表示该指针所指向的变量的数据类型,* 是指针声明符,用于表明这是一个指针变量。例如:

int *p;  // 声明一个指向整型变量的指针p
float *q; // 声明一个指向浮点型变量的指针q

初始化指针

指针可以在声明时进行初始化,也可以在声明后再赋值。指针初始化时,需要将一个变量的地址赋给它。使用 & 运算符可以获取变量的地址。示例如下:

#include <stdio.h>

int main() {
    int num = 10;
    int *p = &num;  // 声明并初始化指针p,使其指向变量num

    printf("变量num的地址: %p\n", &num);
    printf("指针p存储的地址: %p\n", p);

    return 0;
}

在上述代码中,&num 表示变量 num 的地址,将其赋给指针 p,这样 p 就指向了 num。

指针的操作

解引用操作

通过指针访问其所指向的变量的值,需要使用 * 运算符,这称为解引用操作。示例如下:

#include <stdio.h>

int main() {
    int num = 10;
    int *p = &num;

    printf("变量num的值: %d\n", num);
    printf("通过指针p访问num的值: %d\n", *p);

    *p = 20;  // 通过指针p修改num的值
    printf("修改后变量num的值: %d\n", num);

    return 0;
}

在上述代码中,*p 表示指针 p 所指向的变量的值,通过 *p = 20; 可以修改 num 的值。

指针算术运算

指针可以进行一些算术运算,如加法、减法等。指针算术运算的结果取决于指针所指向的数据类型的大小。示例如下:

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int *p = arr;  // 指针p指向数组arr的首元素

    printf("p指向的元素的值: %d\n", *p);
    p++;  // 指针p向后移动一个位置
    printf("p移动后指向的元素的值: %d\n", *p);

    return 0;
}

在上述代码中,p++ 使指针 p 向后移动一个 int 类型的位置,即移动了 sizeof(int) 个字节。

指针的用途

动态内存分配

C 语言提供了一些函数(如 malloc、calloc、realloc 等)用于动态分配内存,这些函数返回的是一个指针,通过指针可以访问和管理动态分配的内存。示例如下:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *p = (int *)malloc(sizeof(int));  // 动态分配一个int类型的内存空间
    if (p == NULL) {
        printf("内存分配失败\n");
        return 1;
    }

    *p = 10;
    printf("动态分配内存中存储的值: %d\n", *p);

    free(p);  // 释放动态分配的内存
    return 0;
}

作为函数参数

指针可以作为函数参数,通过指针传递参数可以在函数内部修改实参的值。示例如下:

#include <stdio.h>

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 10, y = 20;
    printf("交换前: x = %d, y = %d\n", x, y);
    swap(&x, &y);
    printf("交换后: x = %d, y = %d\n", x, y);

    return 0;
}

在上述代码中,swap 函数接受两个指针作为参数,通过指针可以交换 x 和 y 的值。

指针与数组

在 C 语言中,指针和数组有着密切的联系。

数组名作为指针

在 C 语言里,数组名在大多数表达式中会被隐式转换为指向数组首元素的指针。也就是说,数组名代表了数组首元素的地址。
示例代码:

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    // 打印数组首元素的地址
    printf("数组首元素的地址(使用&arr[0]): %p\n", &arr[0]);
    // 打印数组名代表的地址
    printf("数组名代表的地址: %p\n", arr);
    return 0;
}

在上述代码中,&arr[0] 是获取数组 arr 首元素的地址,而 arr 本身在这个表达式中也被解释为指向数组首元素的指针,所以它们的值是相同的。

通过指针访问数组元素

由于数组名可以当作指针使用,因此可以借助指针来访问数组中的元素。
示例代码:

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int *p = arr;  // 指针p指向数组arr的首元素
    for (int i = 0; i < 5; i++) {
        // 通过指针访问数组元素
        printf("arr[%d] = %d\n", i, *(p + i));
    }
    return 0;
}
  • int *p = arr;:将指针 p 指向数组 arr 的首元素。
  • *(p + i):p + i 表示指针 p 向后移动 i 个位置(每个位置的大小为 sizeof(int)),*(p + i) 则是对移动后的指针进行解引用操作,从而访问对应位置的数组元素。

指针算术和数组

指针可以进行算术运算,这使得我们能更灵活地访问数组元素。
示例代码:

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int *p = arr;
    for (int i = 0; i < 5; i++) {
        // 先使用指针p指向的元素的值,然后指针p向后移动一个位置
        printf("%d ", *p++);
    }
    printf("\n");
    return 0;
}

*p++:由于 ++ 运算符的优先级高于 * 运算符,所以先取 p 所指向的元素的值,然后 p 向后移动一个位置(移动的字节数为 sizeof(int))。

数组作为函数参数

当数组作为函数参数传递时,实际上传递的是数组首元素的地址,也就是一个指针。
示例代码:

#include <stdio.h>

// 函数接受一个整型指针和数组的长度作为参数
void printArray(int *arr, int length) {
    for (int i = 0; i < length; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    // 调用函数并传递数组名和数组长度
    printArray(arr, 5);
    return 0;
}
  • void printArray(int *arr, int length):函数 printArray 的第一个参数是一个整型指针,它接收数组首元素的地址。
  • printArray(arr, 5);:在调用 printArray 函数时,传递的 arr 被隐式转换为指向数组首元素的指针。

指针数组和数组指针

指针数组

指针数组是一个数组,数组中的每个元素都是一个指针。
示例代码:

#include <stdio.h>

int main() {
    int a = 1, b = 2, c = 3;
    // 定义一个指针数组
    int *ptrArr[3] = {&a, &b, &c};
    for (int i = 0; i < 3; i++) {
        printf("%d ", *ptrArr[i]);
    }
    printf("\n");
    return 0;
}

int *ptrArr[3] 定义了一个包含 3 个元素的指针数组,每个元素都是一个指向 int 类型的指针。

数组指针

数组指针是一个指针,它指向一个数组。
示例代码:

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    // 定义一个数组指针
    int (*p)[5] = &arr;
    for (int i = 0; i < 5; i++) {
        printf("%d ", (*p)[i]);
    }
    printf("\n");
    return 0;
}

int (*p)[5] 定义了一个数组指针 p,它指向一个包含 5 个 int 类型元素的数组。&arr 是数组 arr 的地址,将其赋值给 p,(*p)[i] 用于访问数组中的元素。

函数指针

在 C 语言中,函数指针是一种特殊的指针,它指向的是函数而非普通的变量。函数指针在很多场景下都非常有用,比如实现回调函数、创建函数表等。

函数指针的定义和声明

函数指针的声明需要指定函数的返回类型和参数列表,其一般语法形式如下:

返回类型 (*指针变量名)(参数列表);
  • 返回类型:表示该指针所指向的函数的返回值类型。
  • 指针变量名:是函数指针的名称。
  • 参数列表:指定该指针所指向的函数的参数类型和数量。

以下是一个简单的函数指针声明示例:

#include <stdio.h>

// 声明一个函数指针,指向返回值为int,接受两个int类型参数的函数
int (*funcPtr)(int, int);

函数指针的初始化和使用

函数指针需要被初始化为指向一个具体的函数,在使用时可以通过该指针调用所指向的函数。
示例代码:

#include <stdio.h>

// 定义一个加法函数
int add(int a, int b) {
    return a + b;
}

int main() {
    // 声明一个函数指针,并初始化为指向add函数
    int (*funcPtr)(int, int) = add;

    // 使用函数指针调用add函数
    int result = funcPtr(3, 5);
    printf("3 + 5 = %d\n", result);

    return 0;
}
  • 函数定义:add 函数接受两个 int 类型的参数,并返回它们的和。
  • 函数指针声明和初始化:int (*funcPtr)(int, int) = add; 声明了一个函数指针 funcPtr,并将其初始化为指向 add 函数。这里 add 是函数名,在这种上下文中,它会被隐式转换为指向该函数的指针。
  • 通过函数指针调用函数:funcPtr(3, 5); 就像直接调用 add 函数一样,通过函数指针 funcPtr 调用了 add 函数。

函数指针作为函数参数(回调函数)

函数指针的一个重要应用是实现回调函数。回调函数是指在某个事件发生时或某个特定条件满足时被调用的函数,通常将回调函数的指针作为参数传递给另一个函数。
示例代码:

#include <stdio.h>

// 定义一个回调函数类型
typedef int (*Callback)(int, int);

// 定义一个加法函数
int add(int a, int b) {
    return a + b;
}

// 定义一个减法函数
int subtract(int a, int b) {
    return a - b;
}

// 执行操作的函数,接受一个回调函数指针作为参数
int performOperation(int a, int b, Callback operation) {
    return operation(a, b);
}

int main() {
    int num1 = 10, num2 = 5;

    // 使用加法函数进行操作
    int sum = performOperation(num1, num2, add);
    printf("%d + %d = %d\n", num1, num2, sum);

    // 使用减法函数进行操作
    int difference = performOperation(num1, num2, subtract);
    printf("%d - %d = %d\n", num1, num2, difference);

    return 0;
}
  • 定义回调函数类型:typedef int (*Callback)(int, int); 使用 typedef 定义了一个函数指针类型 Callback,它指向返回值为 int,接受两个 int 类型参数的函数。
  • 定义具体的操作函数:add 和 subtract 分别实现了加法和减法功能。
  • 执行操作的函数:performOperation 函数接受两个 int 类型的参数和一个 Callback 类型的函数指针,在函数内部通过该指针调用相应的函数。
  • 在 main 函数中使用:分别将 add 和 subtract 函数作为参数传递给 performOperation 函数,实现不同的操作。

函数指针数组

函数指针数组是一个数组,数组中的每个元素都是一个函数指针。它可以用于根据不同的条件选择调用不同的函数。
示例代码:

#include <stdio.h>

// 定义一个加法函数
int add(int a, int b) {
    return a + b;
}

// 定义一个减法函数
int subtract(int a, int b) {
    return a - b;
}

int main() {
    // 定义一个函数指针数组
    int (*funcArray[2])(int, int) = {add, subtract};

    int num1 = 10, num2 = 5;

    // 调用加法函数
    int sum = funcArray[0](num1, num2);
    printf("%d + %d = %d\n", num1, num2, sum);

    // 调用减法函数
    int difference = funcArray[1](num1, num2);
    printf("%d - %d = %d\n", num1, num2, difference);

    return 0;
}
  • int (*funcArray[2])(int, int) = {add, subtract}; 定义了一个包含两个元素的函数指针数组 funcArray,分别指向 add 和 subtract 函数。
  • 通过数组下标可以选择调用不同的函数。

动态内存分配

在 C 语言中,动态内存分配是一项重要的特性,它允许程序在运行时根据需要分配和释放内存,而不是在编译时就确定固定大小的内存。

概念

在程序运行过程中,有些情况下我们无法提前确定所需内存的大小,例如需要存储用户输入的一组数据,但不知道用户会输入多少个元素。这时就需要使用动态内存分配,在程序运行时根据实际需求来分配适当大小的内存空间。动态分配的内存位于堆(heap)上,与栈(stack)上的自动变量内存分配方式不同。

动态内存分配函数

C 语言标准库提供了几个用于动态内存分配的函数,主要包括 malloc、calloc、realloc 和 free。

malloc 函数

功能:malloc 函数用于分配指定字节数的连续内存空间,并返回一个指向该内存空间起始地址的指针。如果分配失败,返回 NULL。
原型:

void* malloc(size_t size);
  • 参数:size 表示需要分配的内存字节数。
  • 返回值:返回一个 void* 类型的指针,指向分配的内存空间的起始地址。

calloc 函数

功能:calloc 函数用于分配指定数量和大小的连续内存空间,并将分配的内存初始化为零。如果分配失败,返回 NULL。
原型:

void* calloc(size_t num, size_t size);
  • 参数:num 表示需要分配的元素数量,size 表示每个元素的字节数。
  • 返回值:返回一个 void* 类型的指针,指向分配的内存空间的起始地址。

realloc 函数

功能:realloc 函数用于重新调整之前分配的内存空间的大小。可以扩大或缩小已分配的内存块。如果分配失败,返回 NULL,原内存块内容保持不变。
原型:

void* realloc(void* ptr, size_t size);
  • 参数:ptr 是之前通过 malloc、calloc 或 realloc 分配的内存块的指针,size 是重新分配后的内存块大小。
  • 返回值:返回一个 void* 类型的指针,指向重新分配后的内存空间的起始地址。如果 ptr 为 NULL,则相当于调用 malloc(size);如果 size 为 0,则相当于调用 free(ptr)。

free 函数

功能:free 函数用于释放之前通过 malloc、calloc 或 realloc 分配的内存空间,将其返回给系统,以便其他程序或代码段可以使用。
原型:

void free(void* ptr);
  • 参数:ptr 是之前分配的内存块的指针。
  • 返回值:无。

示例代码

下面是使用这些函数进行动态内存分配的示例:

#include <stdio.h>
#include <stdlib.h>

int main() {
    // 使用 malloc 分配内存
    int *arr1 = (int *)malloc(5 * sizeof(int));
    if (arr1 == NULL) {
        printf("内存分配失败\n");
        return 1;
    }
    for (int i = 0; i < 5; i++) {
        arr1[i] = i;
    }
    printf("使用 malloc 分配的数组元素: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr1[i]);
    }
    printf("\n");

    // 使用 calloc 分配内存
    int *arr2 = (int *)calloc(5, sizeof(int));
    if (arr2 == NULL) {
        printf("内存分配失败\n");
        free(arr1);
        return 1;
    }
    printf("使用 calloc 分配的数组元素(初始化为 0): ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr2[i]);
    }
    printf("\n");

    // 使用 realloc 调整内存大小
    int *arr3 = (int *)realloc(arr1, 10 * sizeof(int));
    if (arr3 == NULL) {
        printf("内存重新分配失败\n");
        free(arr1);
        free(arr2);
        return 1;
    }
    arr1 = arr3;  // 更新指针
    for (int i = 5; i < 10; i++) {
        arr1[i] = i;
    }
    printf("使用 realloc 调整大小后的数组元素: ");
    for (int i = 0; i < 10; i++) {
        printf("%d ", arr1[i]);
    }
    printf("\n");

    // 释放内存
    free(arr1);
    free(arr2);

    return 0;
}

注意事项

  • 内存泄漏:如果动态分配的内存不再使用,但没有调用 free 函数释放,就会导致内存泄漏。这会使程序占用的内存不断增加,最终可能导致系统资源耗尽。
  • 空指针检查:在使用 malloc、calloc 或 realloc 分配内存后,应该检查返回的指针是否为 NULL,以确保内存分配成功。
  • 避免重复释放:不要对已经释放的内存再次调用 free 函数,这会导致未定义行为。
  • 指针更新:在使用 realloc 函数重新分配内存时,如果返回的指针与原指针不同,需要更新原指针,以避免使用无效的指针。

常见错误与规避

在使用 C 语言进行动态内存分配时,会遇到一些常见的错误,以下为你详细介绍这些错误以及相应的规避方法。

内存泄漏(Memory Leak)

错误描述
内存泄漏指的是程序在动态分配内存后,由于某些原因未能释放这些内存,导致系统中可用内存逐渐减少。随着程序的运行,内存泄漏会不断累积,最终可能导致系统资源耗尽,程序崩溃或系统运行缓慢。
示例代码:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = (int *)malloc(10 * sizeof(int));
    // 忘记释放内存
    return 0;
}

规避方法:

  • 确保每一次 malloc、calloc 或 realloc 调用都有对应的 free 调用:在使用完动态分配的内存后,及时调用 free 函数释放内存。
  • 使用结构化的代码:可以将内存分配和释放操作封装在函数中,确保在函数结束时释放内存。例如:
#include <stdio.h>
#include <stdlib.h>

void process() {
    int *ptr = (int *)malloc(10 * sizeof(int));
    if (ptr == NULL) {
        printf("内存分配失败\n");
        return;
    }
    // 使用内存
    // ...
    free(ptr); // 释放内存
}

int main() {
    process();
    return 0;
}

空指针引用(Null Pointer Dereference)

错误描述
当对一个值为 NULL 的指针进行解引用操作时,会发生空指针引用错误。这是因为 NULL 指针不指向任何有效的内存地址,对其进行解引用会导致未定义行为,通常会使程序崩溃。
示例代码:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = (int *)malloc(10 * sizeof(int));
    if (ptr == NULL) {
        // 没有检查指针是否为 NULL 就进行解引用
        *ptr = 5; 
    }
    free(ptr);
    return 0;
}

规避方法:
在使用指针之前检查其是否为 NULL:在进行动态内存分配后,立即检查返回的指针是否为 NULL,如果是则进行相应的错误处理。例如:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = (int *)malloc(10 * sizeof(int));
    if (ptr == NULL) {
        printf("内存分配失败\n");
        return 1;
    }
    *ptr = 5; // 确保指针不为 NULL 后再进行解引用
    free(ptr);
    return 0;
}

重复释放内存(Double Free)

错误描述
重复释放内存是指对同一块已经释放的内存再次调用 free 函数。这会导致未定义行为,可能会破坏内存管理系统的数据结构,使程序崩溃或产生不可预测的结果。
示例代码:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = (int *)malloc(10 * sizeof(int));
    free(ptr);
    // 重复释放内存
    free(ptr); 
    return 0;
}

规避方法:
在释放内存后将指针置为 NULL:在调用 free 函数释放内存后,将指针赋值为 NULL。这样,即使后续不小心再次调用 free 函数,也不会产生问题,因为 free(NULL) 是安全的操作。例如:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = (int *)malloc(10 * sizeof(int));
    if (ptr == NULL) {
        printf("内存分配失败\n");
        return 1;
    }
    free(ptr);
    ptr = NULL; // 将指针置为 NULL
    // 再次调用 free 不会有问题
    free(ptr); 
    return 0;
}

越界访问(Buffer Overflow)

错误描述
越界访问是指程序访问了动态分配的内存块之外的内存区域。这可能会覆盖其他重要的数据,导致程序崩溃或产生不可预期的结果,甚至可能引发安全漏洞。
示例代码:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = (int *)malloc(5 * sizeof(int));
    if (ptr == NULL) {
        printf("内存分配失败\n");
        return 1;
    }
    // 越界访问
    for (int i = 0; i <= 5; i++) { 
        ptr[i] = i;
    }
    free(ptr);
    return 0;
}

规避方法:
确保访问的内存位置在分配的内存块范围内:在访问动态分配的内存时,要严格控制访问的边界,避免越界。可以使用循环控制变量和数组长度来确保不会越界。例如:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = (int *)malloc(5 * sizeof(int));
    if (ptr == NULL) {
        printf("内存分配失败\n");
        return 1;
    }
    // 正确访问内存
    for (int i = 0; i < 5; i++) { 
        ptr[i] = i;
    }
    free(ptr);
    return 0;
}

realloc 使用不当

错误描述
在使用 realloc 函数时,如果处理不当,可能会导致内存泄漏或其他问题。例如,realloc 调用失败时没有妥善处理原指针,或者没有更新指针导致使用了无效的指针。
示例代码:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = (int *)malloc(5 * sizeof(int));
    if (ptr == NULL) {
        printf("内存分配失败\n");
        return 1;
    }
    // realloc 调用失败时没有处理原指针
    ptr = (int *)realloc(ptr, 10 * sizeof(int)); 
    if (ptr == NULL) {
        // 此时原内存已丢失,造成内存泄漏
        printf("内存重新分配失败\n");
        return 1;
    }
    free(ptr);
    return 0;
}

规避方法:
使用临时指针处理 realloc 的返回值:在调用 realloc 时,先将返回值赋给一个临时指针,检查临时指针是否为 NULL,如果不为 NULL 再更新原指针。例如:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = (int *)malloc(5 * sizeof(int));
    if (ptr == NULL) {
        printf("内存分配失败\n");
        return 1;
    }
    int *temp = (int *)realloc(ptr, 10 * sizeof(int));
    if (temp == NULL) {
        // 原内存仍然有效
        printf("内存重新分配失败\n");
        free(ptr);
        return 1;
    }
    ptr = temp; // 更新指针
    free(ptr);
    return 0;
}

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

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

相关文章

【蓝桥杯单片机】第十一届省赛

一、真题 二、创建工程 1.在C盘以外的盘新建文件夹&#xff0c;并在文件夹里面创建两个文件夹Driver 和Project 2.打开keil软件&#xff0c;在新建工程并选择刚刚建好的project文件夹&#xff0c;以准考证号命名 3.选择对应的芯片型号 4.选择否&#xff0c;即不创建启动文件 …

【存储中间件】Neo4J图数据库超详细教程(一):相关介绍、特点及优势、数据模型、软件安装

文章目录 Neo4J超详细教程一、Neo4J相关介绍1.为什么需要图数据库方案1&#xff1a;Google方案2&#xff1a;Facebook 2.特点和优势3.什么是Neo4j4.Neo4j数据模型图论基础属性图模型Neo4j的构建元素 5.软件安装 个人主页&#xff1a;道友老李 欢迎加入社区&#xff1a;道友老李…

xxl-job部署在docker-destop,实现定时发送预警信息给指定邮箱

XXL-JOB XXL-JOB是一个分布式任务调度平台&#xff08;XXL是作者徐雪里姓名拼音的首字母&#xff09;&#xff0c;其核心设计目标是开发迅速、学习简单、轻量级、易扩展。 源码仓库地址&#xff1a;https://github.com/xuxueli/xxl-job 源码结构&#xff1a; 系统架构 在xxl-j…

【QT】QScrollBar设置样式:圆角、隐藏箭头、上边距等

目录 0.简介 1.原理 2.具体代码 0.简介 环境&#xff1a;Ubuntu22.04、qtDesigner绘制UI 项目需要&#xff0c;按照UI修改滚动条样式&#xff0c;滚动条我使用的是QScrollBar&#xff0c;默认样式和修改之后的样式如下&#xff1a; 1.原理 2.具体代码 我是用qtDesigner绘制…

trae中文版AI搭建完整可用的项目框架

Trae 是由字节跳动推出的 AI 原生集成开发环境&#xff08;AI IDE&#xff09;&#xff0c;号称可以搭建完整项目&#xff0c;个人试用后体验确实比Cursor或cline更便捷&#xff0c;因为他多个文件关联准确率更高。 正式版的trae不支持大陆使用&#xff0c;不过目前已经推出了…

cfi网络安全 网络安全hcip

目录 RIP (路由信息协议) 算法 开销 版本 开销值的计算方式 RIPV1和RIPV2的区别 RIP的数据包 Request(请求)包 Reponse(应答)包 RIP的特征 周期更新 RIP的计时器 1&#xff0c;周期更新计时器 2&#xff0c;失效计时器 3&#xff0c;垃圾回收计时器 RIP的核心思…

Banana Pi 与瑞萨电子携手共同推动开源创新:BPI-AI2N

2025年3月11日&#xff0c; Banana Pi 开源硬件平台很高兴宣布&#xff0c;与全球知名半导体解决方案供应商瑞萨电子&#xff08;Renesas Electronics&#xff09;正式达成技术合作关系。此次合作标志着双方将在开源技术、嵌入式系统和物联网等领域展开深度合作&#xff0c;为全…

linux 命令 ls

ls 是 Linux 系统中用于列出目录内容的核心命令&#xff0c;几乎所有日常操作都会用到。以下是其详细用法和常见场景说明 1. 基础语法 ls [选项] [目录/文件] 不指定目录时&#xff0c;默认列出当前目录的内容。 可以指定文件或目录路径&#xff0c;支持通配符&#xff08;如…

C#-扩展方法-Linq

密封类 sealed&#xff0c;无法被继承 var 可以定义匿名对象 static void test1() {var t 1;t "jack";//报错&#xff0c;类型已经确定好了var s new{id 1,name "tom"};Console.WriteLine(s.id s.name); } 扩展方法 对现有类型做方法的扩展&am…

Go红队开发—web网络编程

文章目录 web网络编程Req快速请求 调试DevModeDebugLogTraceInfo瓶颈分析 控制请求与响应控制请求的字段内容控制调试打印的内容分开dump请求与响应部分请求体设置 作用范围级别设置参数查询URL 路径参数表单请求设置请求头设置 判断响应状态码解析数据SetSuccessResultgjson响…

轻量级模块化前端框架:快速构建强大的Web界面

轻量级模块化前端框架&#xff1a;快速构建强大的Web界面 在当今快节奏的Web开发环境中&#xff0c;选择一个高效且灵活的前端框架至关重要。UIkit 是一个轻量级的模块化前端框架&#xff0c;旨在帮助开发者快速构建功能强大且响应迅速的Web界面。 UIkit提供了丰富的组件和工…

qt+opengl 播放yuv视频

一、实现效果 二、pro文件 Qt widgets opengl 三、主要代码 #include "glwidget.h"GLWidget::GLWidget(QWidget *parent) : QOpenGLWidget(parent) {connect(&m_timer, &QTimer::timeout, this,[&](){this->update();});m_timer.start(1000/33); }v…

5G基本概念

作者:私语茶馆 1. 5G应用场景概述 1.1.5G应用场景 ITU域2015年定义了三大应用场景:eMBB(增强型移动宽带)、uRLLC(低时延高可靠通信)、mMTC(海量物联网通信); emBB:Enhanced Mobile Broadband ,移动互联网应用,是4G MBB(移动宽带)的升级,主要侧重于网络速率、带…

PH热榜 | 2025-03-12

1. Fluently 标语&#xff1a;开始说英语&#xff0c;就像说你的母语一样流利。 介绍&#xff1a;想象一下&#xff0c;有一个像人类一样的英语教练&#xff0c;全天候在线、价格却便宜15倍。这就是 Fluently &#x1f680; 纠正你的错误&#xff0c;提升你的词汇量、发音和语…

Python Web项目的服务器部署

一.部署运行 1.虚拟环境的安装&#xff1a;&#xff08;一行一行运行&#xff09; wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh bash miniconda.sh -b -p /opt/miniconda3 echo export PATH"/opt/miniconda3/bin:$PAT…

[项目]基于FreeRTOS的STM32四轴飞行器: 八.遥控器摇杆

基于FreeRTOS的STM32四轴飞行器: 八.遥控器摇杆 一.摇杆数据的扫描二.处理摇杆数据三.微调按键处理 一.摇杆数据的扫描 下面摇杆初始化时&#xff0c;启动了ADC-DMA进行了采集&#xff0c;已经开始转换直接将数据通过DMA存入buff数组中&#xff1a; static uint16_t buff[4] …

附下载 | 2024 OWASP Top 10 基础设施安全风险.pdf

《2024 OWASP Top 10 基础设施安全风险》报告&#xff0c;由OWASP&#xff08;开放网络应用安全项目&#xff09;发布&#xff0c;旨在提升企业和组织对基础设施安全风险、威胁与漏洞的意识&#xff0c;并提供高质量的信息和最佳实践建议。报告列出了2024年最重要的10大基础设施…

Pytorch的一小步,昇腾芯片的一大步

Pytorch的一小步&#xff0c;昇腾芯片的一大步 相信在AI圈的人多多少少都看到了最近的信息&#xff1a;PyTorch最新2.1版本宣布支持华为昇腾芯片&#xff01; 1、 发生了什么事儿&#xff1f; 在2023年10月4日PyTorch 2.1版本的发布博客上&#xff0c;PyTorch介绍的beta版本…

化工厂防爆气象站:为石油化工、天然气等领域提供安全保障

【TH-FB02】在石油化工、天然气等高危行业中&#xff0c;安全生产是至关重要的。这些行业常常面临着易燃易爆、有毒有害等潜在风险&#xff0c;因此&#xff0c;对气象条件的监测和预警显得尤为重要。化工厂防爆气象站作为一种专门设计用于这些特殊环境的气象监测设备&#xff…

《A Gentle Introduction to Graph Neural Networks》-GNN的综述性论文

目录 一、什么数据可以表示成一张图 &#xff08;1&#xff09;什么是图&#xff1f; &#xff08;2&#xff09;如何表示图的属性 &#xff08;3&#xff09;images as graphs&#xff08;将图片表示为图&#xff09; &#xff08;4&#xff09;text as graphs&#xff08…