perror函数是一个类Unix操作系统的C语言标准库函数,用于输出错误信息。当程序产生某个系统调用错误时,perror会自动将系统错误信息转换成可读格式并输出到stderr(标准错误)设备上,通常和errno全局变量一起使用。
一、perror return 1
perror return 1是一个常见的错误信息输出格式,在该错误格式下,程序返回值通常设为1,表示程序的主要逻辑发生错误。通常的错误信息形如“程序名:错误信息:errno!”
int main(int argc, char *argv[]) {
FILE *fp;
if ((fp = fopen(argv[1], "r")) == NULL) {
perror("fopen");
return 1;
}
// ...
fclose(fp);
return 0;
}
在以上代码中,程序试图打开一个文件,如果打开文件失败,就会调用perror函数输出错误信息,并返回1。其中'fopen'就是错误信息的前缀,代表文件打开失败,errno则是系统错误码。
二、perror原理分析
在了解perror函数具体实现细节之前,需要先了解几个概念:
- errno:errno是C/C++语言中的一个全局变量,负责存储当前程序的错误码。在程序调用Linux系统函数时,例如open、read、write等,系统会在发生错误时将错误码存储在该变量中。
- strerror:strerror函数将错误码转换为一个字符串,该字符串是人类可读的错误信息,比如“文件不存在”、“内存不足”等。 perror函数的原理非常简单,它实际上就是将errno作为strerror的参数,将错误信息转换成一个人类可读的字符串,然后将其输出到stderr设备上:
void perror(const char *s) {
// 将errno转换为人类可读的字符串
char *msg = strerror(errno);
// 将错误信息输出到stderr设备上
fprintf(stderr, "%s: %s\n", s, msg);
}
三、perror和strerror的实战应用
perror和strerror函数是程序员调试程序时经常使用的工具。在接收到其他程序员提交的crash dump时,通常可以根据错误信息分析出程序的错误原因。 在下面的代码示例中,我们假设需要实现一个HTTP服务器,当服务器接收到客户端发来的一份HTTP请求时,根据请求的URL返回文件内容。在该例子中,我们通过perror和strerror函数处理打开文件错误时的错误信息:
int main(int argc, char *argv[]) {
int sockfd, newsockfd, portno;
struct sockaddr_in serv_addr, cli_addr;
socklen_t clilen;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
exit(1);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = 8888;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR on binding");
exit(1);
}
listen(sockfd, 5);
clilen = sizeof(cli_addr);
while (1) {
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0) {
perror("ERROR on accept");
exit(1);
}
// get client request
char buffer[4096];
bzero(buffer, sizeof(buffer));
int n = read(newsockfd, buffer, sizeof(buffer) - 1);
if (n < 0) {
perror("ERROR reading from socket");
exit(1);
}
// parse client request
// ...
// open file
char *filename = ...;
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
perror("ERROR opening file");
exit(1);
}
// read file content
char filebuf[4096];
bzero(filebuf, sizeof(filebuf));
fread(filebuf, sizeof(filebuf) - 1, 1, fp);
// send file content to client
n = write(newsockfd, filebuf, strlen(filebuf));
if (n < 0) {
perror("ERROR writing to socket");
exit(1);
}
fclose(fp);
close(newsockfd);
}
close(sockfd);
return 0;
}
以上示例中,当程序在打开文件时返回NULL时,perror函数将输出“ERROR opening file: 文件不存在或者权限不足!”的错误信息,并且程序将退出。
四、perror和strerror的另一种使用方式
需要注意的是,perror函数一般都是在函数返回-1时才会被调用。如果errno已经被另外的函数清空了,那么perror的输出结果将是一个错误信息,但是这个错误信息实际上根本跟程序运行无关。 如果我们想调用perror函数以输出一个手动设置的错误信息,那么可以通过在调用函数返回前设置errno的办法完成。
int my_read(int fd, char *buf, size_t len) {
int n = read(fd, buf, len);
if (n == -1) {
errno = 42; // 设置errno为任意非0数值
}
return n;
}
int main(int argc, char *argv[]) {
char buf[4096];
int n = my_read(STDIN_FILENO, buf, sizeof(buf) - 1);
if (n == -1) {
perror("ERROR in my_read");
}
return 0;
}
在以上代码示例中,my_read函数会返回-1,表示读取失败。在程序调用perror函数输出错误信息时,会输出“ERROR in my_read: Input/output error!”。
五、总结
C标准库中的perror函数是一个非常实用的函数。通过分析perror函数的原理,可以更好地理解程序在出错时的内部逻辑。在实际开发中,程序员可以通过调用perror函数和strerror函数输出错误信息,方便快速查找程序的错误。