UNIX再学习 -- 错误和警告

错误和警告是常会出现的现象,了解它对以后解决问题会很有帮助。下面我们就重点来详细介绍它们。

一、错误

1、回忆错误

我们之前讲解其他内容时有涉及到错误的部分,下面让我们来回忆一下:

(1)参看:C语言再学习 -- C 预处理器

#error  字符串 => 表示产生一个错误信息
#warning 字符串 => 表示产生一个警告信息

//#error和#warning的使用  
#include <stdio.h>  
  
#define VERSION 4  
#define VERSION 2  
#define VERSION 3  
#if(VERSION < 3)  
    #error "版本过低"  
#elif(VERSION > 3)  
    #warning "版本过高"  
#endif  
  
int main(void)  
{  
    printf("程序正常运行\n");  
    return 0;  
}  
输出结果:  
警告: #warning "版本过高"  
//错误: #error "版本过低"  
//程序正常运行  

(2)参看:C语言再学习 -- 关键字return和exit ()函数

C语言中通过使用返回来表示是否出错,根据返回值来进行具体的错误处理一般规则:
1)如果返回值类型时int类型,并且返回的值不可能是负数时,则使用返回值-1代表出错,其他数据表示正常返回。
2)如果返回值类型时int类型,并且返回的值可能是负数时,则需要使用指针取出返回值的数据,返回值仅仅表示是否出错,-1表示出错,0表示正常返回。
3)如果返回值类型是指针类型,则返回值NULL代表出错。
4)如果不考虑是否出错,返回值类型使用void即可。

(3)参看:C语言再学习 -- 文件

stderr -- 标准错误输出设备,例如:

fprintf(stderr, "Can't open it!\n");

(4)C语言再学习 -- EOF、feof函数、ferror函数

ferror () 函数用法。

2、errno 错误代码

经常在调用 linux 系统 api 的时候会出现一些错误,比方说使用open () write () creat () 之类的函数有些时候会返回 -1,也就是调用失败,这个时候往往需要知道失败的原因。 UNIX/Linux 为我们提供了外部全局变量 errno,当函数调用失败,会将具体的错误编号设置到 errno ,我们可以通过 errno 来获取错误的原因。下面我们来介绍 errno。

(1)介绍errno

error 全局变量在 error.h 头文件定义,extern int errno

 在文件 /usr/include/errno.h 
/* Declare the `errno' variable, unless it's defined as a macro by
   bits/errno.h.  This is the case in GNU, where it is a per-thread
   variable.  This redeclaration using the macro still works, but it
   will be a function declaration without a prototype and may trigger
   a -Wstrict-prototypes warning.  */
#ifndef errno
extern int errno;
#endif
错误 Exx 的宏定义在 /usr/include/asm-generic 文件夹下面的  errno-base.h 和 errno.h,分别定义了 1-34 、35-132 的错误定义。

查看 /usr/include/asm-generic/errno-base.h
#ifndef _ASM_GENERIC_ERRNO_BASE_H
#define _ASM_GENERIC_ERRNO_BASE_H

#define	EPERM		 1	/* Operation not permitted */
#define	ENOENT		 2	/* No such file or directory */
#define	ESRCH		 3	/* No such process */
#define	EINTR		 4	/* Interrupted system call */
#define	EIO		 5	/* I/O error */
#define	ENXIO		 6	/* No such device or address */
#define	E2BIG		 7	/* Argument list too long */
#define	ENOEXEC		 8	/* Exec format error */
#define	EBADF		 9	/* Bad file number */
#define	ECHILD		10	/* No child processes */
#define	EAGAIN		11	/* Try again */
#define	ENOMEM		12	/* Out of memory */
#define	EACCES		13	/* Permission denied */
#define	EFAULT		14	/* Bad address */
#define	ENOTBLK		15	/* Block device required */
#define	EBUSY		16	/* Device or resource busy */
#define	EEXIST		17	/* File exists */
#define	EXDEV		18	/* Cross-device link */
#define	ENODEV		19	/* No such device */
#define	ENOTDIR		20	/* Not a directory */
#define	EISDIR		21	/* Is a directory */
#define	EINVAL		22	/* Invalid argument */
#define	ENFILE		23	/* File table overflow */
#define	EMFILE		24	/* Too many open files */
#define	ENOTTY		25	/* Not a typewriter */
#define	ETXTBSY		26	/* Text file busy */
#define	EFBIG		27	/* File too large */
#define	ENOSPC		28	/* No space left on device */
#define	ESPIPE		29	/* Illegal seek */
#define	EROFS		30	/* Read-only file system */
#define	EMLINK		31	/* Too many links */
#define	EPIPE		32	/* Broken pipe */
#define	EDOM		33	/* Math argument out of domain of func */
#define	ERANGE		34	/* Math result not representable */

