编程各种bug的经验教训和总结(持续更新)
-1.愚蠢的行为
0.当你没有输出时,崩溃时,不妨检查下你的scanf有没有加取地址符&
1.for 循环中定义i和j弄混,导致runtime error
例
1 |
|
2.函数声明变量是中间用逗号, for循环中用分号;
3. 数组类型定义不明
char 与int 混淆,而且还能跑,又找不到问题!!
1 | int main() { |
4.函数定义
为什么定义了个函数在matrix一大串画线报错呢
哦原来是你个大聪明没定义函数类型
5.代码有问题不要再复用
不要光复用代码,要改就删掉!!重写不用多久
debug更浪费时间
6.char指针指针和地址要分清
指针if(*s1==*s2)
地址自增++s1
s1++
地址加1(不会改变源地址的值)s1+1
递归应该使用这个而非自增,会改变全局的值
7.单引号和双引号括起来是有区别的,一个是字符,另一个是字符串
在单个使用时,比如比较,我们要用单引号,如 '\n' '\t' 'z'
用双引号实际是会报错的
8.指针最好不要乱动,尤其是需要返回的指针,找个替身好吗?
0.审题
不要有坏习惯!
做一切题目先审好题
不要按感觉来
看好样例
把样例理解并考虑周全
(好言难劝该死的鬼)
1.for循环中
for(int i=0;i<k;i++)
for(int i=k;i>0;i–)
注意后者是 i– 否则会越界
2.输入
1.scanf
注意加&,除了%s
只有string%s在前面不需要加&,别的都需要!
- 可以读取整行中的空格
%[^\n]
%
前面加空格可以读取上一行的空格,也可在在结束丢弃缓冲区的换行字符"%[^\n]%*c"
,其中%*c
是读取并丢弃一个字符。
注意数据类型!!###
%lld %d %f %lf
3.分号与逗号
for 循环中用分号
其他一般用逗号
结尾记得带分号
4.数组移动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
while (*x != 1) {
arr[*i] = *x; // Store current value of x in arr
(*i)++; // Increment index
if (*x % 2 == 1) {
*x = *x * 3 + 1;
}
else {
*x /= 2;
}
printf("%d\n", *x); // Print the next value in the sequence
}
// Store the last value (1) in the sequence
arr[*i] = *x;
(*i)++;
代码主要看后面
结尾记得把末尾的也储存进去
用while或者do while 一定要记得头和尾要不要
5.输出
输出数据类型
%lld %d %f %lf
还有保留几位小数 %.2f
当然还有几进制
%o
:八进制表示。%x
:十六进制小写表示。%X
:十六进制大写表示。#
修饰符可以用于显示八进制0
前缀和十六进制0x
或0X
前缀。
6.数组定义
C语言C99前不支持定义可变数组
我们可动态分配以方便在落后的ide上调试
1 | int *a=(int *)malloc(n*sizeof(int));//malloc不初始化数组 |
7.执行后的函数
不少函数尤其是以指针为传入函数的,执行函数后会对值直接进行修改并且也返回原来的值。
例如 strlwr(*p);事实上执行后就会对原来的值发生修改,返回的值应该都是小写
1 | strcpy(str1,"saBaaG"); strcpy(str2,strlwr(str1)); printf("%s\n",str1); printf("%s\n",str2); |
8.结构体
1.结构体指向写法
两种 . 或者-> 其实->是语法糖
是结构体就用点,是指针的话可以用语法糖(这时不用解引用)
(*pointer).member
的写法
当你有一个指针,比如book1
,如果你想访问它所指向的结构体的成员Price
,标准写法是:
(*book1).Price
->
语法糖
为了简化写法,C 提供了->
运算符,用于指针访问结构体成员:
book1->Price // 等价于 (*book1).Price
2.结构体的排序
我们使用 <stdlib.h>
中的qsort函数
先写compare函数,需要注意结构体定义应该在更前面,放在全局而非主函数中
将传入的 void指针转化为结构体,弄1,2两个值出来
1 | int compare(const void* a, const void* b) { |
从小到大就正常,从大到小就反过来。
当然简化版更爽(但相减有个问题就是可能会数值溢出)
1 | int compare(const void* a, const void* b) { |
3.语法细节
结构体后面要加分号
1 | struct Person{ |
4.输入
scanf传入的应该是地址
%s是可以的,传入的是数组地址
1 | scanf("%d", &pup[0].age); |
5.概念
1 | typedef struct ST{ |
struct ST
=NEW
结构体类型ST
结构体的标签new
结构体标签
9.多文件
1..h
和.c
都要根据main来写
- 数据体需要看main中的输入类型,别人是float你就不能写double
- 看好main函数中传递的值,人家是整个结构体还是指针,这会影响你写的函数的类型
- 头文件会需要定义一些东西
结构体重命名typedef struct Student Student;
一些在main中出现但是未定义的宏SIZE MAX_XX
2..h
和.c
之间的关系
.c
需要包含头文件#include<student.h>
- 头文件中需要定义函数,然后
.c
再描述函数
10.qsort函数
排序的大招()
函数原型
1 | void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); |
int(*fun)(参数)
是函数的指针,返回整型值
使用:
1.我们先写好compare函数,需要注意类型转化
示例:
- 整数
return (*(int*)a - *(int*)b); // 从小到大排序
- 结构体
BOOK* book1 1
2struct BOOK* book2 = (struct BOOK*)b;
return book1->Price - book2->Price;
2.对于num
size
,我们注意类型是size_t
万能的:
1 | sizeof(array) / sizeof(array[0]) |
很多时候num是已经定下的,我们会用显示转换
1 | qsort(arr, (size_t)n, sizeof(int), compare); |
11.链表
1.删除链表不能乱删除
先跳线,在炸掉桥,再把现在的由之前的来定义
1 | void deleteDuplicates(struct Node* head){ |
12.常用的库
1. <stdio.h>
(标准输入输出库)
用于处理输入输出操作。
常用函数:
printf()
: 格式化输出到标准输出(屏幕)。用途:打印各种格式的数据,如整数、浮点数、字符串等。
示例:
1
printf("Hello, World! %d\n", 10);
scanf()
: 从标准输入(键盘)读取数据。用途:读取用户输入的数据,可以按照指定格式读取。
示例:
1
2int a;
scanf("%d", &a);
getchar()
: 从标准输入读取一个字符。用途:逐个字符地读取输入。
示例:
1
char c = getchar();
putchar()
: 输出一个字符到标准输出。用途:打印单个字符。
示例:
1
putchar('A');
fopen()
,fclose()
: 打开/关闭文件。用途:文件的读取、写入操作。
示例:
1
2FILE *file = fopen("file.txt", "r");
fclose(file);
fscanf()
,fprintf()
: 从文件读取数据,向文件写入数据。用途:文件输入输出。
示例:
1
2fscanf(file, "%d", &a);
fprintf(file, "%d", a);
2. <stdlib.h>
(标准库)
包含动态内存管理、程序控制、转换等功能。
常用函数:
malloc()
: 动态分配内存。用途:在堆上分配一块指定大小的内存。
示例:
1
int *arr = (int*)malloc(10 * sizeof(int));
free()
: 释放动态分配的内存。用途:释放之前通过
malloc
、calloc
或realloc
分配的内存。示例:
1
free(arr);
calloc()
: 分配并初始化内存。用途:分配内存并将其初始化为零。
示例:
1
int *arr = (int*)calloc(10, sizeof(int));
realloc()
: 重新分配内存。用途:调整已分配内存的大小。
示例:
1
arr = (int*)realloc(arr, 20 * sizeof(int));
exit()
: 退出程序。用途:终止程序,并可返回指定的退出状态。
示例:
1
exit(0);
rand()
: 生成随机数。用途:生成随机数,通常需要
srand()
初始化随机数种子。示例:
1
int r = rand();
atoi()
,atof()
,atol()
: 字符串转换为整数、浮点数、长整型等。用途:将字符串转换为整数、浮点数或长整数。
示例:
1
int num = atoi("123");
3. <string.h>
(字符串处理库)
提供字符串操作相关的函数。
常用函数:
strlen()
: 计算字符串长度(不包括结尾的'\0'
)。用途:获取字符串的长度。
示例:
1
int len = strlen("Hello");
strcpy()
: 复制字符串。用途:将一个字符串复制到另一个字符串。
示例:
1
2char dest[20];
strcpy(dest, "Hello");
strncpy()
: 复制指定长度的字符串。用途:安全地复制指定长度的字符串。
示例:
1
strncpy(dest, "Hello", 5);
strcmp()
: 比较两个字符串。用途:比较两个字符串的字典顺序。
示例:
1
int result = strcmp("apple", "banana");
strcat()
: 拼接字符串。用途:将一个字符串追加到另一个字符串末尾。
示例:
1
2char str1[20] = "Hello, ";
strcat(str1, "World!");
strchr()
: 查找字符。用途:在字符串中查找第一个指定字符。
示例:
1
char *ptr = strchr("Hello", 'e');
strstr()
: 查找子字符串。用途:在字符串中查找指定的子字符串。
示例:
1
char *ptr = strstr("Hello, World!", "World");
4. <math.h>
(数学库)
提供数学计算相关的函数。
常用函数:
sin()
,cos()
,tan()
: 三角函数。用途:计算正弦、余弦和正切值。
示例:
1
double result = sin(3.14159); // 计算π的正弦值
sqrt()
: 计算平方根。用途:计算一个数的平方根。
示例:
1
double result = sqrt(25); // 计算25的平方根,结果为5
pow()
: 计算幂。用途:计算一个数的指定幂。
示例:
1
double result = pow(2, 3); // 计算2的3次方,结果为8
log()
,log10()
: 对数函数。用途:计算自然对数和以 10 为底的对数。
示例:
1
double result = log(100); // 计算100的自然对数
abs()
: 计算整数的绝对值。用途:返回整数的绝对值。
示例:
1
int result = abs(-10); // 计算-10的绝对值,结果为10
ceil()
,floor()
: 取整函数。用途:
ceil()
返回大于或等于参数的最小整数,floor()
返回小于或等于参数的最大整数。示例:
1
2double result1 = ceil(3.14); // 结果为4
double result2 = floor(3.14); // 结果为3
fmod()
: 计算浮点数的余数。用途:返回两个浮点数相除的余数。
示例:
1
double result = fmod(5.5, 2); // 结果为1.5
exp()
: 计算自然常数e的幂。用途:计算e的x次方。
示例:
1
double result = exp(1); // 计算e的1次方,结果为2.71828
fabs()
: 计算浮点数的绝对值。用途:返回浮点数的绝对值。
示例:
1
double result = fabs(-3.14); // 结果为3.14
5. <time.h>
(时间日期库)
处理日期和时间。
常用函数:
time()
: 获取当前时间。用途:获取从1970年1月1日(Unix纪元)以来的秒数。
示例:
1
time_t t = time(NULL);
localtime()
: 将时间转换为本地时间。用途:将
time_t
类型的时间转换为本地时间的tm
结构。示例:
1
struct tm *tm_info = localtime(&t);
strftime()
: 格式化时间。用途:将
tm
结构的时间格式化为字符串。示例:
1
2char buffer[80];
strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", tm_info);
6. <ctype.h>
(字符处理库)
提供字符分类和转换的函数。
常用函数:
isdigit()
: 判断字符是否为数字。用途:判断字符是否是数字(0-9)。
示例:
1
if (isdigit('9')) { printf("It's a digit!"); }
isalpha()
: 判断字符是否为字母。用途:判断字符是否是字母。
示例:
1
if (isalpha('A')) { printf("It's a letter!"); }
toupper()
,tolower()
: 字符大小写转换。用途:将字符转换为大写或小写。
示例:
1
2char c = 'a';
char upper = toupper(c);
鸣谢:GPT4o