众所周知,C程序的主函数有两个参数,其中,第一个参数是整型,可以获得包括程序名字的参数个数,第二个参数是字符数组指针或字符指针的指针,可以按顺序获得命令行上各个字符串参数。其原形是:
int main(int argc, char *argv[]);
或者
int main(int argc, char **argv);
如果有一个解析CDR的程序,名叫destroy,负责将一个二进制格式的CDR文件转换为文本文件,输出的文本的样式由另外一个描述文件定义,那么,命令行要求输入的参数就有三个:CDR文件名、输出文件名和描述文件名。其中,前两个参数是必须输入的,第三个的描述文件名可以不输入,程序会自动采用默认的输出样式。很自然,主函数的三个参数就应该这样排列:
./destroy cdr cdr.txt [cdr.desc]
这样做在一般情况下不会有太大问题,问题来源于扩展性的需求。如果有一天,用户要求解析程序能够按关键字解析,只有含有关键字的CDR才能够输出。解决方法很简单,只要在参数列表的最后,加上它就可以了。不过,这样就使得原本可选的描述文件名变为必须输入:
./destroy cdr cdr.txt cdr.desc [keyword]
因为不改的话,你就不知道,第三个参数究竟是描述文件名,还是关键字。现在还算好办,如果以后陆续有增加参数的需求,关键字也变成必须输入了,这个时候,如果要查找全部CDR,你还得定义一个“特殊的关键字”,告诉程序,把数据统统给我捞出来……
有鉴于此,在Unix/Linux的正式的项目上,程序员通常会使用getopt()或者getopt_long()来获得输入的参数。两者的一个区别在于getopt()只支持短格式参数,而getopt_long()既支持短格式参数,又支持长格式参数。
短格式:./destroy -f cdr -o cdr.txt -c cdr.desc -k 123456
长格式:./destroy --file cdr --output cdr.txt --config cdr.desc --keyword 123456
引入了getopt()和getopt_long()的项目,设计者可以按需要,方便地增加参数,或者随意地放置参数的先后次序,只需要在程序中判断,哪些参数是必须的就可以了。关于这两个函数的用法,大家可以上网搜索一下,不再累述。附件destroy_linux.c给出了在Linux下使用getopt_long()的实例。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <getopt.h> void print_usage(const char *program_name) { printf("%s 1.0.0 (2010-06-13)/n", program_name); printf("This is a program decoding a BER encoded CDR file/n"); printf("Usage: %s -f <file_name> -o <output_name> [-c <config_name>] [-k <keyword>]/n", program_name); printf(" -f --file the CDR file to be decoded/n"); printf(" -o --output the output file in plain text format/n"); printf(" -c --config the description file of the CDR file, if not given, use default configuration/n"); printf(" -k --keyword the keyword to search, if not given, all records will be written into output file/n"); } int main(int argc, char *argv[]) { char *file_name = NULL; char *output_name = NULL; char *config_name = NULL; char *keyword = NULL; const char *short_opts = "hf:o:c:k:"; const struct option long_opts[] = { {"help", no_argument, NULL, 'h'}, {"file", required_argument, NULL, 'f'}, {"output", required_argument, NULL, 'o'}, {"config", required_argument, NULL, 'c'}, {"keyword", required_argument, NULL, 'k'}, {0, 0, 0, 0} }; int hflag = 0; int c; opterr = 0; while ( (c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1 ) { switch ( c ) { case 'h' : hflag = 1; break; case 'f' : file_name = optarg; break; case 'o' : output_name = optarg; break; case 'c' : config_name = optarg; break; case 'k' : keyword = optarg; break; case '?' : if ( optopt == 'f' || optopt == 'o' || optopt == 'c' || optopt == 'k' ) printf("Error: option -%c requires an argument/n", optopt); else if ( isprint(optopt) ) printf("Error: unknown option '-%c'/n", optopt); else printf("Error: unknown option character '//x%x'/n", optopt); return 1; default : abort(); } } if ( hflag || argc == 1 ) { print_usage(argv[0]); return 0; } if ( !file_name ) { printf("Error: file name must be specified/n"); return 1; } if ( !output_name ) { printf("Error: output name must be specified/n"); return 1; } // if not setting default, Linux OK, but SunOS core dump if ( !config_name ) config_name = "(null)"; if ( !keyword ) keyword = "(null)"; printf("Parameters got: file_name = %s, output_name = %s, config_name = %s, keyword = %s/n", file_name, output_name, config_name, keyword); return 0; }
另外一个区别是,getopt()几乎通用于所有类Unix系统,而getopt_long()只有在GNU的Unix/Linux下才能用。如果把上述程序放到Tru64上编译,就会出现以下错误:
cc -o destroy destroy_linux.c
cc: Error: destroy_linux.c, line 24: In the initializer for long_opts, an array's element type is incomplete, which precludes its initialization. (incompelinit)
{"help", no_argument, NULL, 'h'},
----------------^
所以,如果一定要在Tru64等非GNU的OS上做到长格式的效果,除了自己另起炉灶之外,基本上只好借助一些跨平台的开源项目了。附件里的getopt_long.c和getopt.h是从opensolaris的网站上抄下来的,是包含在sg3_utils软件包中的程序。sg3_utils具体是什么,我也不知道,据说是一个Linux的开发包,用来直接使用SCSI命令集访问设备。(sg3_utils is a package of utilities for accessing devices that use SCSI command sets.)反正拿来能用就是了!
/* $NetBSD: getopt.h,v 1.7 2005/02/03 04:39:32 perry Exp $ */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Dieter Baron and Thomas Klausner. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * modified May 12, 2005 by Jim Basney <[email protected]> * * removed #include of non-POSIX <sys/cdefs.h> and <sys/featuretest.h> * removed references to _NETBSD_SOURCE and HAVE_NBTOOL_CONFIG_H * added #if !HAVE_GETOPT_LONG * removed __BEGIN_DECLS and __END_DECLS */ #ifndef _MYPROXY_GETOPT_H_ #define _MYPROXY_GETOPT_H_ #if !HAVE_GETOPT_LONG #include <unistd.h> /* * Gnu like getopt_long() and BSD4.4 getsubopt()/optreset extensions */ #define no_argument 0 #define required_argument 1 #define optional_argument 2 extern char *optarg; extern int optind; extern int optopt; extern int opterr; struct option { /* name of long option */ const char *name; /* * one of no_argument, required_argument, and optional_argument: * whether option takes an argument */ int has_arg; /* if not NULL, set *flag to val when option found */ int *flag; /* if flag not NULL, value to set *flag to; else return value */ int val; }; int getopt_long(int, char * const *, const char *, const struct option *, int *); #endif /* !HAVE_GETOPT_LONG */ #endif /* !_MYPROXY_GETOPT_H_ */
/* $NetBSD: getopt_long.c,v 1.17 2004/06/20 22:20:15 jmc Exp $ */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Dieter Baron and Thomas Klausner. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * modified May 12, 2005 by Jim Basney <[email protected]> * * removed #include of non-POSIX <sys/cdefs.h> <err.h> * removed #include of "namespace.h" * use local "port_getopt.h" instead of <getopt.h> * removed REPLACE_GETOPT and HAVE_NBTOOL_CONFIG_H sections * removed __P() from function declarations * use ANSI C function parameter lists * removed optreset support * replace _DIAGASSERT() with assert() * replace non-POSIX warnx(...) with fprintf(stderr, ...) * added extern declarations for optarg, optind, opterr, and optopt */ #if defined(LIBC_SCCS) && !defined(lint) __RCSID("$NetBSD: getopt_long.c,v 1.17 2004/06/20 22:20:15 jmc Exp $"); #endif /* LIBC_SCCS and not lint */ #include <assert.h> #include <errno.h> #include "getopt.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef __weak_alias __weak_alias(getopt_long,_getopt_long) #endif #if !HAVE_GETOPT_LONG #define IGNORE_FIRST (*options == '-' || *options == '+') #define PRINT_ERROR ((opterr) && ((*options != ':') / || (IGNORE_FIRST && options[1] != ':'))) #define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL) #define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST) /* XXX: GNU ignores PC if *options == '-' */ #define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-') /* return values */ #define BADCH (int)'?' #define BADARG ((IGNORE_FIRST && options[1] == ':') / || (*options == ':') ? (int)':' : (int)'?') #define INORDER (int)1 #define EMSG "" extern char *optarg; extern int optind, opterr, optopt; static int getopt_internal (int, char * const *, const char *); static int gcd (int, int); static void permute_args (int, int, int, char * const *); static char *place = EMSG; /* option letter processing */ static int nonopt_start = -1; /* first non option argument (for permute) */ static int nonopt_end = -1; /* first option after non options (for permute) */ /* Error messages */ static const char recargchar[] = "option requires an argument -- %c"; static const char recargstring[] = "option requires an argument -- %s"; static const char ambig[] = "ambiguous option -- %.*s"; static const char noarg[] = "option doesn't take an argument -- %.*s"; static const char illoptchar[] = "unknown option -- %c"; static const char illoptstring[] = "unknown option -- %s"; /* * Compute the greatest common divisor of a and b. */ static int gcd(int a, int b) { int c; c = a % b; while (c != 0) { a = b; b = c; c = a % b; } return b; } /* * Exchange the block from nonopt_start to nonopt_end with the block * from nonopt_end to opt_end (keeping the same order of arguments * in each block). */ static void permute_args(int panonopt_start, int panonopt_end, int opt_end, char * const *nargv) { int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; char *swap; assert(nargv != NULL); /* * compute lengths of blocks and number and size of cycles */ nnonopts = panonopt_end - panonopt_start; nopts = opt_end - panonopt_end; ncycle = gcd(nnonopts, nopts); cyclelen = (opt_end - panonopt_start) / ncycle; for (i = 0; i < ncycle; i++) { cstart = panonopt_end+i; pos = cstart; for (j = 0; j < cyclelen; j++) { if (pos >= panonopt_end) pos -= nnonopts; else pos += nopts; swap = nargv[pos]; /* LINTED const cast */ ((char **) nargv)[pos] = nargv[cstart]; /* LINTED const cast */ ((char **)nargv)[cstart] = swap; } } } /* * getopt_internal -- * Parse argc/argv argument vector. Called by user level routines. * Returns -2 if -- is found (can be long option or end of options marker). */ static int getopt_internal(int nargc, char * const *nargv, const char *options) { char *oli; /* option letter list index */ int optchar; assert(nargv != NULL); assert(options != NULL); optarg = NULL; /* * XXX Some programs (like rsyncd) expect to be able to * XXX re-initialize optind to 0 and have getopt_long(3) * XXX properly function again. Work around this braindamage. */ if (optind == 0) optind = 1; start: if (!*place) { /* update scanning pointer */ if (optind >= nargc) { /* end of argument vector */ place = EMSG; if (nonopt_end != -1) { /* do permutation, if we have to */ permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } else if (nonopt_start != -1) { /* * If we skipped non-options, set optind * to the first of them. */ optind = nonopt_start; } nonopt_start = nonopt_end = -1; return -1; } if ((*(place = nargv[optind]) != '-') || (place[1] == '/0')) { /* found non-option */ place = EMSG; if (IN_ORDER) { /* * GNU extension: * return non-option as argument to option 1 */ optarg = nargv[optind++]; return INORDER; } if (!PERMUTE) { /* * if no permutation wanted, stop parsing * at first non-option */ return -1; } /* do permutation */ if (nonopt_start == -1) nonopt_start = optind; else if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); nonopt_start = optind - (nonopt_end - nonopt_start); nonopt_end = -1; } optind++; /* process next argument */ goto start; } if (nonopt_start != -1 && nonopt_end == -1) nonopt_end = optind; if (place[1] && *++place == '-') { /* found "--" */ place++; return -2; } } if ((optchar = (int)*place++) == (int)':' || (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) { /* option letter unknown or ':' */ if (!*place) ++optind; if (PRINT_ERROR) fprintf(stderr, illoptchar, optchar); optopt = optchar; return BADCH; } if (optchar == 'W' && oli[1] == ';') { /* -W long-option */ /* XXX: what if no long options provided (called by getopt)? */ if (*place) return -2; if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) fprintf(stderr, recargchar, optchar); optopt = optchar; return BADARG; } else /* white space */ place = nargv[optind]; /* * Handle -W arg the same as --arg (which causes getopt to * stop parsing). */ return -2; } if (*++oli != ':') { /* doesn't take argument */ if (!*place) ++optind; } else { /* takes (optional) argument */ optarg = NULL; if (*place) /* no white space */ optarg = place; /* XXX: disable test for :: if PC? (GNU doesn't) */ else if (oli[1] != ':') { /* arg not optional */ if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) fprintf(stderr, recargchar, optchar); optopt = optchar; return BADARG; } else optarg = nargv[optind]; } place = EMSG; ++optind; } /* dump back option letter */ return optchar; } /* * getopt_long -- * Parse argc/argv argument vector. */ int getopt_long(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx) { int retval; assert(nargv != NULL); assert(options != NULL); assert(long_options != NULL); /* idx may be NULL */ if ((retval = getopt_internal(nargc, nargv, options)) == -2) { char *current_argv, *has_equal; size_t current_argv_len; int i, match; current_argv = place; match = -1; optind++; place = EMSG; if (*current_argv == '/0') { /* found "--" */ /* * We found an option (--), so if we skipped * non-options, we have to permute. */ if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } nonopt_start = nonopt_end = -1; return -1; } if ((has_equal = strchr(current_argv, '=')) != NULL) { /* argument found (--option=arg) */ current_argv_len = has_equal - current_argv; has_equal++; } else current_argv_len = strlen(current_argv); for (i = 0; long_options[i].name; i++) { /* find matching long option */ if (strncmp(current_argv, long_options[i].name, current_argv_len)) continue; if (strlen(long_options[i].name) == (unsigned)current_argv_len) { /* exact match */ match = i; break; } if (match == -1) /* partial match */ match = i; else { /* ambiguous abbreviation */ if (PRINT_ERROR) fprintf(stderr, ambig, (int)current_argv_len, current_argv); optopt = 0; return BADCH; } } if (match != -1) { /* option found */ if (long_options[match].has_arg == no_argument && has_equal) { if (PRINT_ERROR) fprintf(stderr, noarg, (int)current_argv_len, current_argv); /* * XXX: GNU sets optopt to val regardless of * flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; return BADARG; } if (long_options[match].has_arg == required_argument || long_options[match].has_arg == optional_argument) { if (has_equal) optarg = has_equal; else if (long_options[match].has_arg == required_argument) { /* * optional argument doesn't use * next nargv */ optarg = nargv[optind++]; } } if ((long_options[match].has_arg == required_argument) && (optarg == NULL)) { /* * Missing argument; leading ':' * indicates no error should be generated */ if (PRINT_ERROR) fprintf(stderr, recargstring, current_argv); /* * XXX: GNU sets optopt to val regardless * of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; --optind; return BADARG; } } else { /* unknown option */ if (PRINT_ERROR) fprintf(stderr, illoptstring, current_argv); optopt = 0; return BADCH; } if (long_options[match].flag) { *long_options[match].flag = long_options[match].val; retval = 0; } else retval = long_options[match].val; if (idx) *idx = match; } return retval; } #endif /* !GETOPT_LONG */
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include "getopt.h" void print_usage(const char *program_name) { printf("%s 1.0.0 (2010-06-13)/n", program_name); printf("This is a program decoding a BER encoded CDR file/n"); printf("Usage: %s -f <file_name> -o <output_name> [-c <config_name>] [-k <keyword>]/n", program_name); printf(" -f --file the CDR file to be decoded/n"); printf(" -o --output the output file in plain text format/n"); printf(" -c --config the description file of the CDR file, if not given, use default configuration/n"); printf(" -k --keyword the keyword to search, if not given, all records will be written into output file/n"); } int main(int argc, char *argv[]) { char *file_name = NULL; char *output_name = NULL; char *config_name = NULL; char *keyword = NULL; const char *short_opts = "hf:o:c:k:"; const struct option long_opts[] = { {"help", no_argument, NULL, 'h'}, {"file", required_argument, NULL, 'f'}, {"output", required_argument, NULL, 'o'}, {"config", required_argument, NULL, 'c'}, {"keyword", required_argument, NULL, 'k'}, {0, 0, 0, 0} }; int hflag = 0; int c; opterr = 0; while ( (c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1 ) { switch ( c ) { case 'h' : hflag = 1; break; case 'f' : file_name = optarg; break; case 'o' : output_name = optarg; break; case 'c' : config_name = optarg; break; case 'k' : keyword = optarg; break; case '?' : if ( optopt == 'f' || optopt == 'o' || optopt == 'c' || optopt == 'k' ) printf("Error: option -%c requires an argument/n", optopt); else if ( isprint(optopt) ) printf("Error: unknown option '-%c'/n", optopt); else printf("Error: unknown option character '//x%x'/n", optopt); return 1; default : abort(); } } if ( hflag || argc == 1 ) { print_usage(argv[0]); return 0; } if ( !file_name ) { printf("Error: file name must be specified/n"); return 1; } if ( !output_name ) { printf("Error: output name must be specified/n"); return 1; } // if not setting default, Linux OK, but SunOS core dump if ( !config_name ) config_name = "(null)"; if ( !keyword ) keyword = "(null)"; printf("Parameters got: file_name = %s, output_name = %s, config_name = %s, keyword = %s/n", file_name, output_name, config_name, keyword); return 0; }
Linux下编译
-bash-3.2$ gcc -o destroy destroy.c getopt_long.c
短格式,全部输入
-bash-3.2$ ./destroy -f aaa -o aaa.txt -c ccc -k 222
Parameters got: file_name = aaa, output_name = aaa.txt, config_name = ccc, keyword = 222
前两个长格式,后两个短格式
-bash-3.2$ ./destroy --file aaa --output aaa.txt -c ccc -k 222
Parameters got: file_name = aaa, output_name = aaa.txt, config_name = ccc, keyword = 222
漏掉一个必须输入的参数会报错
-bash-3.2$ ./destroy -output aaa.txt
Error: file name must be specified
次序随意,长短混用
-bash-3.2$ ./destroy -c ccc -o aaa.txt -k 222 --file aaa
Parameters got: file_name = aaa, output_name = aaa.txt, config_name = ccc, keyword = 222
题外话,#include <filename.h>与#include “filename.h”有什么区别,是面试C程序员经常问到的一个问题。答案大家都知道了,#include <filename.h>,编译器从标准库路径搜索filename.h,而#include “filename.h”,编译器从用户的工作路径搜索filename.h。
此外,网上也有人说从glibc(http://sourceware.org/glibc/)上把getopt.h、getopt.c和getoptl.c拿过来也能够用。我也试过,但是不清楚什么原因不成功。
在这个小实验的过程中,还发现了C语言在各个OS下的一些细小差异,比如destroy.c里,79行到82行:
// if not setting default, Linux OK, but SunOS core dump
if ( !config_name ) config_name = "(null)";
if ( !keyword ) keyword = "(null)";
printf("Parameters got: file_name = %s, output_name = %s, config_name = %s, keyword = %s/n", file_name, output_name, config_name, keyword);
如果在81行和82行不设置空指针的默认值,Linux和Tru64都会自动帮你转换而避免运行时错误,但是SunOS不会,它会死给你看。
./destroy -f aaa -o aaa.txt
Segmentation Fault (core dumped)
再比如第62行的abort()在头文件stdlib.h中定义,如果不包含此文件,SunOS与Tru64编译都没问题,Linux编译时会警告:
warning: incompatible implicit declaration of built-in function abort
由此看来,虽然C也公认是可移植性比较好的语言,但是在跨平台的项目中,也应该注意这些微小的差别。