#endif
查看 /usr/include/asm-generic/errno.h
#ifndef _ASM_GENERIC_ERRNO_H
#define _ASM_GENERIC_ERRNO_H

#include <asm-generic/errno-base.h>

#define	EDEADLK		35	/* Resource deadlock would occur */
#define	ENAMETOOLONG	36	/* File name too long */
#define	ENOLCK		37	/* No record locks available */
#define	ENOSYS		38	/* Function not implemented */
#define	ENOTEMPTY	39	/* Directory not empty */
#define	ELOOP		40	/* Too many symbolic links encountered */
#define	EWOULDBLOCK	EAGAIN	/* Operation would block */
#define	ENOMSG		42	/* No message of desired type */
#define	EIDRM		43	/* Identifier removed */
#define	ECHRNG		44	/* Channel number out of range */
#define	EL2NSYNC	45	/* Level 2 not synchronized */
#define	EL3HLT		46	/* Level 3 halted */
#define	EL3RST		47	/* Level 3 reset */
#define	ELNRNG		48	/* Link number out of range */
#define	EUNATCH		49	/* Protocol driver not attached */
#define	ENOCSI		50	/* No CSI structure available */
#define	EL2HLT		51	/* Level 2 halted */
#define	EBADE		52	/* Invalid exchange */
#define	EBADR		53	/* Invalid request descriptor */
#define	EXFULL		54	/* Exchange full */
#define	ENOANO		55	/* No anode */
#define	EBADRQC		56	/* Invalid request code */
#define	EBADSLT		57	/* Invalid slot */

#define	EDEADLOCK	EDEADLK

