c语言的qsort函数理解与使用

news2024/11/29 16:09:03

介绍:qsort 函数是 C 标准库中用于排序的快速排序算法函数。它的用法非常灵活,可以对任意类型的元素进行排序,只要提供了比较函数即可。

qsort 函数原型及参数解释:

void qsort (

     void* base,         //指向要排序的数组的首元素的指针

     size_t num,         //待排序数组中元素的个数

     size_t size,        //待排序数组中每个元素的大小(以字节为单位)

     int (*compare)(const void*,const void*)  //比较函数的指针,用于确定元素之间的排序

);

注意: 

使用qsort函数时,需要根据实际情况编写一个合适的比较函数来确定排序规则。

比较函数可以根据元素的类型和排序需求进行自定义。

比较函数的原型如下:

int compare(const void *a, const void *b);

比较函数需要返回一个整数值,表示两个元素之间的关系:

如果返回值小于0,则表示a应该排在b之前。

如果返回值等于0,则表示a和b相等,它们的相对顺序不变。

如果返回值大于0,则表示a应该排在b之后。

对于一个数组或是任意需要进行排序的内容进行排序时可以理解如下:

// qsort中自定义比较函数compare返回值 > 0表示对需要排序的内容进行升序排序(小->大)

// qsort中自定义比较函数compare返回值 < 0表示对需要排序的内容进行降序排序(大->小)

实例:

// qsort排序举例:升序排序(小->大)
// 自定义比较函数返回值 > 0表示对需要排序的内容进行升序排序

// qsort 函数是 C 标准库中用于排序的快速排序算法函数。
// 它的用法非常灵活,可以对任意类型的元素进行排序,只要提供了比较函数即可。

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

// 比较函数的形式为:
// int cmp(const void *a, const void *b);
// 其中,cmp 函数用于比较 a 和 b 两个指针所指向的元素的大小关系,
// 如果 a 指向的元素小于 b 指向的元素,则返回负数;
// 如果 a 指向的元素大于 b 指向的元素,则返回正数;
// 如果 a 指向的元素等于 b 指向的元素,则返回 0。
// 下面我们来看一个最简单的实例代码,用于对整数数组进行排序:
int compare(const void *a, const void *b)
{
    printf("%d - %d = %d,  ", *(int *)a, *(int *)b, (*(int *)a - *(int *)b));
    return (*(int*)a - *(int*)b);
}

int main()
{
    int arr0[] = {2, 1};
    int len = sizeof(arr0) / sizeof(int);

    // 这段代码的核心部分就是使用 qsort 函数对整数数组进行排序,排序时需要传递一个比较函数 cmp。
    // cmp 函数的实现非常简单,就是计算 a 和 b 之间的差值。最后,我们依次输出排序后的整数数组。
    // 需要注意的是,在使用 qsort 函数时,我们需要传递以下参数:
    // 1. 待排序的数组的首地址。
    // 2. 数组中元素的个数。
    // 3. 每个元素的大小(以字节为单位)。
    // 4. 比较函数的地址(即函数指针)。
    // 这里我们传递的数组是 int 类型的数组,每个元素占据 4 个字节;
    // 比较函数 cmp 的地址可以直接传递函数名,也可以使用 & 运算符取地址。
    // 这就是 qsort 函数的基本用法。
    // 需要注意的是,cmp 函数要保证正确性和稳定性才能得到正确的排序结果,同时在实际使用时,也需要根据具体情况进行参数传递和处理。
    printf("arr0 升序排序过程:\n");
    // compare 返回值 > 0表示对数组arr1进行降序排序
    qsort(arr0, len, sizeof(int), compare);

    printf("\narr0 after sort:\n");
    for (int i = 0; i < len; i++) {
        printf("%d ", arr0[i]);
    }
    printf("\n");

    int arr1[] = {3, 2, 1, 5, -2, 9};
    int len1 = sizeof(arr1) / sizeof(int);
    printf("\narr1 升序排序过程:\n");
    // compare 返回值 > 0表示对数组arr1进行降序排序
    qsort(arr1, len1, sizeof(int), compare);

    printf("\narr1 after sort:\n");
    for (int i = 0; i < len1; i++) {
        printf("%d ", arr1[i]);
    }
    printf("\n");

    return 0;
}

