V2.0
C++基础教程<br />——<br />作业及参考答案全部汇总文档<br/>节7指针高级阶段作业<br/><br/>最新版本V2.0
<br>王道C++团队<br/>COPYRIGHT ⓒ 2021-2024. 王道版权所有基础题篇实现动态拼接字符串手动实现动态数组Vector二级指针基础练习题二级指针/单链表练习题qsort函数基础语法练习扩展题篇扩展:动态字符串Vector扩展:错误处理宏函数扩展:动态内存分配以及qsort函数扩展:动态拼接字符串数组以及qsort函数The End
Gn!
下面都是一些基础的语法、概念编程练习题。
Gn!
标准库函数strcat会将一个字符串追加到另一个字符串的末尾。
现在我们编写一个函数把两个字符串拼接起来,返回拼接的结果,但要求不改变其中任何一个字符串。其函数声明如下:
xxxxxxxxxx
11char* my_strcat(const char* prefix, const char* suffix);
形参prefix表示前缀,suffix表示后缀,拼接的结果是prefix + suffix
比如:my_strcat("abc", "d"),拼接的结果是"abcd"。
思路:
在堆上分配内存空间,用于存储结果字符串。
将prefix和suffix两个字符串的字符信息复制进去。
参考代码如下:
参考代码:
这个问题非常简单,是一个基础的动态内存分配问题。参考代码如下:
xxxxxxxxxx
291// 在堆上动态分配内存拼接两个字符串
2char* dynamic_strcat(const char* prefix, const char* suffix) {
3// 计算拼接后字符串的长度
4int new_str_len = strlen(prefix) + strlen(suffix);
5char *new_str = malloc(new_str_len + 1); // char在各平台上长度都是1,所以不用乘了
6
7if (new_str == NULL) {
8printf("ERROR: malloc failed in dynamic_strcat!\n");
9exit(1);
10}
11
12// 长度是精确计算得出的,不用担心越界访问
13strcat(strcpy(new_str, prefix), suffix);
14
15return new_str;
16}
17
18int main(void) {
19char str1[] = "hello";
20char str2[] = " world!";
21char* result_str = dynamic_strcat(str1, str2); // 注意只要涉及动态内存分配,一律用指针类型。这里不能用数组类型
22
23puts(result_str);
24
25// 现在不再使用result字符串了,不要忘记free它
26free(result_str);
27
28return 0;
29}
以上。
Gn!
手动实现动态数组Vector,基于以下结构体定义和函数声明:
xxxxxxxxxx
271typedef int ElementType;
2
3typedef struct {
4ElementType *data; // 指向堆空间的数组
5int size; // 元素的个数
6int capacity; // 数组的容量
7} Vector;
8
9// 请实现下面方法
10
11// 初始化一个Vector动态数组
12Vector *vector_create(void);
13
14// 销毁一个Vector动态数组,释放内存。
15void vector_destroy(Vector *v);
16
17// 向动态数组末尾添加一个元素
18void vector_push_back(Vector *v, ElementType val);
19
20// 在动态数组最前面添加元素,所有元素依次后移
21void vector_push_front(Vector *v, ElementType val);
22
23// 将元素val添加到索引为idx的位置,idx后面的元素依次后移
24void vector_insert(Vector *v, int idx, ElementType val);
25
26// 遍历打印整个Vector动态数组
27void vector_print(Vector *v);
注意:
你应该将上述代码放在头文件中,并且自己添加头文件保护语法。
提交作业时,你应该提交头文件,实现Vector的源文件,测试源文件三个文件的代码
参考代码如下:
参考代码:
头文件加上保护语法,代码如下:
xxxxxxxxxx
32123
4typedef int ElementType;
5
6typedef struct {
7ElementType *data; // 指向堆空间的数组
8int size; // 元素的个数
9int capacity; // 数组的容量
10} Vector;
11
12// 请实现下面方法
13
14// 初始化一个Vector动态数组
15Vector *vector_create(void);
16
17// 销毁一个Vector动态数组,释放内存。
18void vector_destroy(Vector *v);
19
20// 向动态数组末尾添加一个元素
21void vector_push_back(Vector *v, ElementType val);
22
23// 在动态数组最前面添加元素,所有元素依次后移
24void vector_push_front(Vector *v, ElementType val);
25
26// 将元素val添加到索引为idx的位置,idx后面的元素依次后移
27void vector_insert(Vector *v, int idx, ElementType val);
28
29// 遍历打印整个Vector动态数组
30void vector_print(Vector *v);
31
32// !VECTOR_H
实现源文件代码:
xxxxxxxxxx
1151234
567
8
9// 在C语言中,static修饰函数表示此函数仅在当前文件内部生效
10// 类似于C++或Java中的访问权限修饰符private
11static void vector_resize(Vector *v) {
12// 只要调用这个函数肯定就是需要扩容的
13int old_capacity = v->capacity;
14
15int new_capacity = (old_capacity < THRESHOLD) ?
16(old_capacity << 1) : // 容量还未超出阈值每次扩容2倍
17(old_capacity + (old_capacity >> 1)); // 容量超出阈值每次扩容1.5倍
18
19// 利用realloc重新分配动态数组
20// realloc惯用法
21ElementType *tmp = realloc(v->data, new_capacity * sizeof(ElementType));
22if (tmp == NULL) {
23printf("realloc failed in resize_vector.\n");
24exit(1); // 扩容失败,退出整个程序。或者也可以做别的处理
25}
26// 扩容成功,重新赋值Vector成员
27v->data = tmp;
28v->capacity = new_capacity;
29}
30
31// 初始化一个Vector动态数组
32Vector *vector_create(void) {
33// 先在堆上申请结构体
34Vector *v = calloc(1, sizeof(Vector));
35if (v == NULL) {
36printf("calloc failed in vector_create.\n");
37return NULL; // 创建失败返回空指针
38}
39
40// 申请动态数组,并初始化Vector的data成员
41v->data = calloc(DEFAULT_CAPACITY, sizeof(ElementType));
42if (v->data == NULL) {
43printf("malloc failed in vector_create.\n");
44// 不要忘记free结构体Vector,否则会导致内存泄漏
45free(v);
46return NULL; // 创建失败返回空指针
47}
48
49// 继续初始化Vector的其它成员
50v->capacity = DEFAULT_CAPACITY;
51
52return v;
53}
54
55// 销毁一个Vector动态数组,释放内存。
56void vector_destroy(Vector *v) {
57free(v->data);
58free(v);
59}
60
61// 向动态数组末尾添加一个元素
62void vector_push_back(Vector *v, ElementType val) {
63// 先判断是否需要扩容
64if (v->capacity == v->size) {
65vector_resize(v);
66}
67// 扩容完成后或不需要扩容,即向末尾添加元素
68v->data[v->size] = val;
69v->size++;
70}
71
72// 在动态数组最前面添加元素,所有元素依次后移
73void vector_push_front(Vector *v, ElementType val) {
74// 先判断是否需要扩容
75if (v->capacity == v->size) {
76vector_resize(v);
77}
78// 扩容完成后或不需要扩容,即从首元素开始将所有元素向后移动
79// 倒着遍历数组方便向后移动元素
80for (int i = v->size - 1; i >= 0; i--) {
81v->data[i + 1] = v->data[i];
82}
83// 新增的元素成为首元素
84v->data[0] = val;
85v->size++;
86}
87
88// 将元素val添加到索引为idx的位置,idx后面的元素依次后移
89void vector_insert(Vector *v, int idx, ElementType val) {
90// 先做参数校验,避免传入非法索引
91if (idx < 0 || idx > v->size) { // 做插入操作时索引合法范围是[0, size]
92printf("Illegal argument: idx = %d, size = %d\n", idx, v->size);
93exit(1); // 直接退出进程,也可以用别的方式进行错误处理
94}
95// 判断是否需要扩容
96if (v->capacity == v->size) {
97vector_resize(v);
98}
99// 扩容完成后或不需要扩容,即从原本的idx元素开始将所有元素向后移动
100for (int i = v->size - 1; i >= idx; i--) {
101v->data[i + 1] = v->data[i];
102}
103// idx位置空出来,将它放进去
104v->data[idx] = val;
105v->size++;
106}
107
108// 遍历打印整个Vector动态数组
109void vector_print(Vector *v) {
110printf("[");
111for (int i = 0; i < v->size; i++){
112printf("%d, ", v->data[i]);
113}
114printf("\b\b]\n");
115}
测试用例代码:
xxxxxxxxxx
3012345
67int main(void) {
8Vector *v = vector_create();
9if (v == NULL) {
10printf("error: vector_create failed in main.\n");
11exit(1);
12}
13
14// 测试自动扩容机制
15for (int i = 0; i < N; ++i) {
16vector_push_back(v, i + 1);
17}
18vector_print(v); // 期望打印结果: 1-100
19
20// 测试头部插入新元素
21vector_push_front(v, 0);
22vector_print(v); // 期望打印结果: 0, 1-100
23
24// 测试根据索引插入新元素
25vector_insert(v, 0, -1);
26vector_print(v); // 期望打印结果: -1, 0, 1-100
27
28vector_destroy(v);
29return 0;
30}
以上。
Gn!
在main函数中定义两个局部变量:
xxxxxxxxxx
21int a = 10;
2int b = 20;
请实现以下两个函数,用于完成局部变量a和b的交换:
xxxxxxxxxx
21void swap(int *pa, int *pb);
2void swap2(int **pa, int **pb);
这两个函数都应该可以实现交换a和b的值。
参考代码如下:
参考代码如下:
xxxxxxxxxx
37123
4// 函数声明
5void swap(int *pa, int *pb);
6void swap2(int **pa, int **pb);
7
8int main(void) {
9int a = 10;
10int b = 20;
11int *ptrA = &a;
12int *ptrB = &b;
13
14printf("初始值:a = %d, b = %d\n", a, b);
15
16// 使用一级指针交换值
17swap(&a, &b);
18printf("使用 swap 函数第一次交换后:a = %d, b = %d\n", a, b);
19
20// 使用二级指针交换指针
21swap2(&ptrA, &ptrB);
22printf("使用 swap2 函数再次交换后:a = %d, b = %d\n", *ptrA, *ptrB);
23
24return 0;
25}
26
27void swap(int *pa, int *pb) {
28int temp = *pa;
29*pa = *pb;
30*pb = temp;
31}
32
33void swap2(int **pa, int **pb) {
34int temp = **pa;
35**pa = **pb;
36**pb = temp;
37}
以上。
Gn!
基于以下链表结点结构体类型的定义:
xxxxxxxxxx
51typedef int ElementType;
2typedef struct node {
3int data;
4struct node* next;
5} Node;
在main函数中定义一个head头指针等于NULL代表此时单链表为空:
xxxxxxxxxx
11Node *head = NULL;
利用二级指针语法实现以下函数:
xxxxxxxxxx
81// 头插法新增一个结点
2void insert_head(Node** head, ElementType new_val);
3// 修改第一个结点的元素值,如果链表为空,则用new_val初始化第一个结点
4void modify_first_node(Node** head, int new_val);
5// 尾插法新增一个结点
6void insert_tail(Node** head, ElementType new_val);
7// 打印单链表 格式为:1 -> 2 -> 3 ->...
8void print_list(Node *head);
注:尾插法会稍微麻烦一些,可以放在最后实现。
参考代码如下:
参考代码:
参考代码如下:
xxxxxxxxxx
104123
4typedef struct node {
5int data;
6struct node *next;
7} Node;
8
9// 单链表的头插法
10void insert_head(Node **head, int new_val) {
11// 1.创建新节点
12Node *new_node = malloc(sizeof(Node));
13if (new_node == NULL) {
14printf("malloc failed in insert_head.\n");
15exit(1);
16}
17// 2.初始化新节点的数据域
18new_node->data = new_val;
19// 3.新结点的next指针指向原本第一个节点
20new_node->next = *head;
21// 4.将头指针指向新结点
22*head = new_node;
23}
24
25// 修改第一个结点的元素值
26void modify_first_node(Node **head, int new_val) {
27if (*head == NULL) {
28// 当前单链表为空,此时为头插法
29*head = malloc(sizeof(Node));
30if (*head == NULL) {
31printf("malloc failed in modify_first_node.\n");
32exit(1);
33}
34(*head)->data = new_val;
35(*head)->next = NULL;
36}
37else {
38(*head)->data = new_val;
39}
40}
41
42// 优化:既然头插法已经实现了,那么也可以直接调用,而不是重新实现
43void modify_first_node2(Node **head, int new_val) {
44if (*head == NULL) { // 链表为空时,初始化第一个节点
45insert_head(head, new_val);
46}
47else { // 链表非空时,修改第一个节点的数据
48(*head)->data = new_val;
49}
50}
51
52// 尾插法新增一个结点
53void insert_tail(Node **head, int val) {
54// 创建新结点
55Node *new_node = (Node *)malloc(sizeof(Node));
56if (new_node == NULL) {
57printf("malloc failed in insert_tail.\n");
58exit(-1);
59}
60new_node->data = val; // 设置新节点的数据
61new_node->next = NULL; // 新节点将成为尾节点,其下一个节点指针为NULL
62
63if (*head == NULL) {
64// 如果链表为空,新节点即为第一个结点
65*head = new_node;
66}
67else {
68// 遍历找到当前链表的最后一个结点
69Node *curr = *head;
70while (curr->next != NULL) {
71curr = curr->next;
72}
73curr->next = new_node; // 将新节点追加到链表尾部
74}
75}
76
77// 打印单链表 格式为:1 -> 2 -> 3 ->...
78void print_list(Node *head) {
79Node *curr = head;
80while (curr != NULL) { // 遍历此单链表
81printf("%d", curr->data);
82if (curr->next != NULL) {
83printf(" -> ");
84}
85curr = curr->next;
86}
87printf("\n");
88}
89
90// 测试用例
91int main(void) {
92// 初始化一个空链表
93Node *head = NULL;
94insert_head(&head, 5);
95insert_head(&head, 4);
96insert_head(&head, 3);
97insert_head(&head, 2);
98insert_head(&head, 1);
99
100modify_first_node(&head, 100);
101insert_tail(&head, 6);
102print_list(head);
103return 0;
104}
以上。
Gn!
给你下面一个Student,学生结构体类型的定义:
xxxxxxxxxx
51// Student学生结构体类型定义
2typedef struct {
3char name[25];
4int score;
5} Student;
假设你有一个学生结构体对象数组,如下所示:
xxxxxxxxxx
121Student stus[] = {
2{"Alice", 85},
3{"Bob", 92},
4{"Charlie", 85},
5{"David", 85},
6{"Eve", 92},
7{"Frank", 75},
8{"Grace", 85},
9{"Hannah", 95},
10{"Ivy", 78},
11{"Jack", 85}
12};
你的任务是使用
qsort
函数对这个结构体数组进行排序:
规则1:将全体结构体对象,按照学生成绩从高到低进行排序。
规则2:先将全体结构体对象,按照学生成绩从低到高进行排序,如果成绩一致,则那么按照名字的字典顺序进行排序。
最后,你还需要实现一个函数用于打印整个学生数组:
xxxxxxxxxx
11void print_students(Student stus[], int size);
参考代码如下:
参考代码如下:
可以直接用上面题目中给出的数据,不需要自己编数据了:
xxxxxxxxxx
6612345
6typedef struct {
7char name[25];
8int score;
9} Student;
10
11
12// 比较函数1:按成绩从高到低排序
13int compare_scores_desc(const void *a, const void *b) {
14Student *stu1 = (Student *)a;
15Student *stu2 = (Student *)b;
16return stu2->score - stu1->score; // 逆序排列
17}
18
19// 比较函数2:按成绩从低到高排序,成绩相同则按名字字典顺序排序
20int compare_scores_asc(const void *a, const void *b) {
21Student *stu1 = (Student *)a;
22Student *stu2 = (Student *)b;
23if (stu1->score == stu2->score) {
24return strcmp(stu1->name, stu2->name); // 字典序比较名字
25}
26return stu1->score - stu2->score; // 正序排列
27}
28
29// 打印学生数组的全部对象
30void print_students(Student stus[], int size) {
31for (int i = 0; i < size; i++) {
32printf("Name: %s, Score: %d\n", stus[i].name, stus[i].score);
33}
34printf("\n");
35}
36
37int main(void) {
38Student stus[] = {
39{"Alice", 85},
40{"Bob", 92},
41{"Charlie", 85},
42{"David", 85},
43{"Eve", 92},
44{"Frank", 75},
45{"Grace", 85},
46{"Hannah", 95},
47{"Ivy", 78},
48{"Jack", 85}
49};
50int len = ARR_SIZE(stus);
51
52printf("原始学生数组是:\n");
53print_students(stus, len);
54
55// 按规则1排序:成绩从高到低
56qsort(stus, len, sizeof(Student), compare_scores_desc);
57printf("将学生按照成绩由高到低排序:\n");
58print_students(stus, len);
59
60// 按规则2排序:成绩从低到高,成绩相同按名字排序
61qsort(stus, len, sizeof(Student), compare_scores_asc);
62printf("将学生按照成绩和名字综合排序:\n");
63print_students(stus, len);
64
65return 0;
66}
以上。
Gn!
以下题目都属于扩展题。
Gn!
你已经手动实现了一个自己的Vector,很好,这很酷。那么请思考和完成以下扩展的题目。
现在请你回答第一个问题:
原本的动态数组用于存储int类型元素,是int类型动态数组。现在我希望你能把它变成字符串动态数组,应该怎么改代码吗?
如果仅在原本代码的基础上修改ElementType的类型,那么请你思考第二个问题:
此时Vector虽然能存字符串了,但只能存什么字符串呢?
把以上问题都考虑清楚后,请完成以下测试工作:
使用push_back函数向动态字符串数组中循环添加100个字符串元素,这些字符串依次是:
"0"
"01"
"012"
"0123"
...... "01234567890"
"012345678901"
"0123456789012"
....
添加完成后,请遍历打印这个动态字符串数组。请将这段测试动态字符串数组Vector的代码贴在答案里。
Vector的其它实现代码不需要贴进来。
注意,实现这个需求时,不需要考虑字符串的生命周期问题。
参考回答如下:
参考代码:
前两个问题的答案:
只需要把ElementType的类型从
int
改为char *
就可以了。如果只是改变ElementType的类型,那么从最佳实践出发:
这个Vector最好存储字面值字符串、全局变量等静态存储期限的字符。因为不用考虑它们的生命周期问题,它们在整个程序运行期间都生效。
如果存储局部变量字符串,那么这个Vector将不能跨函数使用。
如果存堆字符串,那么这个Vector需要管理它存储的字符串的生命周期,这就比较麻烦了。
但对于我们下面的这个需求而言:由于需要动态构建字符串,所以不可能使用字符串字面值,局部变量/全局变量字符串也不可用,所以比较好是实现就是使用堆字符串。
参考代码如下:
xxxxxxxxxx
41123456
7int main(void) {
8// 在栈上创建一个Vector指针,指向堆上的结构体对象
9Vector* v = vector_create();
10if (v == NULL) {
11printf("Error: vector_create failed.\n");
12return 1;
13}
14for (int i = 0; i < N; i++) { // 循环100次向动态字符串数组中插入字符串
15// 用calloc更好,它会自动初始化所有元素是空字符.这对字符串而言很关键,因为第一个空字符作为结束标志
16int str_len = i + 1;
17char* str = calloc(1, (str_len + 1));
18if (str == NULL) {
19printf("Error: calloc str failed.\n");
20return 1;
21}
22for (int j = 0; j <= i; j++) {
23str[j] = '0' + j % 10;
24}
25vector_push_back(v, str);
26}
27
28for (int i = 0; i < N; i++) {
29printf("%s\n", v->data[i]);
30}
31
32// 销毁动态字符串数组指向的所有堆字符串
33for (int i = 0; i < N; i++) {
34free(v->data[i]);
35}
36
37// 用完了销毁Vector
38vector_destroy(v);
39
40return 0;
41}
以上。
Gn!
规范的代码应该对内存分配函数malloc、calloc、realloc的返回值做判NULL处理。因为这些函数的返回值是NULL时意味着内存分配失败,函数出错,需要做错误处理。
后续的学习中,我们还会学习很多这样的函数,需要对它们的返回值做错误处理。
如果每次都需要将错误处理的结构都重新写一遍,那确实很折磨。为了更好的复用代码,简化操作,我们建议大家把这个结构用宏函数定义出来。
可以基于以下结构定义宏函数:
xxxxxxxxxx
41if (p == NULL) {
2printf("error: calloc failed in main.\n");
3exit(1);
4}
提示,为了更好的复用性,这个宏函数最好有三个参数:
1.函数返回值。错误处理是依据函数返回值进行的,所以需要函数返回值作为参数。也就是上述结构中的p
2.函数返回值的错误标志。当返回值是这个错误标志时,做错误处理。上述代码结构中就是NULL
3.msg。错误处理时打印的报错信息。也就是"calloc failed in main."这一段。
这个宏函数的实现不唯一,也不可能做到一个宏函数统一处理所有的情况,大家可以自己想一想写一下,也可以直接参考我给的实现。
最后,你可以将这个宏函数放到你的代码模板中,这样就更方便了。
参考实现如下:
参考代码实现:
xxxxxxxxxx
1312
3
4
5
6
7
8
9int main(void) {
10int *p = malloc(10 * sizeof(int));
11ERROR_CHECK(p, NULL, "malloc failed in main.");
12return 0;
13}
以上。
Gn!
(扩展题,可以放到最后做)
编写一个C语言程序,完成以下任务:
首先,从键盘录入一个整数,表示接下来要录入的字符串的数量。
紧接着根据这个数量,从键盘连续录入字符串,每次都会将一整行键盘录入成一个字符串
这些被录入的字符串需要存入一个字符串数组,然后将该字符串数组按照以下两种规则排序遍历打印:
1.按照字符串的长度,从长到短排序
2.先按照字符串的长度从短到长排序,长度一致的字符串按照字典顺序排序。
程序的执行图,大致如下:
注意:
使用fgets读一整行字符串的话,不要忘记去除字符串中的换行符。
假设每个字符串的长度都不会超过1024。
参考代码如下:
参考代码如下:
xxxxxxxxxx
8512345
67
8// 字符串比较函数:按长度从长到短排序
9int compare_length_desc(const void *a, const void *b) {
10/*
11a 和 b 是指向数组中两个待比较元素的指针。
12数组中的元素是char*字符串类型,指向它的指针也就是char**二级指针类型
13所以这里应该将a和b强转成(char**)类型,然后解引用一次
14才能够得到两个待比较的字符串
15*/
16const char *str1 = *(const char **)a;
17const char *str2 = *(const char **)b;
18int len1 = strlen(str1);
19int len2 = strlen(str2);
20if (len1 != len2) {
21return len2 - len1; // 从长到短排序
22}
23return strcmp(str1, str2); // 如果长度相同,则按字典顺序排序
24}
25
26// 字符串比较函数:先按长度从短到长排序,长度一样则按字典序
27int compare_length_asc(const void *a, const void *b) {
28const char *str1 = *(const char **)a;
29const char *str2 = *(const char **)b;
30int len1 = strlen(str1);
31int len2 = strlen(str2);
32if (len1 != len2) {
33return len1 - len2; // 从短到长排序
34}
35return strcmp(str1, str2); // 如果长度相同,则按字典顺序排序
36}
37
38int main() {
39int str_num;
40printf("请输入您要输入的字符串的个数:");
41scanf("%d", &str_num);
42// 为了避免回车符对下面录入字符串产生影响,清空缓冲区
43while (getchar() != '\n');
44
45/*
46申请长度是str_num的char指针数组
47返回值类型是void*,在接收返回值也必须给一个指针类型进行类型转换
48所以不能直接写char *arr[]
49而需要写指向指针的指针类型,也就是char**,二级指针
50*/
51char **str_array = malloc(str_num * sizeof(char *));
52for (int i = 0; i < str_num; i++) {
53// 每个字符串的长度都不超过1024
54str_array[i] = malloc(MAX_STRLEN + 1);
55printf("请输入第 %d 个字符串:", i + 1);
56fgets(str_array[i], MAX_STRLEN + 1, stdin);
57// 使用strlen检查并移除换行符
58int length = strlen(str_array[i]);
59if (length > 0 && str_array[i][length - 1] == '\n') {
60str_array[i][length - 1] = '\0';
61}
62}
63
64// 按长度从长到短排序并打印
65qsort(str_array, str_num, sizeof(char *), compare_length_desc);
66printf("按长度从长到短排序的结果:\n");
67for (int i = 0; i < str_num; i++) {
68printf("%s\n", str_array[i]);
69}
70
71// 先按长度从短到长排序,长度一样则按字典序排序
72qsort(str_array, str_num, sizeof(char *), compare_length_asc);
73printf("按长度从短到长排序,长度一致时按字典顺序的结果:\n");
74for (int i = 0; i < str_num; i++) {
75printf("%s\n", str_array[i]);
76}
77
78// 释放内存
79for (int i = 0; i < str_num; i++) {
80free(str_array[i]);
81}
82free(str_array);
83
84return 0;
85}
以上。
Gn!
首先给定以下字符数组:
xxxxxxxxxx
91char* strings[] = {
2"4show ",
3"1code.",
4"5cheap, ",
5"6is ",
6"7Talk ",
7"2the ",
8"3me "
9};
请你先利用
qsort
函数将此字符串数组,按照逆字典顺序排序。然后将这个字符串数组中的每一个字符串,按照从前到后的顺序,拼接组装成一个新字符串,并且在拼接时删除每个子字符串的第一个字符。这样你将得到Linux之父Linus Torvalds的名言:“Talk is cheap, show me the code.”
要求使用动态分配内存来存储结果字符串,函数声明如下:
xxxxxxxxxx
11char* concat_strs(char* strings[], int count);
参考代码如下:
参考代码:
xxxxxxxxxx
60123456
7// 函数声明
8char *concat_strs(char *strings[], int count);
9int cmp_ruler(const void *a, const void *b);
10
11int main(void) {
12char *strings[] = {
13"4show ",
14"1code.",
15"5cheap, ",
16"6is ",
17"7Talk ",
18"2the ",
19"3me "
20};
21int count = ARR_SIZE(strings); // 字符串数组的长度
22
23// 使用 qsort 对字符串数组进行逆字典序排序
24qsort(strings, count, sizeof(char *), cmp_ruler);
25
26// 调用 concat_strs 函数进行拼接合并
27char *result = concat_strs(strings, count);
28printf("拼接并排序后的字符串: %s\n", result);
29
30return 0;
31}
32
33// 字符串逆字典序比较函数
34int cmp_ruler(const void *a, const void *b) {
35const char *str1 = *(const char **)a;
36const char *str2 = *(const char **)b;
37return strcmp(str2, str1); // 逆字典顺序,所以传参要反过来
38}
39
40// 拼接并排序字符串数组
41char *concat_strs(char *strings[], int count) {
42// 计算所有字符串的总长度,去掉每个字符串的第一个字符
43int total_length = 0;
44for (int i = 0; i < count; i++) {
45total_length += strlen(strings[i]) - 1; // 减去第一个字符
46}
47// 动态分配内存
48char *result = (char *)calloc(total_length + 1, sizeof(char)); // +1 用于存储末尾的空字符
49if (result == NULL) {
50printf("error: malloc failed in concat_strs.\n");
51exit(-1);
52}
53
54// 拼接所有排序后的字符串,去掉每个字符串的第一个字符
55for (int i = 0; i < count; i++) {
56strcat(result, strings[i] + 1); // 跳过第一个字符
57}
58
59return result;
60}
以上。