<errno.h>
header file defines macros that correspond to error codes. #!/usr/bin/perl use strict; use warnings; use Errno; foreach my $err (sort keys (%!)) { $! = eval "Errno::$err"; printf "%20s %4d %s\n", $err, $! + 0, $! }
0
. /* convert from UTF16 to UTF8 */ errno = 0; n_ret = iconv(icd, (char **) &p_src, &n_src, &p_dst, &n_dst); if (n_ret == (size_t) -1) { VJ_PERROR(); if (errno == E2BIG) fprintf(stderr, " Error : input conversion stopped due to lack of space in the output buffer\n"); else if (errno == EILSEQ) fprintf(stderr, " Error : input conversion stopped due to an input byte that does not belong to the input codeset\n"); else if (errno == EINVAL) fprintf(stderr, " Error : input conversion stopped due to an incomplete character or shift sequence at the end of the input buffer\n"); /* clean the memory */ free(p_out_buf); errno = 0; n_ret = iconv_close(icd); if (n_ret == (size_t) -1) VJ_PERROR(); return (size_t) -1; }
iconv()
function specification are more informative than in <errno.h>
.void perror(const char *s);
s
, followed by a colon, a space, and an error message. Then it prints the newline character '\n'
. /* // main.c // perror example // // Created by Ariel Feinerman on 23/03/17. // Copyright 2017 Feinerman Research, Inc. All rights reserved. */ #include <stdio.h> #include <stdlib.h> #include <errno.h> int main(int argc, const char * argv[]) { // Generate unique filename. char *file_name = tmpnam((char[L_tmpnam]){0}); errno = 0; FILE *file = fopen(file_name, "rb"); if (file) { // Do something useful. fclose(file); } else { perror("fopen() "); } return EXIT_SUCCESS; }
errnum
. The language of the message depends on the locale (German, Hebrew and even Japanese), but usually only English is supported. /* // main.c // strerror example // // Created by Ariel Feinerman on 23/03/17. // Copyright 2017 Feinerman Research, Inc. All rights reserved. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> int main(int argc, const char * argv[]) { // Generate unique filename. char *file_name = tmpnam((char[L_tmpnam]){0}); errno = 0; FILE *file = fopen(file_name, "rb"); // Save error number. errno_t error_num = errno; if (file) { // Do something useful. fclose(file); } else { char *errorbuf = strerror(error_num); fprintf(stderr, "Error message : %s\n", errorbuf); } return EXIT_SUCCESS; }
strerror()
not a safe function. First, the string returned by it is not constant. However, it can be stored in static or in dynamic memory, depending on the implementation. In the first case, changing it will result in a run-time error. Secondly, if you decide to save a pointer to a string, and then call a function with a new code, all previous pointers will point to a new row, because it uses one buffer for all rows. Third, its behavior in a multithreaded environment is not defined in the standard. However, in QNX, it is declared as thread safe.size_t strerrorlen_s(errno_t errnum);
errnum
.errno_t strerror_s(char *buf, rsize_t buflen, errno_t errnum);
errnum
error errnum
to the buf
buffer of length buflen
. /* // main.c // strerror_s example // // Created by Ariel Feinerman on 23/02/17. // Copyright 2017 Feinerman Research, Inc. All rights reserved. */ #define __STDC_WANT_LIB_EXT1__ 1 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> int main(int argc, const char * argv[]) { // Generate unique filename. char *file_name = tmpnam((char[L_tmpnam]){0}); errno = 0; FILE *file = fopen(file_name, "rb"); // Save error number. errno_t error_num = errno; if (file) { // Do something useful. fclose(file); } else { #ifdef __STDC_LIB_EXT1__ size_t error_len = strerrorlen_s(errno) + 1; char error_buf[error_len]; strerror_s(error_buf, error_len, errno); fprintf(stderr, "Error message : %s\n", error_buf); #endif } return EXIT_SUCCESS; }
char *strerror_l(int errnum, locale_t locale);
errnum
error using locale
. Safe in multithreaded environment. Not implemented on Mac OS X , FreeBSD , NetBSD , OpenBSD , Solaris, and other commercial UNIX. Implemented in Linux, MINIX 3 and Illumos (OpenSolaris). /* // main.c // strerror_l example – works on Linux, MINIX 3, Illumos // // Created by Ariel Feinerman on 23/03/17. // Copyright 2017 Feinerman Research, Inc. All rights reserved. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <locale.h> int main(int argc, const char * argv[]) { locale_t locale = newlocale(LC_ALL_MASK, "fr_FR.UTF-8", (locale_t) 0); if (!locale) { fprintf(stderr, "Error: cannot create locale."); exit(EXIT_FAILURE); } // Generate unique filename. char *file_name = tmpnam((char[L_tmpnam]){0}); errno = 0; FILE *file = fopen(tmpnam(file_name, "rb"); // Save error number. errno_t error_num = errno; if (file) { // Do something useful. fclose(file); } else { char *error_buf = strerror_l(errno, locale); fprintf(stderr, "Error message : %s\n", error_buf); } freelocale(locale); return EXIT_SUCCESS; }
Error message : Aucun fichier ou dossier de ce type
int strerror_r(int errnum, char *buf, size_t buflen);
errnum
error errnum
to the buf
buffer of length buflen
. If buflen
less than the length of the string, the excess is trimmed. Safe in multithreaded environment. Implemented in all UNIX. /* // main.c // strerror_r POSIX example // // Created by Ariel Feinerman on 25/02/17. // Copyright 2017 Feinerman Research, Inc. All rights reserved. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define MSG_LEN 1024 int main(int argc, const char * argv[]) { // Generate unique filename. char *file_name = tmpnam((char[L_tmpnam]){0}); errno = 0; FILE *file = fopen(file_name, "rb"); // Save error number. errno_t error_num = errno; if (file) { // Do something useful. fclose(file); } else { char error_buf[MSG_LEN]; errno_t error = strerror_r (error_num, error_buf, MSG_LEN); switch (error) { case EINVAL: fprintf (stderr, "strerror_r() failed: invalid error code, %d\n", error); break; case ERANGE: fprintf (stderr, "strerror_r() failed: buffer too small: %d\n", MSG_LEN); case 0: fprintf(stderr, "Error message : %s\n", error_buf); break; default: fprintf (stderr, "strerror_r() failed: unknown error, %d\n", error); break; } } return EXIT_SUCCESS; }
strerrorlen_s()
in POSIX was determined, so the length of the string can only be determined experimentally. Usually 300 characters are enough for the eyes The GNU C Library implementation of strerror()
uses a buffer with a length of 1024 characters. But you never know, and suddenly? /* // main.c // strerror_r safe POSIX example // // Created by Ariel Feinerman on 23/03/17. // Copyright 2017 Feinerman Research, Inc. All rights reserved. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define MSG_LEN 1024 #define MUL_FACTOR 2 int main(int argc, const char * argv[]) { // Generate unique filename. char *file_name = tmpnam((char[L_tmpnam]){0}); errno = 0; FILE *file = fopen(file_name, "rb"); // Save error number. errno_t error_num = errno; if (file) { // Do something useful. fclose(file); } else { errno_t error = 0; size_t error_len = MSG_LEN; do { char error_buf[error_len]; error = strerror_r (error_num, error_buf, error_len); switch (error) { case 0: fprintf(stderr, "File : %s\nLine : %d\nCurrent function : %s()\nFailed function : %s()\nError message : %s\n", __FILE__, __LINE__, __func__, "fopen", error_buf); break; case ERANGE: error_len *= MUL_FACTOR; break; case EINVAL: fprintf (stderr, "strerror_r() failed: invalid error code, %d\n", error_num); break; default: fprintf (stderr, "strerror_r() failed: unknown error, %d\n", error); break; } } while (error == ERANGE); } return EXIT_SUCCESS; }
File : /Users/ariel/main.c Line : 47 Current function : main() Failed function : fopen() Error message : No such file or directory
void assert(expression)
expression
condition (its result must be a number) at run time. If the condition is not satisfied ( expression
is zero), it prints the values __FILE__
, __LINE__
, __func__
and expression
as strings to stderr , and then calls the abort()
function. /* // main.c // assert example // // Created by Ariel Feinerman on 23/03/17. // Copyright 2017 Feinerman Research, Inc. All rights reserved. */ #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <math.h> int main(int argc, const char * argv[]) { double x = -1.0; assert(x >= 0.0); printf("sqrt(x) = %f\n", sqrt(x)); return EXIT_SUCCESS; }
Assertion failed: (x >= 0.0), function main, file /Users/ariel/main.c, line 17.
NDEBUG
macro NDEBUG
defined before including <assert.h>
, then assert()
expanded to ((void) 0)
and does nothing. Used for debugging purposes. /* // main.c // assert_example // // Created by Ariel Feinerman on 23/03/17. // Copyright 2017 Feinerman Research, Inc. All rights reserved. */ #NDEBUG #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <math.h> int main(int argc, const char * argv[]) { double x = -1.0; assert(x >= 0.0); printf("sqrt(x) = %f\n", sqrt(x)); return EXIT_SUCCESS; }
sqrt(x) = nan
int atexit(void (*func)(void));
_Noreturn void exit(int exit_code);
exit_code
on Wednesday. The ISO C standard defines only three possible values: 0
, EXIT_SUCCESS
and EXIT_FAILURE
. In this case, functions registered through atexit()
are called, input-output streams are reset and closed, temporary files are deleted, after which control is transferred to the environment. The exit()
function is called in main()
when executing a return or reaching the end of a program.exit()
is that it allows you to terminate the program not only from main()
, but also from any nested function. For example, if in a deeply nested function a certain condition is fulfilled (or not fulfilled), after which further program execution loses all meaning. Such a reception (early exit) is widely used when writing daemons, system utilities and parsers. In interactive programs with an infinite main loop, exit()
can be used to exit the program by selecting the desired menu item. /* // main.c // exit example // // Created by Ariel Feinerman on 17/03/17. // Copyright 2017 Feinerman Research, Inc. All rights reserved. */ #include <stdio.h> #include <stdlib.h> #include <math.h> void third_2(void) { printf("third #2\n"); // Does not print. } void third_1(void) { printf("third #1\n"); // Does not print. } void second(double num) { printf("second : before exit()\n"); // Prints. if ((num < 1.0f) && (num > -1.0f)) { printf("asin(%.1f) = %.3f\n", num, asin(num)); exit(EXIT_SUCCESS); } else { fprintf(stderr, "Error: %.1f is beyond the range [-1.0; 1.0]\n", num); exit(EXIT_FAILURE); } printf("second : after exit()\n"); // Does not print. } void first(double num) { printf("first : before second()\n") second(num); printf("first : after second()\n"); // Does not print. } int main(int argc, const char * argv[]) { atexit(third_1); // Register first handler. atexit(third_2); // Register second handler. first(-3.0f); return EXIT_SUCCESS; }
first : before second() second : before exit() Error: -3.0 is beyond the range [-1.0; 1.0] third #2 third #1
_Noreturn void abort(void);
exit(EXIT_FAILURE)
calls is that the first one sends the SIGABRT
signal to the program, it can be intercepted and the necessary actions can be taken before the program ends. A core dump file of the program is recorded, if enabled. When launched in the debugger, it intercepts the SIGABRT
signal and stops the execution of the program, which is very convenient in debugging. /* // main.c // abort example // // Created by Ariel Feinerman on 17/02/17. // Copyright 2017 Feinerman Research, Inc. All rights reserved. */ #include <stdio.h> #include <stdlib.h> #include <math.h> void third_2(void) { printf("third #2\n"); // Does not print. } void third_1(void) { printf("third #1\n"); // Does not print. } void second(double num) { printf("second : before exit()\n"); // Prints. if ((num < 1.0f) && (num > -1.0f)) { printf("asin(%.1f) = %.3f\n", num, asin(num)); exit(EXIT_SUCCESS); } else { fprintf(stderr, "Error: %.1f is beyond the range [-1.0; 1.0]\n", num); abort(); } printf("second : after exit()\n"); // Does not print. } void first(double num) { printf("first : before second()\n"); second(num); printf("first : after second()\n"); // Does not print. } int main(int argc, const char * argv[]) { atexit(third_1); // register first handler atexit(third_2); // register second handler first(-3.0f); return EXIT_SUCCESS; }
first : before second() second : before exit() Error: -3.0 is beyond the range [-1.0; 1.0] Abort trap: 6
$ lldb abort_example (lldb) target create "abort_example" Current executable set to 'abort_example' (x86_64). (lldb) run Process 22570 launched: '/Users/ariel/abort_example' (x86_64) first : before second() second : before exit() Error: -3.0 is beyond the range [-1.0; 1.0] Process 22570 stopped * thread #1: tid = 0x113a8, 0x00007fff89c01286 libsystem_kernel.dylib`__pthread_kill + 10, queue = 'com.apple.main-thread', stop reason = signal SIGABRT frame #0: 0x00007fff89c01286 libsystem_kernel.dylib`__pthread_kill + 10 libsystem_kernel.dylib`__pthread_kill: -> 0x7fff89c01286 <+10>: jae 0x7fff89c01290 ; <+20> 0x7fff89c01288 <+12>: movq %rax, %rdi 0x7fff89c0128b <+15>: jmp 0x7fff89bfcc53 ; cerror_nocancel 0x7fff89c01290 <+20>: retq (lldb)
abort()
function must be used. For example, if an error occurred while allocating memory or writing a file. Any further action may aggravate the situation. If you complete the execution in the usual way, which is used to reset the input / output streams, you can lose still intact data and temporary files, so the best solution would be to write a dump and instantly terminate the program.exit()
.setjmp()
and longjmp()
work on the goto principle, but unlike it, they allow you to jump from one place to another within the entire program, rather than one function.int setjmp(jmp_buf env);
env
. Returns 0
if it was called directly or value
, if from longjmp()
.void longjmp(jmp_buf env, int value);
env
, returns control to setjmp()
and passes it value
. /* // main.c // setjmp simple // // Created by Ariel Feinerman on 18/02/17. // Copyright 2017 Feinerman Research, Inc. All rights reserved. */ #include <stdio.h> #include <stdlib.h> #include <setjmp.h> static jmp_buf buf; void second(void) { printf("second : before longjmp()\n"); // prints longjmp(buf, 1); // jumps back to where setjmp was called – making setjmp now return 1 printf("second : after longjmp()\n"); // does not prints // <- Here is the point that is never reached. All impossible cases like your own house in Miami, your million dollars, your nice girl, etc. } void first(void) { printf("first : before second()\n"); second(); printf("first : after second()\n"); // does not print } int main(int argc, const char * argv[]) { if (!setjmp(buf)) first(); // when executed, setjmp returned 0 else // when longjmp jumps back, setjmp returns 1 printf("main\n"); // prints return EXIT_SUCCESS; }
first : before second() second : before longjmp() main
setjmp()
and longjmp
(), you can implement an exception mechanism. In many high-level languages ​​(for example, in Perl ), exceptions are implemented through them. /* // main.c // exception simple // // Created by Ariel Feinerman on 18/02/17. // Copyright 2017 Feinerman Research, Inc. All rights reserved. */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <setjmp.h> #define str(s) #s static jmp_buf buf; typedef enum { NO_EXCEPTION = 0, RANGE_EXCEPTION = 1, NUM_EXCEPTIONS } exception_t; static char *exception_name[NUM_EXCEPTIONS] = { str(NO_EXCEPTION), str(RANGE_EXCEPTION) }; float asin_e(float num) { if ((num < 1.0f) && (num > -1.0f)) { return asinf(num); } else { longjmp(buf, RANGE_EXCEPTION); // | @throw } } void do_work(float num) { float res = asin_e(num); printf("asin(%f) = %f\n", num, res); } int main(int argc, const char * argv[]) { exception_t exc = NO_EXCEPTION; if (!(exc = setjmp(buf))) { // | do_work(-3.0f); // | @try } // | else { // | fprintf(stderr, "%s was hadled in %s()\n", exception_name[exc], __func__); // | @catch } // | return EXIT_SUCCESS; }
RANGE_EXCEPTION was hadled in main()
setjmp()
and longjmp
() functions are primarily used in system programming, and their use in client code is not recommended. Their use worsens the readability of the program and can lead to unpredictable errors. For example, what happens if you jump not up the stack - into the calling function, but into a parallel one that has already completed its execution?Source: https://habr.com/ru/post/324642/
All Articles