// qsort排序举例:降序排序(大->小)
#include <stdio.h>
#include <stdlib.h>

int cmp0(const void *a, const void *b)
{
    printf("%d - %d = %d,  ", *(int *)a, *(int *)b, (*(int *)a - *(int *)b));
    return -(*(int*)a - *(int*)b);
}

int cmp1(const void *a, const void *b)
{
    printf("%d - %d = %d,  ", *(int *)b, *(int *)a, (*(int *)b - *(int*)a));
    return (*(int *)b - *(int*)a);
}

int main()
{
    int arr[] = {5, 4, 2, 5, 3, 1};
    int len = sizeof(arr) / sizeof(int);

    printf("升序排序过程:\n");
    // cmp1返回值 < 0表示对数组arr1进行降序排序
    qsort(arr, len, sizeof(int), cmp0);

    printf("\nafter sort:\n");
    for (int i = 0; i < len; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    // cmp1返回值 < 0表示对数组arr1进行降序排序
    int arr1[] = {3, 2, 1, 5, -2, 9};
    printf("降序排序过程:\n");
    qsort(arr1, len, sizeof(int), cmp1);

    printf("\nafter sort:\n");
    for (int i = 0; i < len; i++) {
        printf("%d ", arr1[i]);
    }
    printf("\n");

    return 0;
}


// 对浮点数进行排序
#include <stdio.h>
#include <stdlib.h>

// 对浮点数进行升序排序
int cmpFloat2Ascending(const void *a, const void *b)
{
    return *(float *)a - *(float *)b;
}

// 对浮点数进行降序排序
int cmpFloat2Descending(const void *a, const void *b)
{
    return -(*(float *)a - *(float *)b);
}

int main()
{
    float arr0[] = { -0.52, 1.7, 20.5, 9.9, 10.22 };
    int num0 = sizeof(arr0) / sizeof(int);

    printf("升序排序过程:\n");
    // cmp1返回值 < 0表示对数组arr1进行降序排序
    qsort(arr0, num0, sizeof(float), cmpFloat2Ascending);

    printf("\nafter sort:\n");
    for (int i = 0; i < num0; i++) {
        printf("%.2f ", arr0[i]);
    }
    printf("\n");

    // cmp1返回值 < 0表示对数组arr1进行降序排序
    float arr1[] = {3.3, 5.2, 1.1, 0.5, -1.2, 9.9};
    int num1 = sizeof(arr0) / sizeof(float);
    printf("降序排序过程:\n");
    qsort(arr1, num1, sizeof(float), cmpFloat2Descending);

    printf("\nafter sort:\n");
    for (int i = 0; i < num1; i++) {
        printf("%.2f ", arr1[i]);
    }
    printf("\n");

    return 0;
}

// qsort 函数对结构体类型的数据进行升序和降序排序:
// 这个程序定义了一个 Student 结构体类型,包含了学生的姓名、年龄和考试成绩三个成员变量。
// 在比较函数中,我们先比较考试成绩的大小,如果不同则返回差值;
// 如果考试成绩相同,则比较年龄的大小,如果不同则返回差值;
// 如果年龄也相同,则比较姓名的大小,从而实现了升序和降序排序。
// 在 main 函数中,我们首先定义了一个 Student 类型的数组,用于测试排序结果。
// 然后先调用 qsort 函数实现升序排序,并输出排序后的结果;
// 接着再调用 qsort 函数实现降序排序,并输出排序后的结果。
// 需要注意的是,在实际使用 qsort 函数时,需要根据具体情况进行参数传递和处理,
// 特别是在程序中使用了自定义结构体类型时,需要编写相应的比较函数来实现排序。
// 同时,也需要特别注意比较函数的实现方式,确保能够正确地比较结构体类型的各个成员变量。

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

typedef struct {
    char name[20];
    int age;
    int score;
} Student;

// 升序
int cmpAsc(const void *a, const void *b)
{
    Student *sa = (Student *)a;
    Student *sb = (Student *)b;
    if (sa->score != sb->score) {
        return sa->score - sb->score;
    } else if (sa->age != sb->age) {
        return sa->age - sb->age;
    } else {
        return strcmp(sa->name, sb->name);
    }
}

// 降序
int cmpDesc(const void *a, const void *b)
{
    Student *sa = (Student *)a;
    Student *sb = (Student *)b;
    if (sa->score != sb->score) {
        return -(sa->score - sb->score);
    } else if (sa->age != sb->age) {
        return -(sa->age - sb->age);
    } else {
        return -strcmp(sa->name, sb->name);
    }
}

int main()
{
    Student students[] = {
        {"Alice", 18, 80},
        {"Bob", 20, 70},
        {"Charlie", 19, 90},
        {"David", 18, 80},
        {"Ella", 20, 85}
    };


    int n = sizeof(students) / sizeof(students[0]);

    // 升序
    qsort(students, n, sizeof(Student), cmpAsc);
    printf("Asc:\n");
    for (int i = 0; i < n; i++) {
        printf("Name: %s\t Age: %d\t Score: %d\n", students[i].name, students[i].age, students[i].score);
    }
    printf("\n");

    // 降序排序
    qsort(students, n, sizeof(Student), cmpDesc);

    printf("Desc:\n");
    for (int i = 0; i < n; i++) {
        printf("Name: %s\t Age: %d\t Score: %d\n", students[i].name, students[i].age, students[i].score);
    }

    return 0;
}

// 实现一个自定义的模拟qsort函数
// 在这个程序中,我们首先定义了一个 Person 结构体类型,包含名称和年龄两个成员变量。
// 在比较函数中,我们首先比较名称的大小,如果不同则返回名称的差值;
// 如果名称相同,则比较年龄的大小,从而实现了升序和降序排序。
// 在 bubble_sort 函数中,我们使用冒泡排序的逻辑来实现元素的排序,传入的对象是 void 指针,
// 因此我们需要将其强制转换为 char 指针,然后利用 memcpy 函数以元素的大小 width 为单位进行移动。
// 每次比较都使用传入的函数指针 cmp 来比较元素的大小,如果需要交换则使用 memcpy 函数来实现交换。
// 在 main 函数中,我们首先定义了一个 Person 类型的数组,包含了五个元素。
// 我们先对数组进行升序排序打印输出,然后再进行降序排序打印输出。
// 需要注意的是,在输出时使用了结构体的成员变量 name 和 age,分别代表了名称和年龄
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    char name[20];
    int age;
} Person;

