【C语言深入探索】结构体详解(二):使用场景

news2025/2/5 17:20:39

目录

一、复杂数据的表示

二、数据的封装

三、多态的模拟

四、回调函数的实现

五、多线程编程

六、通信协议的实现和文件操作

6.1. 使用结构体实现简单通信协议

6.2. 使用结构体进行文件操作

七、图形界面编程


结构体在C语言中具有广泛的应用场景,以下是一些主要的使用场景。

一、复杂数据的表示

当需要表示具有多个属性的复杂数据时,结构体是非常有用的。例如,在处理学生信息、员工记录、商品信息等时,可以将相关的数据(如姓名、年龄、性别、学号、工资、地址等)组织到一个结构体中,便于统一管理和操作。

下面将给出一个示例,展示如何使用结构体来表示包含多个层级和不同类型成员的复杂数据。

假设我们要表示一个学校的课程信息,每门课程都有课程ID、课程名称、授课老师以及一个包含学生信息的列表。而学生信息又包括学生的学号、姓名和成绩。这里,学生列表可以使用结构体数组或者链表来表示,但为了简化示例,我们将使用结构体数组。

#include <stdio.h>  
#include <string.h>  
  
// 定义学生信息的结构体  
typedef struct {  
    int id;  
    char name[50];  
    float score;  
} Student;  
  
// 接下来,定义课程信息的结构体,其中包含学生信息的数组(为了简化,这里使用固定大小的数组)  
#define MAX_STUDENTS 10  
  
typedef struct {  
    int courseId;  
    char courseName[100];  
    char teacherName[50];  
    Student students[MAX_STUDENTS]; // 学生信息的数组  
    int numStudents; // 实际学生数量  
} Course;  
  
// 示例函数,用于初始化课程信息  
void initCourse(Course *course, int courseId, const char *courseName, const char *teacherName, int numStudents, ...) {  
    course->courseId = courseId;  
    strcpy(course->courseName, courseName);  
    strcpy(course->teacherName, teacherName);  
    course->numStudents = numStudents;  
  
    // 由于C语言不支持可变参数结构体的直接传递,我们使用一个额外的va_list来处理(但为了简化,我们使用固定参数)  
    // 实际上,这里应该使用循环和额外的参数列表来填充students数组,但这里为了简洁,我们直接假设已经知道了所有学生信息  
    // 下面是一个简化的填充示例,仅填充第一个学生  
    if (numStudents > 0) {  
        course->students[0].id = 1; // 假设的学生ID  
        strcpy(course->students[0].name, "Alice"); // 假设的学生姓名  
        course->students[0].score = 92.5; // 假设的学生成绩  
        // ... 这里可以添加更多的学生信息填充逻辑  
    }  
    // 注意:实际使用中,可能需要实现一个更复杂的函数来处理可变数量的学生信息  
}  
  
// 示例函数,用于打印课程信息  
void printCourse(const Course *course) {  
    printf("Course ID: %d\n", course->courseId);  
    printf("Course Name: %s\n", course->courseName);  
    printf("Teacher Name: %s\n", course->teacherName);  
    printf("Number of Students: %d\n", course->numStudents);  
    for (int i = 0; i < course->numStudents; i++) {  
        printf("Student %d: ID=%d, Name=%s, Score=%.2f\n", i+1, course->students[i].id, course->students[i].name, course->students[i].score);  
    }  
}  
  
int main() {  
    Course course;  
    initCourse(&course, 101, "Mathematics", "Mr. Smith", 1, 1, "Alice", 92.5); // 注意:这里的initCourse函数参数是简化的,实际中需要调整  
  
    // 调用printCourse函数打印课程信息  
    printCourse(&course);  
  
    return 0;  
}  
  
// 注意:上面的initCourse函数调用是不正确的,因为C语言不支持直接将可变数量的结构体作为函数参数。  
// 这里只是为了说明如何设计函数,实际上你需要使用额外的机制(如指针数组、链表或动态内存分配)来处理可变数量的学生信息。

  • 上面的initCourse函数参数列表是不正确的,因为C语言不支持直接将可变数量的结构体作为函数参数。这里只是为了说明如何设计这样的函数,而实际上需要使用额外的机制来处理可变数量的参数。
  • 在实际应用中,处理可变数量的学生信息时,可能会使用指针数组(如Student* students)或链表,并在函数内部动态分配内存。
  • 示例中的initCourse函数仅填充了第一个学生的信息作为示例,实际使用时需要添加逻辑来填充所有学生的信息。
  • 为了简化示例,我们使用了固定大小的Student数组来存储学生信息。在真实应用中,如果学生数量不确定,可能会选择使用动态内存分配来管理这个数组。