#define	EBFONT		59	/* Bad font file format */
#define	ENOSTR		60	/* Device not a stream */
#define	ENODATA		61	/* No data available */
#define	ETIME		62	/* Timer expired */
#define	ENOSR		63	/* Out of streams resources */
#define	ENONET		64	/* Machine is not on the network */
#define	ENOPKG		65	/* Package not installed */
#define	EREMOTE		66	/* Object is remote */
#define	ENOLINK		67	/* Link has been severed */
#define	EADV		68	/* Advertise error */
#define	ESRMNT		69	/* Srmount error */
#define	ECOMM		70	/* Communication error on send */
#define	EPROTO		71	/* Protocol error */
#define	EMULTIHOP	72	/* Multihop attempted */
#define	EDOTDOT		73	/* RFS specific error */
#define	EBADMSG		74	/* Not a data message */
#define	EOVERFLOW	75	/* Value too large for defined data type */
#define	ENOTUNIQ	76	/* Name not unique on network */
#define	EBADFD		77	/* File descriptor in bad state */
#define	EREMCHG		78	/* Remote address changed */
#define	ELIBACC		79	/* Can not access a needed shared library */
#define	ELIBBAD		80	/* Accessing a corrupted shared library */
#define	ELIBSCN		81	/* .lib section in a.out corrupted */
#define	ELIBMAX		82	/* Attempting to link in too many shared libraries */
#define	ELIBEXEC	83	/* Cannot exec a shared library directly */
#define	EILSEQ		84	/* Illegal byte sequence */
#define	ERESTART	85	/* Interrupted system call should be restarted */
#define	ESTRPIPE	86	/* Streams pipe error */
#define	EUSERS		87	/* Too many users */
#define	ENOTSOCK	88	/* Socket operation on non-socket */
#define	EDESTADDRREQ	89	/* Destination address required */
#define	EMSGSIZE	90	/* Message too long */
#define	EPROTOTYPE	91	/* Protocol wrong type for socket */
#define	ENOPROTOOPT	92	/* Protocol not available */
#define	EPROTONOSUPPORT	93	/* Protocol not supported */
#define	ESOCKTNOSUPPORT	94	/* Socket type not supported */
#define	EOPNOTSUPP	95	/* Operation not supported on transport endpoint */
#define	EPFNOSUPPORT	96	/* Protocol family not supported */
#define	EAFNOSUPPORT	97	/* Address family not supported by protocol */
#define	EADDRINUSE	98	/* Address already in use */
#define	EADDRNOTAVAIL	99	/* Cannot assign requested address */
#define	ENETDOWN	100	/* Network is down */
#define	ENETUNREACH	101	/* Network is unreachable */
#define	ENETRESET	102	/* Network dropped connection because of reset */
#define	ECONNABORTED	103	/* Software caused connection abort */
#define	ECONNRESET	104	/* Connection reset by peer */
#define	ENOBUFS		105	/* No buffer space available */
#define	EISCONN		106	/* Transport endpoint is already connected */
#define	ENOTCONN	107	/* Transport endpoint is not connected */
#define	ESHUTDOWN	108	/* Cannot send after transport endpoint shutdown */
#define	ETOOMANYREFS	109	/* Too many references: cannot splice */
#define	ETIMEDOUT	110	/* Connection timed out */
#define	ECONNREFUSED	111	/* Connection refused */
#define	EHOSTDOWN	112	/* Host is down */
#define	EHOSTUNREACH	113	/* No route to host */
#define	EALREADY	114	/* Operation already in progress */
#define	EINPROGRESS	115	/* Operation now in progress */
#define	ESTALE		116	/* Stale NFS file handle */
#define	EUCLEAN		117	/* Structure needs cleaning */
#define	ENOTNAM		118	/* Not a XENIX named type file */
#define	ENAVAIL		119	/* No XENIX semaphores available */
#define	EISNAM		120	/* Is a named type file */
#define	EREMOTEIO	121	/* Remote I/O error */
#define	EDQUOT		122	/* Quota exceeded */

#define	ENOMEDIUM	123	/* No medium found */
#define	EMEDIUMTYPE	124	/* Wrong medium type */
#define	ECANCELED	125	/* Operation Canceled */
#define	ENOKEY		126	/* Required key not available */
#define	EKEYEXPIRED	127	/* Key has expired */
#define	EKEYREVOKED	128	/* Key has been revoked */
#define	EKEYREJECTED	129	/* Key was rejected by service */

/* for robust mutexes */
#define	EOWNERDEAD	130	/* Owner died */
#define	ENOTRECOVERABLE	131	/* State not recoverable */

#define ERFKILL		132	/* Operation not possible due to RF-kill */

#define EHWPOISON	133	/* Memory page has hardware error */

#endif
还有一些更大的错误号是留给内核级别的,如系统调用等,用户程序一般是看不见的这些号的,Ubuntu12.04中/usr/src/linux-headers-3.2.0-23-generic-pae/include/linux/errno.h 

查看 /usr/src/linux-headers-3.2.0-23-generic-pae/include/linux/errno.h 
#ifndef _LINUX_ERRNO_H
#define _LINUX_ERRNO_H

#include <asm/errno.h>

#ifdef __KERNEL__

/*
 * These should never be seen by user programs.  To return one of ERESTART*
 * codes, signal_pending() MUST be set.  Note that ptrace can observe these
 * at syscall exit tracing, but they will never be left for the debugged user
 * process to see.
 */
#define ERESTARTSYS	512
#define ERESTARTNOINTR	513
#define ERESTARTNOHAND	514	/* restart if no handler.. */
#define ENOIOCTLCMD	515	/* No ioctl command */
#define ERESTART_RESTARTBLOCK 516 /* restart by calling sys_restart_syscall */