int cmp_asc(const void *a, const void *b)
{
    Person *pa = (Person *)a;
    Person *pb = (Person *)b;
    if (strcmp(pa->name, pb->name) != 0) {
        return strcmp(pa->name, pb->name);
    } else {
        return pa->age - pb->age;
    }
}

int cmp_desc(const void *a, const void *b)
{
    Person *pa = (Person *)a;
    Person *pb = (Person *)b;
    if (strcmp(pa->name, pb->name) != 0) {
        return strcmp(pb->name, pa->name);
    } else {
        return pb->age - pa->age;
    }
}

void bubble_sort0(void *base, size_t nel, size_t width, int (*cmp)(const void *, const void *))
{
    char *array = (char *)base;
    for (int i = 0; i < nel - 1; i++) {
        for (int j = i + 1; j < nel; j++) {
            // > 0 表示升序
            if (cmp(array + i * width, array + j * width) > 0) {
                char tmp[width];
                memcpy(tmp, array + j * width, width);
                memcpy(array + j * width, array + i * width, width);
                memcpy(array + i * width, tmp, width);
            }
        }
    }
}

void bubble_sort1(void *base, size_t nel, size_t width, int (*cmp)(const void *, const void *))
{
    char *array = (char *)base;
    for (int i = 0; i < nel - 1; i++) {
        for (int j = 0; j < nel - i - 1; j++) {
            // > 0 表示升序
            if (cmp(array + j * width, array + (j + 1) * width) > 0) {
                char tmp[width];
                memcpy(tmp, array + j * width, width);
                memcpy(array + j * width, array + (j + 1) * width, width);
                memcpy(array + (j + 1) * width, tmp, width);
            }
        }
    }
}