二、数据的封装

虽然C语言本身不支持像C++那样的封装特性,但可以通过结构体和函数将数据和操作数据的函数组合在一起,达到类似封装的效果。有助于隐藏数据实现细节,只暴露有限的接口给外部使用。提高代码的安全性和可维护性。

下面是一个使用C结构体和函数来模拟封装的示例。我们将定义一个Person结构体来表示人的信息,并通过函数来访问和修改这些信息,而不是直接暴露结构体的内部字段。

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

// 定义Person结构体,模拟封装
typedef struct {
    int age;
    char name[50];
} Person;

// 创建Person的实例
Person* createPerson(int age, const char* name) {
    Person* person = (Person*)malloc(sizeof(Person)); // 动态分配内存
    if (person != NULL) {
        person->age = age;
        strcpy(person->name, name);
    }
    return person;
}

// 销毁Person的实例
void destroyPerson(Person* person) {
    if (person != NULL) {
        free(person); // 释放内存
    }
}

// 设置Person的年龄
void setAge(Person* person, int age) {
    if (person != NULL) {
        person->age = age;
    }
}

// 获取Person的年龄
int getAge(const Person* person) {
    if (person != NULL) {
        return person->age;
    }
    return -1; // 返回一个错误码或默认值
}

// 设置Person的姓名
void setName(Person* person, const char* name) {
    if (person != NULL && name != NULL) {
        strcpy(person->name, name);
    }
}

// 打印Person的信息
void printPerson(const Person* person) {
    if (person != NULL) {
        printf("Name: %s, Age: %d\n", person->name, person->age);
    }
}

int main() {
    // 创建一个Person实例
    Person* alice = createPerson(30, "Alice");

    // 访问和修改Person实例的信息
    setName(alice, "Alice Wonderland");
    setAge(alice, 31);

    // 打印Person实例的信息
    printPerson(alice);

    // 销毁Person实例
    destroyPerson(alice);

    return 0;
}

Person结构体包含了人的姓名和年龄信息。但是,我们并没有直接在main函数或其他外部函数中访问这些字段。相反,我们定义了一系列函数(如createPersondestroyPersonsetAgegetAgesetNameprintPerson)来管理Person实例的创建、销毁、信息的设置和获取以及信息的打印。

这样,我们就实现了对Person结构体数据的封装。外部代码只能通过这些函数来与Person实例交互,而无法直接访问或修改其内部字段,从而提高了代码的安全性和可维护性。

在实际开发中,可能还需要为这些函数添加更多的错误检查和边界条件处理,以确保程序的健壮性。此外,如果使用的是C99或更高版本的C标准,还可以考虑使用更高级的特性(如可变长参数列表、内联函数等)来进一步增强封装和接口设计。

三、多态的模拟

在C语言中,直接实现多态(Polymorphism)是不可能的,因为C语言是一种静态类型语言,不支持运行时类型识别和函数重载等特性。然而,我们可以通过一些技术手段来模拟多态的行为,例如使用结构体、函数指针数组(或结构体中的函数指针成员)以及void指针等技术。

例如,可以定义一个包含函数指针的结构体,这些函数指针指向具有相同签名但实现不同的函数。通过这种方式,可以在运行时动态地决定调用哪个函数,从而实现类似多态的行为。

下面是一个使用结构体和函数指针来模拟多态的示例。我们将定义一个形状(Shape)的接口,然后实现几种具体的形状(如圆形Circle和矩形Rectangle),最后通过统一的接口来操作这些形状。

#include <stdio.h>  
#include <stdlib.h>  
  
// 定义一个Shape的基结构体,包含一个绘制函数指针  
typedef struct {  
    void (*draw)(void *self); // 使用void*类型以便接收任意类型的Shape对象  
} Shape;  
  
// 圆形结构体  
typedef struct {  
    Shape base; // 继承Shape  
    int radius;  
} Circle;  
  
// 矩形结构体  
typedef struct {  
    Shape base;  
    int width, height;  
} Rectangle;  
  