/* Defined for the NFSv3 protocol */
#define EBADHANDLE	521	/* Illegal NFS file handle */
#define ENOTSYNC	522	/* Update synchronization mismatch */
#define EBADCOOKIE	523	/* Cookie is stale */
#define ENOTSUPP	524	/* Operation is not supported */
#define ETOOSMALL	525	/* Buffer or request is too small */
#define ESERVERFAULT	526	/* An untranslatable error occurred */
#define EBADTYPE	527	/* Type not supported by server */
#define EJUKEBOX	528	/* Request initiated, but will not complete before timeout */
#define EIOCBQUEUED	529	/* iocb queued, will get completion event */
#define EIOCBRETRY	530	/* iocb queued, will trigger a retry */

#endif

#endif

(2)errno 转换成 字符串

1)strerror 函数
#include <string.h>
char *strerror(int errnum);
函数功能:
主要用于将参数指定的错误编号翻译成对应的错误信息返回。
#include <stdio.h>
#include <errno.h>
#include <string.h>

extern int errno ;

int main ()
{
   FILE *fp;
   // file.txt 不存在
   fp = fopen("file.txt", "r");
   if( fp == NULL ) 
   {
      fprintf(stderr, "错误码: %d\n", errno);
      fprintf(stderr, "对应错误信息为: %s\n", strerror(errno));
   }
   else 
   {
      fclose(fp);
   }
   
   return(0);
}
输出结果:
错误码: 2
对应错误信息为: No such file or directory
2)perror 函数 (重点)
#include <stdio.h>
void perror(const char *s);
函数功能:
表示将最后一个错误信息打印出来,参数 s 不为空时原样输出,后面追加一个冒号和空格,再跟着错误信息,以及换行。
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

extern int errno ;

int main (void)
{
	FILE *fp;
	// file.txt 不存在
	fp = fopen("file.txt", "r");   
	if( fp == NULL )   
	{    
		printf("错误码 = %d\n",errno);   
		perror ("打开失败"), exit (-1);   
	}   
	else    
	{      
		fclose(fp);   
	}      
	return(0);
}
输出结果:
错误码 = 2
打开失败: No such file or directory
3)printf 函数
使用 printf("%m\n");  
%m 格式化标记打印错误信息 
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

extern int errno ;

int main (void)
{
	FILE *fp;
	// file.txt 不存在
	fp = fopen("file.txt", "r");   
	if( fp == NULL )   
	{    
		printf("错误码 = %d\n",errno);   
		printf ("%m\n");   
	}   
	else    
	{      
		fclose(fp);   
	}      
	return(0);
}
输出结果:
错误码 = 2
No such file or directory

(3)不能根据错误号判断是否出错

虽然所有的错误号都不是零,但是因为函数执行成功的情况下错误号全局变量 errno 不会被修改,所以不能用该变量的值为零或非零,作为出错或没出错的判断依据。例如:
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

extern int errno ;

int main ()
{
   FILE *fp;
   // file.txt 不存在	
   fp = fopen("file.txt", "r");

   FILE *fp_test;
   // test.txt 存在	
   fp_test = fopen("test.txt", "r");
   if(errno)  //不能根据 errno 判断是否出错
   { 
	   fprintf (stderr, "打开失败\n");
   }
   else 
   {
      fclose(fp_test);
   }
   
   return(0);
}
输出结果:
打开失败
上述例子,就可以看出本来不应该打印 "打开失败" 的。
如果非要使用错误号判断是否出错也可以,那你在调用它之前必须手动将errno清零
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

extern int errno ;

int main ()
{
   FILE *fp;
   // file.txt 不存在	
   fp = fopen("file.txt", "r");

   errno = 0;  //将 errno 清零
   FILE *fp_test;
   // test.txt 存在	
   fp_test = fopen("test.txt", "r");
   if(errno)  //不能根据 errno 判断是否出错
   { 
	   fprintf (stderr, "打开失败\n");
   }
   else 
   {
      fclose(fp_test);
   }
   
   return(0);
}
正确的做法是,先根据函数的返回值判断是否出错,在确定出错的前提下再根据 errno 的值判断具体出了什么错。
#include <stdio.h>
#include <errno.h>
#include <string.h>

extern int errno ;

int main ()
{
   FILE *fp;
   // file.txt 不存在
   fp = fopen("file.txt", "r");
   if( fp == NULL ) 
   {
      fprintf(stderr, "错误码: %d\n", errno);
      fprintf(stderr, "对应错误信息为: %s\n", strerror(errno));
   }
   else 
   {
      fclose(fp);
   }
   
   return(0);
}
输出结果:
错误码: 2
对应错误信息为: No such file or directory