int main()
{
    Person people[] = {
        {"Alice", 18},
        {"Bob", 20},
        {"Charlie", 19},
        {"David", 18},
        {"Ella", 20}
    };
    int n = sizeof(people) / sizeof(Person);

    // 升序排序
    bubble_sort0(people, n, sizeof(Person), cmp_asc);

    printf("Asc:\n");
    for (int i = 0; i < n; i++) {
        printf("Name: %s\t Age: %d\n", people[i].name, people[i].age);
    }
    printf("\n");

    // 降序排序
    bubble_sort1(people, n, sizeof(Person), cmp_desc);

    printf("Desc:\n");
    for (int i = 0; i < n; i++) {
        printf("Name: %s\t Age: %d\n", people[i].name, people[i].age);
    }

    return 0;
}

// 实现一个自定义的模拟qsort函数
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    char name[20];
    int age;
} Person;

int cmp_asc(const void *a, const void *b)
{
    Person *pa = (Person *)a;
    Person *pb = (Person *)b;
    if (strcmp(pa->name, pb->name) != 0) {
        return strcmp(pa->name, pb->name);
    } else {
        return pa->age - pb->age;
    }
}

int cmp_desc(const void *a, const void *b)
{
    Person *pa = (Person *)a;
    Person *pb = (Person *)b;
    if (strcmp(pa->name, pb->name) != 0) {
        return strcmp(pb->name, pa->name);
    } else {
        return pb->age - pa->age;
    }
}

//交换 --一个字节一个字节的交换,共交换width次
void Swap(char* buf1, char* buf2, size_t width)
{
	size_t i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}
void my_BubbleSort(void* base, size_t num,size_t width, int(*cmp)(const void* e1, const void* e2))
{
	//冒泡排序
	//若要排序n个元素,只需要进行n-1趟
	//每一趟可以少比较一个元素,每一趟可以使一个元素在确定的位置上
	//num:要排序元素的个数 类型是size_t 
    //num是无符号数 防止产生警告 所以i和j也定义为size_t
    // size_t == unsigned int 
	size_t i = 0;
	size_t j = 0;
 
	//共进行num-1趟
	for (i = 0; i < num; i++)
	{
		//每一趟
		for (j = 0; j < num - 1 - i; j++)
		{
			//比较
			//传地址   
			//相邻两个元素比较   width:宽度,每个元素所占字节
			//排成升序
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				//交换两数
				Swap( (char*)base + j * width, (char*)base + (j + 1) * width, width );
			}
		}
	}
}


int main()
{
    Person people[] = {
        {"Alice", 18},
        {"Bob", 20},
        {"Charlie", 19},
        {"David", 18},
        {"Ella", 20}
    };
    int n = sizeof(people) / sizeof(Person);

    // 升序排序
    my_BubbleSort(people, n, sizeof(Person), cmp_asc);

    printf("Asc:\n");
    for (int i = 0; i < n; i++) {
        printf("Name: %s\t Age: %d\n", people[i].name, people[i].age);
    }
    printf("\n");

    // 降序排序
    my_BubbleSort(people, n, sizeof(Person), cmp_desc);

    printf("Desc:\n");
    for (int i = 0; i < n; i++) {
        printf("Name: %s\t Age: %d\n", people[i].name, people[i].age);
    }

    return 0;
}