// 圆形绘制函数  
void drawCircle(void *self) {  
    Circle *circle = (Circle *)self;  
    printf("Drawing Circle with radius %d\n", circle->radius);  
}  
  
// 矩形绘制函数  
void drawRectangle(void *self) {  
    Rectangle *rectangle = (Rectangle *)self;  
    printf("Drawing Rectangle with width %d and height %d\n", rectangle->width, rectangle->height);  
}  
  
// 初始化Circle  
Circle *createCircle(int radius) {  
    Circle *circle = (Circle *)malloc(sizeof(Circle));  
    if (circle != NULL) {  
        circle->radius = radius;  
        circle->base.draw = drawCircle; // 设置Circle的绘制函数  
    }  
    return circle;  
}  
  
// 初始化Rectangle  
Rectangle *createRectangle(int width, int height) {  
    Rectangle *rectangle = (Rectangle *)malloc(sizeof(Rectangle));  
    if (rectangle != NULL) {  
        rectangle->width = width;  
        rectangle->height = height;  
        rectangle->base.draw = drawRectangle; // 设置Rectangle的绘制函数  
    }  
    return rectangle;  
}  
  
// 统一的绘制函数,接受任意类型的Shape对象  
void drawShape(Shape *shape) {  
    if (shape != NULL && shape->draw != NULL) {  
        shape->draw(shape); // 注意这里传入的是shape自身,用于函数内部转换类型  
    }  
}  
  
// 释放Shape对象(注意:这里只是释放了Shape结构体占用的内存,并未处理Shape实际类型的内存)  
void freeShape(Shape *shape) {  
    // 在实际应用中,这里可能需要根据shape的实际类型来释放内存  
    // 但由于C语言不支持RTTI(运行时类型识别),因此这里仅作为示例  
    free(shape); // 注意:这里可能会出错,因为shape可能只是结构体的一部分  
}  
  
// 注意:freeShape函数存在问题,因为它假设Shape就是完整的对象。  
// 在实际应用中,你可能需要设计一个更复杂的释放机制,或者使用容器来管理内存。  
  
int main() {  
    Shape *shapes[2];  
  
    shapes[0] = (Shape *)createCircle(5);  
    shapes[1] = (Shape *)createRectangle(10, 20);  
  
    for (int i = 0; i < 2; i++) {  
        drawShape(shapes[i]);  
        // 注意:这里没有释放shapes[i]指向的内存,因为freeShape函数不适合这里的用法  
    }  
  
    // 由于freeShape函数存在问题,这里直接跳过了释放内存的步骤  
    // 在实际应用中,需要为每个创建的形状对象编写合适的释放逻辑  
  
    return 0;  
}  
  
// 注意:这个示例中的freeShape函数并没有在main函数中被调用,  
// 因为它的设计不适合直接用于这里的场景。  
// 需要根据你的具体需求来设计内存管理机制。

定义了一个Shape结构体,它包含一个函数指针draw。然后,定义了CircleRectangle结构体,它们都包含了一个Shape类型的成员(作为“基类”或“接口”),并各自实现了draw函数。通过这种方式,我们可以通过Shape类型的指针来调用不同形状对象的绘制函数,从而模拟多态的行为。

需要注意的是,由于C语言不支持运行时类型识别,我们在释放内存时需要格外小心,以确保不会错误地释放或泄露内存。在这个示例中,freeShape函数的设计并不适用于这种情况,因为它假设了Shape就是完整的对象。在实际应用中,可能需要为每个形状类型编写专门的释放函数,或者使用其他内存管理机制(如智能指针、对象池等)来管理内存。

四、回调函数的实现

结构体中的函数指针常用于实现回调函数的机制。回调函数是指在编程中,将一个函数作为参数传递给另一个函数,并在特定事件发生时被调用执行的一种机制。通过结构体中的函数指针,可以方便地实现回调函数,从而在处理异步操作或事件驱动的编程模式时更加灵活和高效。

下面是一个使用结构体和回调函数的实现示例。在这个示例中,我们定义了一个简单的任务(Task)结构体,它包含一个回调函数和一个与该回调函数相关的用户数据指针。然后,我们定义了一个执行任务的函数,它接受一个任务结构体作为参数,并调用其中的回调函数。

#include <stdio.h>  
  
// 定义一个回调函数类型  
typedef void (*TaskCallback)(void *userData);  
  
// 任务结构体  
typedef struct {  
    TaskCallback callback; // 回调函数指针  
    void *userData;        // 回调函数相关的用户数据  
} Task;  
  