二、编译错误和警告

1、上面提到的 errno 是标准库函数的错误代码,现在来看看gcc编译错误和警告

(1)让所有编译警告都显示出来,选项 -Wall

如下,编辑一段警告的代码
#include <stdio.h>  
  
int main (void)  
{  
    int i;  
    printf ("\n hello world![i]\n", i);  
    return 0;  
}  
  
root@ubuntu:/home/tarena/project/c_test# gcc -Wall hello.c -o hello  
hello.c: 在函数‘main’中:  
hello.c:6:2: 警告: 提供给格式字符串的实参太多 [-Wformat-extra-args]  
hello.c:6:9: 警告: 此函数中的‘i’在使用前未初始化 [-Wuninitialized]  

(2)将编译警告转换成错误的选项 -Werror

编译警告很多时候会被我们忽视,在特殊场合我们还是需要重视编译警告的,如果能把编译警告变成直接输出错误,那我们的重视程度会提高很多并去解决。
#include <stdio.h>  
  
int main (void)  
{  
    int i;  
    printf ("\n hello world![i]\n", i);  
    return 0;  
}  
  
root@ubuntu:/home/tarena/project/c_test# gcc -Wall -Werror hello.c  
hello.c: 在函数‘main’中:  
hello.c:6:2: 错误: 提供给格式字符串的实参太多 [-Werror=format-extra-args]  
cc1: all warnings being treated as errors  

(3)警告级别

如果你觉得警告级别不够,可以使用更高的警告级别。
参看:Options to Request or Suppress Warnings

2、C语言编译错误及警告对照表 

3、将警告,错误等信息输出到文件中

(1)其中标准输出、标准输出、标准错误,可参看:C语言再学习 -- 文件

C 程序自动打开3个文件。这3个文件被称为标准输入,标准输出和标准错误输出。默认的标准输入是系统的一般输入设备,通常为键盘;默认的标准输出和标准错误输出是系统的一般输出设备,通常为显示器,分别得到文件描述符 0, 1, 2.

下面的方法从标准输入(键盘)获得一个字符:  ch = getchar ( );

标准文件指针:

stdio.h文件把3个文件指针与3个C 程序自动打开的标准文件进行了并联,如下表所示:

标准文件  

文件指针  

一般使用的设备  

标准输入

stdin

键盘

标准输出

stdout

显示器

标准错误

stderr

显示器

这些指针都是FILE指针类型,所以可以被用作标准I/O函数的参数。

(2)下面以make命令为例来说明,如何把对应的信息,输出到对应的文件中:

【用法】
1)想要把make输出的全部信息,输出到某个文件中,最常见的办法就是:
make xxx > build_output.txt
此时默认情况是没有改变2=stderr的输出方式,还是屏幕,所以,如果有错误信息,还是可以在屏幕上看到的。

2)只需要把make输出中的错误(及警告)信息输出到文件中ing,可以用:
make xxx 2> build_output.txt
相应地,由于1=stdout没有变,还是屏幕,所以,那些命令执行时候输出的正常信息,还是会输出到屏幕上,你还是可以在屏幕上看到的。
3)只需要把make输出中的正常(非错误,非警告)的信息输出到文件中,可以用:
make xxx 1> build_output.txt
相应地,由于2=stderr没有变,还是屏幕,所以,那些命令执行时候输出的错误信息,还是会输出到屏幕上,你还是可以在屏幕上看到的。
4)想要把正常输出信息和错误信息输出到分别的文件中,可以用:
make xxx 1> build_output_normal.txt 2>build_output_error.txt
即联合使用了1和2,正常信息和错误信息,都输出到对应文件中了。
5)所有的信息都输出到同一个文件中:
make xxx > build_output_all.txt 2>&1
其中的2>&1表示错误信息输出到&1中,而&1,指的是前面的那个文件:build_output_all.txt 。
注意:上面所有的1,2等数字,后面紧跟着大于号'>' ,中间不能有空格。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

聚优致成

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值