使用qsort对字符串数组进行排序:

// 使用qsort函数来对字符串数组进行升序和降序排序

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

#define MAX_STR_NUM 10
#define MAX_STR_LEN 20

int cmpAsc(const void *a, const void *b)
{
    char **pa = (char **)a;
    char **pb = (char **)b;
    return strcmp(*pa, *pb);
}

int cmpDesc(const void *a, const void *b)
{
    char **pa = (char **)a;
    char **pb = (char **)b;
    return -strcmp(*pa, *pb);
    return strcmp(*pb, *pa);
}

int main()
{
    int n = 3;
    int i = 0;
    char **strs = (char **)malloc(n * sizeof(char *));
    char *str0 = (char *)malloc(100);
    for (i = 0; i < 3; ++i) {
        strs[i] = (char *)malloc(100);
    }
    snprintf(strs[0], 100, "ff str0\n");
    snprintf(strs[1], 100, "dd str1\n");
    snprintf(strs[2], 100, "aa str2\n");

    // 升序排序
    qsort(strs, n, sizeof(char *), cmpAsc);

    printf("\nAsc: \n");
    for(int i = 0; i < n; i++) {
        printf("%s", strs[i]);
    }
    printf("\n");

    // 降序排序
    qsort(strs, n, sizeof(char *), cmpDesc);

    printf("\nDesc: \n");
    for(int i = 0; i < n; i++) {
        printf("%s", strs[i]);
    }
    printf("\n");

    return 0;
}
// 使用qsort函数对字符串数组进行排序

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


// 在实际使用时,比较函数的参数应该根据实际情况转换为正确的指针类型。
// 在字符串数组中,每个元素都是指向一个字符串的指针,
// 因此需要将`const void *`类型的指针转换为`char **`类型的指针,即指向指针的指针,然后才能解引用该指针,
// 即使用`*(char **)a`,获取指向的字符串。
// 如果使用`(char *)a`进行强制类型转换,`char *`类型的指针是单层指针,而不是指针的指针。
// 这将导致在解引用该指针时不能得到正确的字符串指针,从而产生错误的答案。
// 因此,在使用指针数组中的字符串进行排序时,
// 必须使用`char **`类型的指针进行指针转换,再解引用该指针获取到正确的字符串指针进行比较,才能保证排序的正确性。
// a和b的值是strs的元素的地址,也就是指针的指针,所以void其实是char*,void*就是char**
// strs的每个元素都是一个指针,每个元素的地址就应该用指针的指针来存储
// 所以a和b就是指针的指针,所以void*就是char**,void想当于char*
int cmp(const void *a, const void *b)
{
    printf("a : %p, b : %p\n", a, b);
    char *tmpa = (char *)a;
    char *tmpb = (char *)b;
    char **tmpaa = (char **)a;
    char **tmpbb = (char **)b;
    printf("*tmpa:  %s, **tmpaa:%s\n", tmpa, *tmpaa);
    printf("tmpa: %p, tmpb: %p, tmpaa: %p, tmpbb: %p\n",
             tmpa, tmpb, tmpaa, tmpbb);
    // 调试后发现:
    // a存放的是strs[0]的地址(char**类型)b存放的是strs[1]的地址(char**类型)        
    // strcmp()会将p地址对应的内容转化成字符串,也就是将strs[0~2]的地址转化成字符串,将得到一个乱码
    // 因为将(char **类型的数据转换成了char *类型的数据,这样解引用出来的字符串当然是错误的
    // 因此得先把a,b转化成char**,这样解引用以后才是一个char*的地址
    // return strcmp(tmpa, tmpb);
    // return strcmp(*tmpaa, *tmpbb);
    // 用指针的方式进行排序
    return strcmp(*(char **)a, *(char **)b);
    // 不能将二级指针直接解引用,这样借用用后的值为1个一级指针,而不是一个字符串,
    // 因此需要转换成char **类型后在进行解引用操作
    // return strcmp((char *)a, (char *)b);
}