// 一个示例回调函数  
void exampleCallback(void *userData) {  
    // 假设userData是指向一个整数的指针  
    int *value = (int *)userData;  
    printf("Callback called with value: %d\n", *value);  
}  
  
// 执行任务的函数  
void executeTask(Task task) {  
    if (task.callback != NULL) {  
        task.callback(task.userData); // 调用回调函数  
    }  
}  
  
int main() {  
    // 准备用户数据  
    int userData = 42;  
  
    // 创建并初始化任务  
    Task myTask = {  
        .callback = exampleCallback, // 设置回调函数  
        .userData = &userData        // 设置用户数据  
    };  
  
    // 执行任务  
    executeTask(myTask);  
  
    return 0;  
}

Task 结构体包含了两个成员:一个是指向回调函数的指针 callback,另一个是指向用户数据的指针 userDataexecuteTask 函数接受一个 Task 类型的参数,并检查 callback 是否非空。如果非空,则调用该回调函数,并将 userData 作为参数传递给它。

exampleCallback 函数是一个示例回调函数,它简单地打印出通过 userData 指针传递的整数值。在 main 函数中,我们创建了一个 Task 类型的变量 myTask,设置了它的回调函数和用户数据,然后调用了 executeTask 函数来执行任务。

这个示例展示了如何在C语言中使用结构体和回调函数来实现更灵活的代码结构。通过这种方式,可以将任务的执行逻辑与具体的回调函数实现分离,使得代码更加模块化和易于测试。

五、多线程编程

在多线程编程中,结构体常用于表示线程的状态信息。例如,可以定义一个结构体来包含线程ID、优先级、执行状态等信息,并通过这个结构体来管理和控制线程的行为。

下面是一个使用C语言结构体和pthread库进行多线程编程的简单示例。在这个示例中,我们定义了一个结构体来存储线程需要处理的数据,并创建了一个线程来修改这些数据。

#include <stdio.h>  
#include <stdlib.h>  
#include <pthread.h>  
  
// 定义一个结构体来存储线程数据  
typedef struct {  
    int id;  
    int *counter;  
} ThreadData;  
  
// 线程函数  
void *thread_function(void *arg) {  
    ThreadData *data = (ThreadData *)arg;  
  
    // 假设这里有一些复杂的计算或处理  
    // 这里我们只是简单地增加计数器的值  
    for (int i = 0; i < 1000; i++) {  
        (*data->counter)++;  
    }  
  
    printf("Thread %d finished. Counter: %d\n", data->id, *data->counter);  
  
    // 线程结束时返回NULL  
    return NULL;  
}  
  
int main() {  
    pthread_t thread1, thread2;  
    int counter = 0;  
  
    // 创建ThreadData实例  
    ThreadData td1 = {1, &counter};  
    ThreadData td2 = {2, &counter};  
  
    // 创建线程  
    if (pthread_create(&thread1, NULL, thread_function, &td1) != 0) {  
        perror("Failed to create thread 1");  
        return 1;  
    }  
  
    if (pthread_create(&thread2, NULL, thread_function, &td2) != 0) {  
        perror("Failed to create thread 2");  
        return 1;  
    }  
  
    // 等待线程结束  
    pthread_join(thread1, NULL);  
    pthread_join(thread2, NULL);  
  
    printf("Final counter value: %d\n", counter);  
  
    return 0;  
}

两个线程都试图修改同一个全局变量counter。由于多线程同时访问共享资源可能会导致竞态条件(race condition),因此在实际应用中,可能需要使用互斥锁(mutex)或其他同步机制来保护共享数据。

然而,为了简化示例,这里并没有包含互斥锁的使用。在真实场景中,应该在修改counter之前锁定一个互斥锁,并在修改完成后释放它。

此外,由于两个线程都指向同一个counter变量,并且都试图修改它,因此最终的counter值将是两个线程增加的总和(在这个例子中,每个线程增加1000次,所以最终值应该是2000)。但是,由于竞态条件,如果没有适当的同步,实际结果可能会不同。

如果想要看到竞态条件的影响(尽管这通常不是推荐的做法),可以尝试移除互斥锁并观察输出是否总是2000。然而,由于操作系统的调度和线程执行的不确定性,可能会看到不同的结果。

六、通信协议的实现和文件操作

