实验六记录,加深对指针的理解
学习到指针函数(返回值类型为指针,形如“*p()”)和函数指针(指向函数的指针,可以间接访问函数,形如“(*p)()”)的区别,掌握了带有参数的main的用法。实现动态内存分配,strlen测量输入的字符串t的长度,+1是为了留一个空间放\0,malloc函数(需要声明stdlib.h库函数)会在堆区开辟对应大小的内存空间,并返回一个类型为void*的指针,而欲将该值赋给类型为char*的s[i]
小白记录学习过程,实验小结前均为ai辅助编写,myversion下的代码为学习后自己默写出的合理代码,小结处为独立编写,总结了从ai学习到的知识
实验6 指针实验
6.1、实验目的
(1)熟练掌握指针的说明、赋值、使用。
(2)掌握用指针引用数组的元素,熟悉指向数组的指针的使用。
(3)熟练掌握字符数组与字符串的使用,掌握指针数组及字符指针数组的用法。
(4)掌握指针函数与函数指针的用法。
(5)掌握带有参数的main函数的用法。
6.2、实验题目及要求
1、源程序改错题
在下面所给的源程序中,函数strcopy(t, s)的功能是将字符串s复制给字符串t,并且返回串t的首地址。请单步跟踪程序,根据程序运行时出现的现象或观察到的字符串的值,分析并排除源程序的逻辑错误,使之能按照要求输出如下结果:
Input a string:
programming↙ (键盘输入)
programming
Input a string again:
language↙ (键盘输入)
language
1 #include<stdio.h>
2 char *strcopy(char *, const char *);
3 int main(void)
4 {
5 char *s1, *s2, *s3;
6 printf("Input a string:\n", s2);
7 scanf("%s", s2);
8 strcopy(s1, s2);
9 printf("%s\n", s1);
10 printf("Input a string again:\n", s2);
11 scanf("%s", s2);
12 s3 = strcopy(s1, s2);
13 printf("%s\n", s3);
14 return 0;
15 }
/*将字符串s复制给字符串t,并且返回串t的首地址*/
16 char * strcopy(char *t, const char *s)
17 {
18 while(*t++ = *s++);
19 return (t);
20 }
解答:
- 错误修改:
- 第5行未对三个字符指针初始化造成野指针,正确形式为
char S1[80] = { 0 };
char S2[80] = { 0 };
char S3[80] = { 0 };
char* s1 = S1;
char* s2 = S2;
char* s3 = S3;
2)第12行不能将返回值为字符指针的函数赋值给另一个字符指针,正确形式为:
strcopy(s3,s2);
(2)错误修改后的运行结果:

图6-1源程序改错后的运行截图
2、源程序完善、修改替换题
(1)下面程序中函数strsort用于对字符串进行升序排序,在主函数中输入N个字符串(字符串长度不超过49)存入通过malloc动态分配的存储空间,然后调用strsort对这N个串按字典序升序排序(提示:使用strcmp函数比较字符(串)大小)。
①请在源程序中的下划线处填写合适的代码来完善该程序。
#include<stdio.h>
#include<_________>
#include<string.h>
#define N 4
/*对指针数组s指向的size个字符串进行升序排序*/
void strsort(char *s[], int size)
{
_______temp;
int i, j;
for(i=0; i<size-1; i++)
for (j=0; j<size-i-1; j++)
if (___________)
{
temp = s[j];
___________;
s[j+1] = temp;
}
}
int main()
{
int i;
char *s[N], t[50];
for (i=0; i<N; i++)
{
gets(t);
s[i] = (char *)malloc(strlen(t)+1);
strcpy(_______);
}
strsort(________);
for (i=0; i<N; i++) {puts(s[i]); free(s[i]);}
return 0;
}
②数组作为函数参数其本质类型是指针。例如,对于形参char *s[],编译器将其解释为char **s,两种写法完全等价。请用二级指针形参重写strsort函数,并且在该函数体的任何位置都不允许使用下标引用。
解答:①
1)完善后的源程序清单如下:
void strsort(char* s[], int size)
{
char* temp;
int i, j;
for (i = 0; i < size - 1; i++)
for (j = 0; j < size - i - 1; j++)
if (strcmp(s[j], s[j + 1]) > 0)
{
temp = s[j];
s[j] = s[j+1];
s[j + 1] = temp;
}
}
int main()
{
int i;
char* s[N], t[50];
for (i = 0; i < N; i++)
{
gets(t);
s[i] = (char*)malloc(strlen(t) + 1);
strcpy(s[i],t);
}
strsort(s,N);
for (i = 0; i < N; i++) { puts(s[i]); free(s[i]); }
return 0;
}
2)完善后的运行截图:

图6-2程序完善题(1)的运行截图
②
- 替换后的源程序清单如下:
void strsort(char** s, int size)
{
char* temp;
int i, j;
for (i = 0; i < size - 1; i++)
for (j = 0; j < size - i - 1; j++)
{
char** p1 = s + j;
char** p2 = s + j + 1;
if (strcmp(*p1, *p2) > 0)
{
temp = *p1;
*p1 = *p2;
*p2 = temp;
}
}
}
My version:
void strsort(char** s,int size)
{
char* temp;
int i, j;
for (i = 0; i < size - 1; i++)
for (j = 0; j < size - i - 1; j++)
if (strcmp(*(s+j),*(s+j+1))>0)
{
temp = *(s+j);
*(s+j) = *(s+j+1);
*(s+j+1) = temp;
}
}
值得像ai学习的风格:使用p1,p2变量使程序更加整洁美观
int main()
{
int i;
char* s[N], t[50];
for (i = 0; i < N; i++)
{
gets(t);
s[i] = (char*)malloc(strlen(t) + 1);
strcpy(s[i],t);
}
strsort(s,N);
for (i = 0; i < N; i++) { puts(s[i]); free(s[i]); }
return 0;
}
2)替换后的运行截图:

图6-3程序修改替换题(1)的运行截图
(2)下面源程序通过函数指针和菜单选择来调用库函数实现字符串操作;串复制strcpy、串连接strcat或串分解strtok。
①请在源程序中的下划线处填写合适的代码来完善该程序,使之能按照要求输出下面结果:
1 copy string.
2 connect string.
3 parse string.
4 exit.
input a number (1-4) please!
2↙ (键盘输入)
input the first string please!
the more you learn,↙ (键盘输入)
input the second string please!
the more you get. ↙ (键盘输入)
the result is the more you learn, the more you get.
# include<stdio.h>
# include<string.h>
int main (void)
{
____________________;
char a[80], b[80], *result;
int choice;
while(1)
{
do
{
printf("\t\t1 copy string.\n");
printf("\t\t2 connect string.\n");
printf("\t\t3 parse string.\n");
printf("\t\t4 exit.\n");
printf("\t\tinput a number (1-4) please.\n");
scanf("%d", &choice);
}while(choice<1 || choice>4);
switch(choice)
{
case 1: p = strcpy; break;
case 2: p = strcat; break;
case 3: p = strtok; break;
case 4: goto down;
}
getchar();
printf("input the first string please!\n");
______________________;
printf("input the second string please!\n");
______________________;
result = ___________(a, b);
printf("the result is %s\n", result);
}
down:
return 0;
}
②函数指针的一个用途是用户散转程序,即通过一个转移表(函数指针数组)来实现多分枝函数处理,从而省去了大量的if语句或者switch语句。转移表中存放了各个函数的入口地址(函数名),根据条件的设定来查表选择执行相应的函数。请使用转移表而不是switch语句重写以上程序。
解答:①
- 完善后的源程序清单:
#include<stdio.h>
#include<string.h>
int main(void)
{
char *(*p)(char *, const char *) = NULL;
char a[80], b[80], *result;
int choice;
while (1)
{
do {
printf("\t\t1 copy string.\n");
printf("\t\t2 connect string.\n");
printf("\t\t3 parse string.\n");
printf("\t\t4 exit.\n");
printf("\t\tinput a number (1-4) please.\n");
scanf("%d", &choice);
} while (choice < 1 || choice > 4);
switch(choice)
{
case 1: p = strcpy; break;
case 2: p = strcat; break;
case 3: p = strtok; break;
case 4: goto down;
}
getchar();
printf("input the first string please!\n");
gets_s(a, 80);
printf("input the second string please!\n");
gets_s(b, 80);
result = p(a, b);
printf("the result is %s\n", result);
}
down:
return 0;
}
2.完善后的运行截图:

图6-4程序完善题(2)的运行截图
②
- 修改替换后的源程序清单:
#include<stdio.h>
#include<string.h>
int main(void)
{
char *(*table[])(char *, const char *) = { strcpy, strcat, strtok };
char a[80], b[80], *result;
int choice;
while (1)
{
do {
printf("\t\t1 copy string.\n");
printf("\t\t2 connect string.\n");
printf("\t\t3 parse string.\n");
printf("\t\t4 exit.\n");
printf("\t\tinput a number (1-4) please.\n");
scanf("%d", &choice);
} while (choice < 1 || choice > 4);
if (choice == 4)
break;
getchar();
printf("input the first string please!\n");
gets_s(a, 80);
printf("input the second string please!\n");
gets_s(b, 80);
/* choice=1→index 0; choice=2→index1; choice=3→index2 */
result = table[choice - 1](a, b);
printf("the result is %s\n", result);
}
return 0;
}
My version:
# include<stdio.h>
# include<string.h>
int main(void)
{
char a[80], b[80], * result = NULL;
int choice;
while (1)
{
do
{
printf("\t\t1 copy string.\n");
printf("\t\t2 connect string.\n");
printf("\t\t3 parse string.\n");
printf("\t\t4 exit.\n");
printf("\t\tinput a number (1-4) please.\n");
scanf("%d", &choice);
} while (choice < 1 || choice>4);
if (choice == 4)
break;
char* (*p[4])(char* x, const char* y) = { strcpy,strcat,strtok };函数指针数组的初始化不熟
getchar();
printf("input the first string please!\n");
gets_s(a,80);
printf("input the second string please!\n");
gets_s(b,80);
result = p[choice - 1](a, b);
printf("the result is %s\n", result);
}
down:
return 0;
}
- 修改替换后的运行截图:

图6-5程序修改替换题(2)的运行截图
3、编程设计题
以下(1)至(4)题对应Educoder 教学平台“C语言实验”课程,实验6,第13关实验6-1、第14关实验6-2、第15关实验6-3, 以及第16关实验6-4。
(1)一个长整型变量占4个字节,其中每个字节又分成高4位和低4位。输入一个长整型变量,要求从高字节开始,依次取出每个字节的高4位和低4位并以十六进制数字字符的形式进行显示,通过指针取出每字节。
样例输入:15
样例输出:0000000F
解答:
- 解题思路:
long 型占 4 个字节,每个字节 8 位。
题目要求从高字节到低字节依次输出每个字节的 高 4 位 和 低 4 位,因此总输出 8 个十六进制字符。
将 long 型变量的地址强制转换为 unsigned char 类型。*
unsigned char 是 1 个字节,这样可以通过指针访问 long 的每一个字节。
由于计算机采用小端存储(低字节在低地址),为了从高字节开始输出,需要逆序取字节。
即按 *(p+3), *(p+2), *(p+1), *(p+0) 的顺序取出各字节。
对每个字节,利用位运算拆出高 4 位和低 4 位:
高 4 位:(byte >> 4) & 0x0F
低 4 位:byte & 0x0F
将这两个 4 位值按十六进制格式输出 (%X) 即可形成对应的十六进制字符。。
- 源程序清单:
#include <stdio.h>
int main()
{
long x;
scanf("%ld", &x);
unsigned char *p = (unsigned char *)&x;
unsigned char byte;
for (int i = 3; i >= 0; i--)
{
byte = *(p + i);
unsigned char high = (byte >> 4) & 0x0F;
unsigned char low = byte & 0x0F;
printf("%X%X", high, low);
}
return 0;
}
- 测试:
- 测试数据:输入15,应输出0000000F
- 对应测试数据的运行截图:

图6-6编程设计题(1)测试数据的运行截图
(2)旋转是图像处理的基本操作,编程实现一个将一个图像逆时针旋转90°。提示:计算机中的图像可以用一个矩阵来表示,旋转一个图像就是旋转对应的矩阵。将旋转矩阵的功能定义成函数,通过使用指向数组元素的指针作为参数使该函数能处理任意大小的矩阵。要求在main函数中输入图像矩阵的行数n和列数m,接下来的n行每行输入m个整数,表示输入的图像。输出原始矩阵逆时针旋转90°后的矩阵。
样例输入:
2 3
1 5 3
3 2 4
样例输出:
3 4
5 2
1 3
解答:
-
- 解题思路:
逆时针旋转 90° 后,得到的新矩阵大小为 m × n。
指针访问元素时遵循原矩阵下标:
新矩阵中的 (i, j) 对应
new[i][j] = old[j][m - 1 - i]
在 main 中分配数组并调用旋转函数,输出结果。
2)源程序清单:
#include <stdio.h>
#include <stdlib.h>
void rotate90cc(int n, int m, int *src, int *dst)
{
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
dst[i*n + j] = src[j*m + (m - 1 - i)];
}
int main()
{
int n, m;
scanf("%d %d", &n, &m);
int *src = (int *)malloc(n * m * sizeof(int));
int *dst = (int *)malloc(m * n * sizeof(int));
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
scanf("%d", &src[i*m + j]);
rotate90cc(n, m, src, dst);
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
printf("%d\t", dst[i*n + j]);
printf("\n");
}
free(src);
free(dst);
return 0;
}
My version:
#include <stdio.h>
#include <stdlib.h>
void rotate(int n, int m, int* src, int* dst)
{
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
dst[i * n + j] = src[j * m + m - 1 - i];
}
int main()
{
int n, m = 0;
scanf("%d%d", &n, &m);
int* src = (int*)malloc(n * m * sizeof(int));
int* dst = (int*)malloc(m * n * sizeof(int));
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
scanf("%d", &src[i * m + j]);
rotate(n, m, src, dst);
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
printf("%d", dst[i * n + j]);
printf("\n");
}
free(src);
free(dst);
return 0;
}
3)测试:
- 测试数据:
输入:2 3
1 5 3
3 2 4
应输出:3 4
5 2
1 3
- 对应测试数据的运行截图:

图6-7编程设计题(2)测试数据的运行截图
(3)输入n行文本,每行不超过80个字符,用字符指针数组指向键盘输入的n行文本,且n行文本的存储无冗余,删除每一行中的前置空格(' ')和水平制表符('\t')。要求:将删除一行文本中前置空格和水平制表符的功能定义成函数,在main函数中输出删除前置空格符的各行。
解答:
- 解题思路:
输入 n 行文本(每行不超过 80 字符)
用字符指针数组(char s[])指向每一行*
每行只分配刚好足够的存储空间(无冗余)
定义函数删除前置空格 ' ' 和水平制表符 '\t'
main 中输出处理后的 n 行
- 源程序清单:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void trim_leading(char* str)
{
int i = 0;
while (str[i] == ' ' || str[i] == '\t')
i++;
memmove(str, str + i, strlen(str + i) + 1);
}
int main()
{
int n;
scanf("%d", &n);
getchar();
char** s = (char**)malloc(n * sizeof(char*));
char buffer[81];
for (int i = 0; i < n; i++)
{
fgets(buffer, 81, stdin);
buffer[strcspn(buffer, "\n")] = '\0';
s[i] = (char*)malloc(strlen(buffer) + 1);
strcpy(s[i], buffer);
trim_leading(s[i]);
}
for (int i = 0; i < n; i++)
{
puts(s[i]);
free(s[i]);
}
free(s);
return 0;
}
My version:
#include <stdio.h>//scanf,printf,gets,puts
#include <stdlib.h>//malloc,free
#include <string.h>//strcpy,strcspn(目标地址,源地址,移动范围)
void delete(char* p)
{
int i = 0;
while (p[i] == ' '|| p[i] == '\t')
i++;
memmove(p, p + i, strlen(p + i) + 1);
}
int main()
{
int n = 0;
scanf("%d", &n);
getchar();
char** s = (char**)malloc(n * sizeof(char*));//用二级指针模拟字符指针数组,利用字符指针数组名为数组首元素地址类型也为二级指针的特性
char buffer[81] = { 0 };//静态缓冲区
for (int i = 0; i < n; i++)
{
fgets(buffer, 81, stdin);
buffer[strcspn(buffer, "\n")] = '\0';
s[i] = (char*)malloc((strlen(buffer) + 1) * sizeof(char));//最后一定要*sizeof(类型)以便确定开辟空间的大小以及单次访问的权限
strcpy(s[i], buffer);
delete(s[i]);
}
for (int i = 0; i < n; i++)
{
puts(s[i]);
free(s[i]);
}
free(s);
return 0;
}
- 测试:
- 测试数据:
输入:2
Hello world
C language
应输出:
Hello world
C language
- 对应测试数据的运行截图:

图6-8编程设计题(3)的运行截图
(4)编写8个任务函数,一个scheduler调度函数和一个execute执行函数。仅在main函数中调用scheduler函数,scheduler函数要求用最快的方式调度执行用户指定的任务函数。
①先设计task0, task1, task2, task3, task4, task5, task6, task7共8个任务函数,每个任务函数的任务就是输出该任务被调用的字符串。例如,第0个任务函数输出“task0 is called!”,第1个任务函数输出“task1 is called!”,以此类推。
②scheduler函数根据键盘输入的数字字符的先后顺序,一次调度选择对应的任务函数。例如,输入:1350并回车,则scheduler函数一次调度选择task1, task3, taks5, task0,然后以函数指针数组和任务个数为参数将调度选择结果传递给execute函数并调用execute函数。
③execute函数根据scheduler函数传递的指针数组和任务个数为参数,按照指定的先后顺序依此调用执行选定的任务函数。
例如,当输入13607122并回车,程序运行结果如下:
task1 is called!
task3 is called!
task6 is called!
task0 is called!
task7 is called!
task1 is called!
task2 is called!
task2 is called!
解答:
- 解题思路:
任务函数 (task0-task7): 这 8 个函数功能单一,只需打印各自的调用信息。我们可以将它们的函数指针存入一个数组,方便通过索引快速查找和调用。
调度函数 (scheduler): 此函数是核心,负责接收用户输入并规划执行顺序。
输入处理: 使用 fgets 读取整行输入,这比 scanf 更安全,可以防止缓冲区溢出,并且能方便地处理包含多个数字的字符串。
解析与验证: 遍历输入的字符串,逐个检查字符。如果字符是数字('0'-'7'),则将其转换为对应的整数索引,并存储起来。忽略非数字字符,或者可以添加错误提示。
调用执行函数: 将解析出的任务索引数组和任务总数传递给 execute 函数。
执行函数 (execute): 此函数负责接收调度计划并执行。
它接收两个参数:一个指向任务索引数组的指针,和一个表示任务数量的整数。
函数内部遍历这个索引数组,对于每个索引,从全局的任务函数指针数组中找到对应的函数并调用它。
主函数 (main): 程序的入口点,只需要调用 scheduler 函数即可启动整个流程。
- 源程序清单:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef void (*TaskFunction)(void);
void task0() { printf("task0 is called!\n"); }
void task1() { printf("task1 is called!\n"); }
void task2() { printf("task2 is called!\n"); }void task3() { printf("task3 is called!\n"); }
void task4() { printf("task4 is called!\n"); }
void task5() { printf("task5 is called!\n"); }
void task6() { printf("task6 is called!\n"); }
void task7() { printf("task7 is called!\n"); }
TaskFunction task_table[8] = { task0, task1, task2, task3,
task4, task5, task6, task7 };
void execute(int* task_indices, int count) {
if (count <= 0) {
printf("No tasks to execute.\n");
return; }
printf("\n--- Starting Execution ---\n");
for (int i = 0; i < count; i++) {
int index = task_indices[i];
if (index >= 0 && index < 8) {
task_table[index]();
}
else {
printf("Invalid task index: %d, skipped.\n", index);
}
}
printf("--- Execution Completed ---\n");
}
void scheduler() {
char input[1024];
int task_indices[1024];
int task_count = 0;
printf("Enter task sequence (digits 0-7, separated by any non-digit characters): ");
if (fgets(input, sizeof(input), stdin) == NULL) {
perror("Error reading input");
return;
}
input[strcspn(input, "\n")] = '\0';
printf("You entered: \"%s\"\n", input);
for (int i = 0; input[i] != '\0'; i++) {
if (isdigit((unsigned char)input[i])) {
int task_num = input[i] - '0';
if (task_num >= 0 && task_num <= 7) {
task_indices[task_count++] = task_num;
}
else {
printf("Warning: Ignoring invalid task number '%c'.\n", input[i]);
}
}
}
execute(task_indices, task_count);
}
int main() {
scheduler();
return 0;
}
Myversion:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
void task0()
{
printf("task0 is called!\n");
}
void task1()
{
printf("task1 is called!\n");
}
void task2()
{
printf("task2 is called!\n");
}
void task3()
{
printf("task3 is called!\n");
}
void task4()
{
printf("task4 is called!\n");
}
void task5()
{
printf("task5 is called!\n");
}
void task6()
{
printf("task6 is called!\n");
}
void task7()
{
printf("task7 is called!\n");
}
typedef void(*taskfunc)(void);
void execute(taskfunc task_list[], int task_count)
{
for (int i = 0; i < task_count; i++)
{
task_list[i]();
}
}
void scheduler()
{
taskfunc task_list[1024] = { 0 };
taskfunc task_f[8] = { task0,task1,task2,task3,task4,task5,task6,task7 };
int task_count = 0;
char input[1024] = { 0 };
fgets(input, 1024, stdin);
for (int i = 0; input[i] != '\0'; i++)
{
if (isdigit(input[i]) && input[i] >= '0' && input[i] <= '7')
task_list[task_count++] = task_f[input[i] - '0'];
}
if (task_count != 0)
execute(task_list,task_count);
}
int main()
{
scheduler();
return 0;
}
- 测试:
- 测试数据:输入13607122,应输出
task1 is called!
task3 is called!
task6 is called!
task0 is called!
task7 is called!
task1 is called!
task2 is called!
task2 is called!
- 对应测试数据的运行截图:

图6-9编程设计题(4)的运行截图
6.3、实验小结
通过这次实验体会到指针必须初始化,要么赋为空指针,要么要创建一个对应类型的变量,否则就会造成野指针越界访问。认识到数组名代表的是数组首元素的地址,函数参数既可以使用形参数组不定长也可以使用指针接收实参部分的数组传参。了解到只需要一个指向起始字符的指针就可以打印一整个字符串。学习到指针函数(返回值类型为指针,形如“*p()”)和函数指针(指向函数的指针,可以间接访问函数,形如“(*p)()”)的区别,掌握了带有参数的main的用法。
另:
一.针对源程序改错题中的这段代码:
char* strcopy(char* t, const char* s)
{
while (*t++ = *s++);
return (t);
}
有两点想要讨论的:
- 如果只是为了达到复制的效果,没有必要返回char*类型的值,完全可以将返回值类型设为void
- 如果想实现链式调用,这样的代码也是有错误的,因为返回的t值已经加了n次,指向的是字符串\0的下一位,所以无法实现链式调用(形如:
Strcopy(str1,strcopy(str2,strcopy(str3,”hello”)))
如果将函数实现改成:
Char*strcopy(char*t,const char*s)
{
Char* temp_p = t//记录t初始指向的地址
While(*t++ = *s++);
Return temp_p;
}
最内层调用的函数将hello赋值给str3,然后返回值是str3的首地址,从而让这个过程相似的重复下去,实现链式调用
二.动态内存:
1.s[i] = (char*)malloc(strlen(t) + 1);
实现动态内存分配,strlen测量输入的字符串t的长度,+1是为了留一个空间放\0,malloc函数(需要声明stdlib.h库函数)会在堆区开辟对应大小的内存空间,并返回一个类型为void*的指针,而欲将该值赋给类型为char*的s[i],必须强制类型转换为char*使其兼容
2.Strcpy(s[i],t)
首先要介绍字符数组t的学名叫静态缓冲区或者临时缓冲区,gets输入字符串必须先将字符串存入t中,再将t当作一个跳板,将输入的值复制到动态开辟的内存空间中
- free(s[i])\
动态内存释放,将开辟的动态内存还给系统(更准确是堆区),避免内存泄漏(如若一直不归还,则开辟的空间会一直被占用,直至内存不足)
三.Getchar与gets_s,scanf
先解释 getchar() 的作用:清空输入缓冲区的残留换行符
代码中 getchar() 写在 switch 之后、gets_s 之前,作用是 吃掉 scanf("%d", &choice) 执行后,输入缓冲区里残留的换行符 \n,避免后续 gets_s 读取到空字符串。
详细过程(用例子说话):
假设你运行程序后,输入 1 选择 “复制字符串”,操作流程是这样的:
你在键盘输入 1 后,会按 回车 → 输入缓冲区里实际存的是 '1' + '\n'(回车就是换行符 \n);
scanf("%d", &choice) 会读取缓冲区里的数字 '1',转换为整数 1 存入 choice—— 但它 只会读取数字,不会处理后面的 '\n';
此时缓冲区里还剩一个 '\n';
如果没有 getchar():接下来执行 gets_s(a, 80) 时,gets_s 会从缓冲区读取内容,一遇到 '\n' 就停止(认为你输入了空字符串),导致你还没来得及输入第一个字符串,a 就被读成空了;
有了 getchar():它会读取缓冲区里残留的那个 '\n'(并丢弃,不存任何变量),此时缓冲区为空,后续 gets_s 才能正常等待你输入字符串。
总结 getchar() 的核心作用:
解决 scanf(读取整数 / 浮点数时)和 gets_s(读取整行字符串时)的 输入缓冲区残留冲突,确保 gets_s 能正常接收用户输入

四.函数指针数组的初始化:
是数组不是指针所以不能将数组名和*括在一起
应写成*p[]
P为数组名,紧挨的[]表明其数组身份,而数组每个元素的类型是指针,指向的是返回值为char*参数为char*,const char*的函数,
所以要写成char*(*p[])(char*,const char*) = {…}
五.scanf记得%,%,%!!!通过指针取出每字节,使用的指针必须是char*,将取得的地址强制类型转换为char*
Unsigned char byte = 0;
Unsigned char high = (byte>>4)&0x0F;
Unsigned char low=byte&0x0F;
Printf(“%X%X”,high,low)
六.用动态内存创建数组
S1:创建行列数:int row,col
Scanf(“%d%d”,&n,&m);
S2:动态创建一维数组(指针形式)
Int* src = (int*)malloc(n*m*sizeof(int));
Int* dst = (int*)malloc(m*n*sizeof(int));
即创建一个指针指向该数组的起始地址,并让该指针的可访问范围为n*m*sizeof(int)
S3:输入数组值(用一维数组+for循环+下标表达式模拟二维数组)
For(int I = 0;I < n;i++)
For(int j = 0;j < m;j++)
Scanf(“%d”,&src[I * m + j])
七.如何删除字符串前置空格和制表符?
利用while循环跳过空格和制表符,找到第一个有意义字符的地址,利用memmove字符串的移动覆盖实现删除
Void delete(char*p)
{
Int I = 0;
While(p[i] == ‘ ‘ || p[i] = ‘\t’)
I++;
Memmove(p,p + I,strlen(p + i)+1);
P是想移动到的目标地址,p + i是有效地址,strlen是移动操作对象的范围,+1是为了涵盖\0
}
八. fgets(buffer, 81, stdin)81 = 80 + 1,最后留一个给\n
buffer[strcspn(buffer, "\n")] = '\0'strcspn是用来找指定字符串第一次出现的地址的,即使是单个字符也要用与字符串相同规格的“”来引用,而memmove才是用来覆盖删除的
九.初始化函数指针数组可以通过typedef简化:
typedef void(*taskfunc)(void)
如果函数指针的类型是:返回值类型(*)(参数类型)
即可按照返回值类型(*傀儡名)(参数类型)typedef
下次再定义这样的函数指针变量时就可以简化为:傀儡名 变量名的形式
例如此时我要创建名字为task_f和task_list的字符指针数组,就可以写成:
Taskfunc task_f[8] = {…}
Taskfunc task_list[1024] = {0}
十.访问数组元素时一定要时刻提醒自己元素类型,如:
task_list[i]()task_list中的元素类型是函数指针,所以task_list相当于一个函数名后面要用括号括住参数,即使函数参数为void,也要有括号,如果删掉,程序虽然不会报错,但功能完全丧失。如图:

有括号时的运行截图

无参数时的运行截图
再如:
for (int i = 0; input[i] != '\0'; i++)
{
if (isdigit(input[i]) && input[i] >= '0' && input[i] <= '7')input[i]是字符,一定要用’’引用字符数字
task_list[task_count++] = task_f[input[i] - '0'];下标是整型数字,一定要-‘0’还原成整型数字
s[i] = (char*)malloc((strlen(buffer) + 1) * sizeof(char));//最后一定要*sizeof(类型)以便确定开辟空间的大小以及单次访问的权限,‘1’是给\0的
strcpy(s[i], buffer);动态开辟后别忘了把静态缓冲区的内容复制保存
最后杂:1.Fgets使用前要清空空格和换行符,用getchar实现,只是getchar常和scanf连用紧跟scanf而不是跟着fgets写在for循环中,这样的话除了第一次后面的输入第一字符都会被吞
2.Malloc后面表示开拓大小的参数中一定要含sizeof
3.malloc后一定要free
结语:此篇报告耗时五天,标志着笔者学习编程两个月以来第一次完全弄懂一个实验每个过程,之前的报告基本上都靠ai蒙混过关,平日里也就三天打鱼两天晒网地学一些基础理论,临近期末(还有一月),感到自己应该做出一些改变,从这周开始,将持续更新每次实验,并抽空补上前五次实验。话说听古风dj写代码感觉很上头,或许是个不错的开始
更多推荐



所有评论(0)