int main()
{
    int n = 3;
    int i = 0;
    char **strs = (char **)malloc(n * sizeof(char *));
    char *str0 = (char *)malloc(100);
    for (i = 0; i < 3; ++i) {
        strs[i] = (char *)malloc(100);
    }
    printf("strs: %p, *strs: %p, strs[0]: %p, strs[1]: %p, strs[2]: %p\n",
            strs, *strs, strs[0], strs[1], strs[2]);
    printf("strs: %p, &strs[0]: %p, &strs[1]: %p, &strs[2]: %p\n",
            strs, &strs[0], &strs[1], &strs[2]);

    snprintf(strs[0], 100, "ff str0\n");
    snprintf(strs[1], 100, "dd str1\n");
    snprintf(strs[2], 100, "aa str2\n");

    printf("\nbefore sort:\n");
    for (i = 0; i < n; ++i) {
        printf("%s", strs[i]);
    }

    // 传入的参数为strs[0~2],即指针的指针
    qsort(strs, n, sizeof(char *), cmp);

    printf("\nafter sort:\n");
    for (i = 0; i < n; ++i) {
        printf("%s", strs[i]);
    }
    printf("\n");

    for (i = 0; i < n; ++i) {
        free(strs[i]);
    }
    free(strs);

    return 0;
}

通过以上代码可以活得一个经验,在进行字符串数组比时较需要注意如下问题:

不能在自定义的数据比较函数中return strcmp((char *)a, (char *)b);

而是应该return strcmp(*(char **)a, *(char **)b);

strs是一个指针数组,保存3个指针,也就是说&strs[0], &strs[1], &strs[2]都是二级指针,

地址分别为

0x555555757260

0x555555757268

0x555555757270

这3个二级指针分别各自保存1个一级指针,分别为strs[0], strs[1], strs[2]

指针的地址和保存的字串分别为:

0x5555557572f0 "ff str0\n"

0x555555757360 "dd str1\n"

0x5555557573d0 "aa str2\n"

而传入到cmp函数中的指针为二级指针&strs[0], &strs[1], &strs[2]

(用void *类型作为形参类型,并于不同类型数据的转换),

二级指针保存一级指针的地址对应关系为:

&strs[0]: 0x555555757260 ->  strs[0]:0x5555557572f0 "ff str0\n"

&strs[1]: 0x555555757268->  strs[0]:0x555555757360 "dd str1\n"

&strs[2]: 0x555555757270->  strs[0]:0x5555557573d0 "aa str2\n"

若将传入的二级指针转换成char *类型,则接应用时去除的字符串是一个乱码,

因为二级指针保存的值为1个一级指针,所以接应用后的值仍然是一个一级指针,

而这个一级指针指向的内容才是需要获取的字符串。

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

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

相关文章

【力扣】125. 验证回文串

问题描述 思路详情 本题目的重点是对java中字符串的各种API用法的掌握理解 1.首先只保留字母和数字 1.1可以使用正则表达式1.2 Character.isLetterOrDight(ch) &#xff0c;但是这个只能单个字符判断2.将大写字母全部小写3.验证是否为回文串 代码 通过正则表达式 &#xff…

JavaEE---计算机是如何工作的?