在网络通信和文件操作中,经常需要按照特定的格式组织数据。结构体提供了一种方便的方式来定义这些数据的结构,并确保数据的正确性和一致性。例如,在发送或接收网络数据包时,可以使用结构体来定义数据包的格式,并通过网络函数将数据包的各个字段发送或接收出去。

下面将分别给出一个使用结构体实现简单通信协议和进行文件操作的示例。

6.1. 使用结构体实现简单通信协议

假设我们要实现一个简单的网络通信协议,该协议的数据包包含一个消息类型(int)、一个消息长度(int)和实际的消息内容(char 数组)。

#include <stdio.h>  
#include <string.h>  
  
// 定义数据包结构体  
typedef struct {  
    int type;       // 消息类型  
    int length;     // 消息长度  
    char message[1024]; // 消息内容  
} Packet;  
  
// 发送数据包(这里用打印模拟发送)  
void sendPacket(Packet p) {  
    printf("Sending packet: Type=%d, Length=%d, Message='%s'\n", p.type, p.length, p.message);  
}  
  
// 接收数据包(这里用输入模拟接收)  
Packet receivePacket() {  
    Packet p;  
    printf("Enter packet type: ");  
    scanf("%d", &p.type);  
    printf("Enter packet length: ");  
    scanf("%d", &p.length);  
    printf("Enter packet message: ");  
    fgets(p.message, sizeof(p.message), stdin);  
    // 注意:fgets会读取换行符,可能需要处理  
    p.message[strcspn(p.message, "\n")] = 0; // 去除换行符  
    return p;  
}  
  
int main() {  
    Packet p = receivePacket(); // 模拟接收数据包  
    sendPacket(p); // 模拟发送数据包  
    return 0;  
}

这个示例仅用于演示如何使用结构体来模拟网络通信协议的数据包。在实际的网络通信中,需要使用套接字(sockets)等网络编程接口来发送和接收数据。

6.2. 使用结构体进行文件操作

假设我们要将一系列学生的信息(姓名、年龄和成绩)保存到文件中,并从文件中读取这些信息。我们可以定义一个结构体来表示学生信息。

注意:在实际应用中,可能需要处理更复杂的文件读写操作,包括错误检查、二进制文件读写、文件锁定等。此外,使用%s读取字符串时需要注意缓冲区溢出的问题,可能需要限制读取的字符数。在这个示例中,为了简化,我们直接使用了%s。在更复杂的应用中,可能需要使用fgets等函数来安全地读取字符串。

七、图形界面编程

在图形界面编程中,结构体也扮演着重要的角色。例如,在开发GUI应用程序时,可以使用结构体来表示窗口、按钮、文本框等控件的属性和行为。通过结构体,可以方便地管理这些控件的状态和事件响应。

一个流行的C语言图形库是GTK(GIMP Toolkit),它广泛用于Linux和Unix系统上的图形界面开发。下面给出一个简单的示例,展示如何在C语言中使用GTK库和结构体来创建一个基本的图形界面。

首先,需要确保你的系统上安装了GTK库。在Ubuntu系统上,可以通过运行sudo apt-get install libgtk-3-dev来安装GTK 3的开发文件。

接下来是一个简单的示例,该示例创建了一个窗口,并在其中放置了一个标签(Label),我们将使用结构体来管理窗口和标签的引用。

#include <gtk/gtk.h>  
  
// 定义一个结构体来保存窗口和标签的引用  
typedef struct {  
    GtkWidget *window;  
    GtkWidget *label;  
} MyApp;  
  
// 窗口销毁时的回调函数  
static void on_window_destroy(GtkWidget *widget, gpointer data) {  
    g_print("Window closed. Exiting.\n");  
    gtk_main_quit();  
}  
  
// 初始化GUI的函数  
MyApp *create_app() {  
    MyApp *app = g_malloc(sizeof(MyApp));  
  
    // 初始化GTK  
    gtk_init(NULL, NULL);  
  
    // 创建一个新窗口  
    app->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);  
    gtk_window_set_title(GTK_WINDOW(app->window), "MyApp");  
    g_signal_connect(app->window, "destroy", G_CALLBACK(on_window_destroy), NULL);  
  
    // 创建一个新标签  
    app->label = gtk_label_new("Hello, GTK+!");  
    gtk_container_add(GTK_CONTAINER(app->window), app->label);  
  
    // 显示所有窗口组件  
    gtk_widget_show_all(app->window);  
  
    return app;  
}  
  
