程序员的错误处理
要把正确的搞定,也要把错误的搞定,还要防止别人有意无意的破坏;
主流的编程语言(除了C语言外)基本都使用异常机制处理错误,C语言中没有异常机制;
C程序员是用返回值代表是否出错,主要有以下4种情况:
1.如果返回类型是 int(如主函数/随机数等),并且返回值不可能是负数,正常数据会直接返回,用返回-1代表出错(如返回数组下标>=0);
2.如果返回类型是 int,并且返回值有可能是负数(如返回两个负数中的大者),此时正常数据用指针返回,返回-1代表出错,0代表正确;
3.如果返回类型是指针,返回 NULL 代表出错,其他代表正常;
4.如果函数不需要考虑出错问题,用 void 型函数,无返回值;
注意
以上4种情况只是经验之谈,大多如此;但不是绝对的;
如有时返回指针不用NULL而用-1表示出错((void*)-1))也是可以的;
一个函数影响外部数据的方式有2种:
1.用 return 语句返回;
2.参数中传递指针形式,也可以影响外部数据;
练习
有4个函数
a返回两个数的最大值,如果这两个数相等,返回错误;
/* 数据用指针传递,情况2 */
b返回0-10的随机数,如果随机数为0则函数返回错误;
/* 可以用-1表示出错,情况1 */
c传入一个字符串,如果传入的是"error",返回错误;否则返回原来函数的字符串;
/* 返回指针,情况3; */
d打印一个传入的字符串;
/* 不需要返回值,也无须考虑错误,情况4; */
/*
* 错误处理演示
*/
#include
#include
#include
#include
/* 情况1,正常值非负,用-1表示出错 */
int ran10() {
srand(time(0)); //设置种子防止伪随机
int ra = rand() % 11;
if (ra == 0) {
return -1;
}
return ra;
//返回-1代表出错,数据正常返回;
}
/* 情况2,正常值也有负数,返回0表示正常,数据用指针处理传递 */
int max(int num1, int num2, int *pi) {
if (num1 == num2) {
return -1; //相等则返回
}
*pi = (num1 > num2) ? num1 : num2;
return 0;
//0代表没有出错,数据存在*pi中
}
/* 情况3,返回值是指针类型,返回NULL代表出错,其他值直接带回 */
char *iserror(char *str) {
if (!strcmp(str, "error")) {
return NULL; //返回NULL代表错误
}
return str;
//返回指针
}
/* 情况4,不需要考虑出错问题,用void型函数; */
void print(char *str) {
printf("%s\n", str);
}
int main() {
int num = 0;
int r = max(-1, -2, &num);
if (r == -1) {
printf("error !\n");
} else {
printf("r = %d, max = %d\n", r, num);
}
r = ran10();
if (r == -1) {
printf("ran error \n");
} else {
printf("r = %d\n", ran10());
}
char *s = iserror("ok");
if (s == NULL) {
printf("iserror error !\n");
} else {
printf("%s\n", s);
}
print(s);
return 0;
}
C语言用外部的全局变量errno(error number)记录错误信息;
错误信息包括错误编号(int)和具体信息(字符串);
每个错误编号都对应一个具体信息;
errno存储的是错误编号;
函数strerror()/perror()/printf()用于错误信息的显示和转换;
char *strerror(int errnum) 传入一个编号,返回具体信息(转换函数);
void perror(const char *s) 不用传入错误编号,直接打印errno对应的信息(会自动换行);
printf("%m") 直接打印errno对应的信息;
不是所有的函数都使用errno处理错误,比如线程的函数;
注意安装翻译软件星际译王或goldendict
man命令可以查看命令/函数和头文件等的帮助信息;
如果man命令显示的不是英文,可以首先执行,export LANG="en";
在Linux中获取指令的帮助信息
man string.h
使用空格或Page Down翻页,也可使用上下键滚屏
在结果中可以使用搜索,n下一个,N上一个
q退出
man 3 printf //查看C库函数
3表示的是man查找帮助的来源,可分为以下8种;
首先要查看概述,尤其注意其中的头文件和函数原型,参数类型,返回值类型;
然后查看描述信息,可以简要了解函数的功能;
最后还要看函数的返回值的意义;
man find
查找系统命令的帮助信息;
首先看name,了解命令的基本功能;
然后看概述,了解命令的使用形式;
然后再看描述,了解其具体功能;
然后还要看选项信息,了解各选项的意义;
如果可能还可以看一些使用范例;
不需要记住帮助信息,只需要明白使用的有价值的信息即可,用的多的自然就会记住常用的;
man man
The standard sections of the manual include
1 User Commands //用户命令
2 System Calls //系统命令
3 C Library Functions //C库函数
4 Devices and Special Files //设备和特设文件
5 File Formats and Conventions //文件格式与转换
6 Games et. Al. //游戏等
7 Miscellanea //杂集
8 System Administration tools and Deamons //系统管理工具
man strerror
errno只有在出错时才会改变值,不出错也不会恢复值;
因此errno只能说明出过什么错,而不能据此判断当前步骤是否出错;
若判断是否出错要使用函数的返回值;
确定出错以后再使用errno判断出了什么错
/*
* perror()使用示例
*/
#include
#include
#include
int main() {
FILE *file = fopen("/etc/passwd1", "r");
if (file == NULL) { //判断是否出错
printf("[%d] %s\n", errno, strerror(errno));
perror("fopen 出错了");
//perror()会打印"参数: 错误信息"自动换行
//其中参数就是传给perror()的参数;
//函数出错基本都是使用perror(),线程一般不用perror()
printf("%m\n");
} else {
fclose(file);
}
file = NULL; //如果确保file不再使用可以省略;
return 0;
}