1.了解冯诺依曼体系结构 2.CPU的核心概念,CPU的两个重要指标(核心数和频率) 3.CPU执行指令的流程(指令表,一条一条指令,取指令,解析指令,执行指令) 4.操作系统核心概念(管理硬件,给软件提供稳定的运行环境) 5.进程的概念(运行起来的程序和可执行文件的区别) 6.进程的管理(…

gitee:创建仓库,存入本地文件至仓库

一、git下载 git:下载与安装-CSDN博客https://blog.csdn.net/weixin_46001736/article/details/144107485?sharetypeblogdetail&sharerId144107485&sharereferPC&sharesourceweixin_46001736&spm1011.2480.3001.8118 二、创建仓库 1、主页面->右上角新增…

Flink 安装与入门:开启流式计算新时代

在当今大数据蓬勃发展的时代&#xff0c;数据处理的时效性愈发关键。传统基于先存储再批量处理的数据方式&#xff0c;在面对诸如网站实时监控、异常日志即时分析等场景时&#xff0c;显得力不从心。随着 5G、物联网等技术的兴起&#xff0c;海量数据如潮水般涌来&#xff0c;且…

使用 Jina Embeddings v2 在 Elasticsearch 中进行后期分块

作者&#xff1a;来自 Elastic Gustavo Llermaly 在 Elasticsearch 中使用 Jina Embeddings v2 模型并探索长上下文嵌入模型的优缺点。 在本文中&#xff0c;我们将配置和使用 jina-embeddings-v2&#xff0c;这是第一个开源 8K 上下文长度嵌入模型&#xff0c;首先使用 semant…

XTuner 微调个人小助手认知 -- 书生大模型实训营第4期基础岛第五关

目录 基础任务 任务要求 算力要求 环境配置与数据准备 使用 conda 先构建一个 Python-3.10 的虚拟环境 安装 XTuner 验证安装 修改提供的数据 创建一个新的文件夹用于存储微调数据 ​编辑 创建修改脚本 执行脚本 查看数据 训练启动 复制模型 修改 Config 启动…

使用vcpkg自动链接tinyxml2时莫名链接其他库(例如boost)

使用vcpkg自动链接tinyxml2时莫名链接其他库&#xff08;例如boost&#xff09; vcpkg的自动链接功能非常方便&#xff0c;但在某些情况下会出现过度链接的问题。 链接错误症状 以tinyxml2为例&#xff0c;程序中调用tinyxml2的函数后&#xff0c;若vcpkg中同时存在opencv和…

06_数据类型

数据类型 数据类型分类 JavaScript 语言的每一个值,都属于某一种数据类型。JavaScript 的数据类型,共有六种。(ES6 又新增了第七种 Symbol 类型的值和第八种 BigInt类型,当前课程暂不涉及) 据类型分类 原始类型(基础类型) var age = 20, var name = 尚学堂"; var le…

GitLab 使用过程中常见问题及解决方案

开发人员常见问题及解决方案 合并请求被拒绝 原因&#xff1a;代码质量问题、安全漏洞或流水线失败。解决方案&#xff1a; 使用 Code Quality 工具检查代码质量。查看流水线日志&#xff0c;修复单元测试、编译错误或扫描问题。优化静态分析&#xff08;SAST&#xff09;结果&…

网络空间安全之一个WH的超前沿全栈技术深入学习之路(13-2)白帽必经之路——如何用Metasploit 渗透到她的心才不会让我释怀

欢迎各位彦祖与热巴畅游本人专栏与博客 你的三连是我最大的动力 以下图片仅代表专栏特色 [点击箭头指向的专栏名即可闪现] 专栏跑道一 ➡️网络空间安全——全栈前沿技术持续深入学习 专栏跑道二 ➡️ 24 Network Security -LJS ​ ​ ​ 专栏跑道三 ➡️ MYSQL REDIS Advan…

机器学习6_支持向量机_算法流程

最大化&#xff1a; 限制条件&#xff1a; &#xff08;1&#xff09; &#xff08;2&#xff09; 如何求解这个对偶问题&#xff0c;同时基于对偶问题给出支持向量机算法的统一流程。 (核函数) 只要知道核函数&#xff0c;就可以求个这个最优化的对偶问题。 求解了这个对偶…

DM8 Docker环境部署

1 环境说明 类别 版本 介质 操作系统 CentOS-7-x86_64-DVD-2207-02.iso docker-27.3.1.tgz Dm8 Docker DM8开发版 dm8_20241119_x86_rh6_64_rq_ent_8.1.2.84.tar 备注&#xff1a; 下载docker源码包 下载地址&#xff1a; https://download.docker.com/linux/static/stable/x…

DevOps工程技术价值流:Jenkins驱动的持续集成与交付实践

一、Jenkins系统概述 Jenkins&#xff1a;开源CI/CD引擎的佼佼者 Jenkins&#xff0c;作为一款基于Java的开源持续集成&#xff08;CI&#xff09;与持续交付&#xff08;CD&#xff09;系统&#xff0c;凭借其强大的插件生态系统&#xff0c;成为DevOps实践中不可或缺的核心…

apache实现绑定多个虚拟主机访问服务

1个网卡绑定多个ip的命令 ip address add 192.168.45.140/24 dev ens33 ip address add 192.168.45.141/24 dev ens33 在linux服务器上&#xff0c;添加多个站点资料&#xff0c;递归创建三个文件目录 分别在三个文件夹下&#xff0c;建立测试页面 修改apache的配置文件http.…

【css实现收货地址下边的平行四边形彩色线条】

废话不多说&#xff0c;直接上代码&#xff1a; <div class"address-block" ><!-- 其他内容... --><div class"checked-ar"></div> </div> .address-block{height:120px;position: relative;overflow: hidden;width: 500p…

启动SpringBoot

前言&#xff1a;大家好我是小帅&#xff0c;今天我们来学习SpringBoot 文章目录 1. 环境准备2. Maven2.1 什么是Maven2.2 创建⼀个Maven项⽬2.3 依赖管理2.3.1 依赖配置2.3.2 依赖传递2.3.4 依赖排除2.3.5 Maven Help插件&#xff08;plugin&#xff09; 2.4 Maven 仓库2.6 中…

ERROR CoarseGrainedExecutorBackend: RECEIVED SIGNAL TERM

如果你的spark on yarn任务正常运行&#xff0c;能得到结果&#xff0c;但是日志中出现了如下的报错 看见这个报错不要慌张&#xff0c;这个是你开启了动态伸缩容器&#xff0c;且当这个容器触发了空闲关闭的阈值&#xff0c;默认是60秒&#xff0c;这时候该容器会被删除掉&a…

SpringBoot实战(三十二)集成 ofdrw,实现 PDF 和 OFD 的转换、SM2 签署OFD

目录 一、OFD 简介1.1 什么是 OFD&#xff1f;1.2 什么是 版式文档&#xff1f;1.3 为什么要用 OFD 而不是PDF&#xff1f; 二、ofdrw 简介2.1 定义2.2 Maven 依赖2.3 ofdrw 的 13 个模块 三、PDF/文本/图片 转 OFD&#xff08;ofdrw-conterver&#xff09;3.1 介绍&#xff1a…

QT6学习第四天 感受QT的文件编译

QT6学习第四天 感受QT的文件编译 使用纯代码编写程序新建工程 使用其他编辑器纯代码编写程序并在命令行运行使用 .ui 表单文件生成界面使用自定义 C 窗口类使用现成的QT Designer界面类 使用纯代码编写程序 我们知道QT Creator中可以用拖拽的方式在 .ui 文件上布局&#xff0c…

C++:用红黑树封装map与set-2

文章目录 前言一、红黑树封装map与set中const迭代器1. 框架的搭建2. set实现const迭代器3. map实现const迭代器 二、operator[ ]1. operator[ ]要达成的样子2. insert的改变 三. 解决insert里set中的问题四. 解决map中的operator[ ]总结用红黑树封装map与set代码 前言 前面我们…