int main(int argc, char *argv[]) {  
    MyApp *app = create_app();  
  
    // GTK主事件循环  
    gtk_main();  
  
    // 清理分配的内存  
    g_free(app);  
  
    return 0;  
}

定义了一个MyApp结构体,它包含指向窗口和标签的指针。在create_app函数中,初始化了GTK,创建了一个窗口和一个标签,并将它们添加到窗口中。然后,我们设置了窗口的销毁回调函数,并在最后显示了窗口和它的所有子组件。

请注意,这个示例使用了GTK 3的API,并且依赖于GTK库的正确安装和配置。如果使用的是其他操作系统或希望使用其他图形库(如Qt、wxWidgets等),将需要查找该库的具体文档和API来使用它。

此外,为了编译这个示例,需要链接GTK库。在GCC中,可以使用类似下面的命令:

gcc `pkg-config --cflags gtk+-3.0` -o myapp myapp.c `pkg-config --libs gtk+-3.0`

这个命令使用pkg-config工具来自动添加GTK的编译和链接标志。

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

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

相关文章

【大模型】AI 辅助编程操作实战使用详解

目录 一、前言 二、AI 编程介绍 2.1 AI 编程是什么 2.1.1 为什么需要AI辅助编程 2.2 AI 编程主要特点 2.3 AI编程底层核心技术 2.4 AI 编程核心应用场景 三、AI 代码辅助编程解决方案 3.1 AI 大模型平台 3.1.1 AI大模型平台代码生成优缺点 3.2 AI 编码插件 3.3 AI 编…

RK3566-移植5.10内核Ubuntu22.04

说明 记录了本人使用泰山派&#xff08;RK3566&#xff09;作为平台并且成功移植5.10.160版本kernel和ubuntu22.04&#xff0c;并且成功配置&连接网络的完整过程。 本文章所用ubuntu下载地址&#xff1a;ubuntu-cdimage-ubuntu-base-releases-22.04-release安装包下载_开源…

从零开始实现一个双向循环链表:C语言实战

文章目录 1链表的再次介绍2为什么选择双向循环链表&#xff1f;3代码实现&#xff1a;从初始化到销毁1. 定义链表节点2. 初始化链表3. 插入和删除节点4. 链表的其他操作5. 打印链表和判断链表是否为空6. 销毁链表 4测试代码5链表种类介绍6链表与顺序表的区别7存储金字塔L0: 寄存…

51单片机 06 定时器

51 单片机的定时器属于单片机的内部资源&#xff0c;其电路的连接和运转均在单片机内部完成。 作用&#xff1a;1、用于计时&#xff1b;2、替代长时间的Delay&#xff0c;提高CPU 运行效率和处理速度。 定时器个数&#xff1a;3个&#xff08;T0、T1、T2&#xff09;&#xf…

【C++】P1957 口算练习题

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目描述输入格式&#xff1a;输出格式&#xff1a; &#x1f4af;我的做法代码实现&#xff1a; &#x1f4af;老师的做法代码实现&#xff1a; &#x1f4af;对比分析&am…

Workbench 中的热源仿真

探索使用自定义工具对移动热源进行建模及其在不同行业中的应用。 了解热源动力学 对移动热源进行建模为各种工业过程和应用提供了有价值的见解。激光加热和材料加工使用许多激光束来加热、焊接或切割材料。尽管在某些情况下&#xff0c;热源 &#xff08;q&#xff09; 不是通…

CCF-GESP 等级考试 2023年12月认证C++八级真题解析

2023年12月真题 一、单选题&#xff08;每题2分&#xff0c;共30分&#xff09; 正确答案&#xff1a;C 考察知识点&#xff1a;数学问题 解析&#xff1a;本题可抽象为分类计数问题&#xff0c;应使用加法原理&#xff0c;而不是乘法原理。答案为 ACB 的方案数 2 加上 ADB 的…

vscode搭建git

vscode搭建git 一、安装git二、vscode上搭建git(1) 先创建本地仓库再上传到远程仓库&#xff0c;远程仓库名是根据本地仓库名一致(2) 先创建远程仓库&#xff0c;再将本地仓库上传到指定远程仓库 一、安装git 网络教程很多&#xff0c;在此就不赘述了 参考&#xff1a;git安装…

解决Mac安装软件的“已损坏,无法打开。 您应该将它移到废纸篓”问题

mac安装软件时&#xff0c;如果出现这个问题&#xff0c;其实很简单 首先打开终端&#xff0c;输入下面的命令 sudo xattr -r -d com.apple.quarantine 输入完成后&#xff0c;先不要回车&#xff0c;点击访达--应用程序--找到你无法打开的app图标&#xff0c;拖到终端窗口中…

ChatGPT-4o和ChatGPT-4o mini的差异点

在人工智能领域&#xff0c;OpenAI再次引领创新潮流&#xff0c;近日正式发布了其最新模型——ChatGPT-4o及其经济实惠的小型版本ChatGPT-4o Mini。这两款模型虽同属于ChatGPT系列&#xff0c;但在性能、应用场景及成本上展现出显著的差异。本文将通过图文并茂的方式&#xff0…

读书笔记--分布式架构的异步化和缓存技术原理及应用场景

本篇是在上一篇的基础上&#xff0c;主要对分布式应用架构下的异步化机制和缓存技术进行学习&#xff0c;主要记录和思考如下&#xff0c;供大家学习参考。大家知道原来传统的单一WAR应用中&#xff0c;由于所有数据都在同一个数据库中&#xff0c;因此事务问题一般借助数据库事…

BUU10 [极客大挑战 2019]LoveSQL1

万能用户名&#xff08;密码随便&#xff09; 登录进去以后发现是这个东西&#xff0c;然而并没有什么卵用 然后就开始爆破数据库名字--表名--列名 注意&#xff1a;这道题需要将所有的表名都爆出来&#xff0c;需要在payload里头写 group_concat()&#xff0c;否则页面只会显…

tomcat核心组件及原理概述

目录 1. tomcat概述 1.1 概念 1.2 官网地址 2. 基本使用 2.1下载 3. 整体架构 3.1 核心组件 3.2 从web.xml配置和模块对应角度 3.3 如何处理请求 4. 配置JVM参数 5. 附录 1. tomcat概述 1.1 概念 什么是tomcat Tomcat是一个开源、免费、轻量级的Web服务器。 Tomca…

冰蝎v4.0.5 来啦

webshell始终是渗透测试的热门&#xff0c;上次护网写冰蝎检测规则&#xff0c;加密流量&#xff0c;有点压力&#xff0c;今天终于有空来复现一下&#xff0c;我知道玩知乎的大佬很多&#xff0c;轻一点喷&#xff0c;学习新知识不丢人&#xff5e; ailx10 1949 次咨询 4.9 …

【C++】B2120 单词的长度

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目描述&#x1f4af;我的做法代码实现&#xff1a;思路解析&#xff1a; &#x1f4af;老师的第一种做法代码实现&#xff1a;思路解析&#xff1a; &#x1f4af;老师的…

使用线性回归模型逼近目标模型 | PyTorch 深度学习实战

前一篇文章&#xff0c;计算图 Compute Graph 和自动求导 Autograd | PyTorch 深度学习实战 本系列文章 GitHub Repo: https://github.com/hailiang-wang/pytorch-get-started 使用线性回归模型逼近目标模型 什么是回归什么是线性回归使用 PyTorch 实现线性回归模型代码执行结…

深入浅出:频谱掩码 Spectral Masking —— 噪音消除利器

在语音处理领域&#xff0c;噪声是一个常见的敌人。无论是语音通话、语音识别&#xff0c;还是语音合成&#xff0c;噪声都会大大降低语音的质量和可理解性。为了解决这个问题&#xff0c;Spectral Masking&#xff08;频谱掩码&#xff09; 模型应运而生。它通过从带噪信号的频…

C++ Primer 多维数组

欢迎阅读我的 【CPrimer】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…

Mac M1 ComfyUI 中 AnyText插件安装问题汇总?

Q1&#xff1a;NameError: name ‘PreTrainedTokenizer’ is not defined ? 该项目最近更新日期为2024年12月&#xff0c;该时间段的transformers 版本由PyPI 上的 transformers 页面 可知为4.47.1. A1: transformers 版本不满足要求&#xff0c;必须降级transformors &#…

C++基础(2)

目录 1. 引用 1.1 引用的概念和定义 1.2 引用的特性 1.3 引用的使用 2. 常引用 3. 指针和引用的关系 4. 内联函数inline 5. nullptr 1. 引用 1.1 引用的概念和定义 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开…