Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
becki:linux:c [2009-12-22 16:05] becki |
becki:linux:c [2017-12-07 14:19] (aktuell) becki [Definition of constant Strings] |
||
---|---|---|---|
Zeile 1: | Zeile 1: | ||
====== C Tips == | ====== C Tips == | ||
- | ===== A Proposal for unified Exception Handling == | ||
- | ==== Assertions == | ||
- | Concepts are borrowed mainly from Java here. | + | This is about ISO C and its standard library. POSIX/Linux enhancements and features are described in [[:becki:my:linux:linux programming]]. How to build applications is described in [[build tools]]. |
- | Use ''assert'' to handle errors in code. An assertion can be seen as a runtime exception which is never catched and can be switched of. Both assertions an runtime exceptions deal with errors in the code. | + | ===== CLI args and main() == |
- | Use ''check'' when you neigher want or can afford exception handling nor program termniation :?: | ||
<code c> | <code c> | ||
- | /* assert.h */ | + | int main(int argc, char **argv){ |
- | #define check(expr) (__check((expr), #expr, __FILE__, __LINE__)) | + | char * progName= argv[0]; // Filename of programm itself |
- | int __check(int expVal, const char *expStr, const char *file, unsigned line); | + | if (argc >= 2) char * firstArg= argv[1]; // First Argument |
- | + | ||
- | /* assert.c */ | + | |
- | int __check(int exp, const char *expS, const char *file, unsigned int line) { | + | |
- | if (!exp) fprintf( | + | |
- | stderr, | + | |
- | "\n!!!Check failed:\n expression: %s\n file: %s\n line: %u\n", | + | |
- | expS, file, line | + | |
- | ); | + | |
- | return exp; | + | |
} | } | ||
- | </code> | ||
- | Handle exceptions with the tips below: | + | int main(void){} |
- | + | // is also possible (Carm p.416) | |
- | ==== Error Handling in Functions == | + | // don't omit void! (Carm p.278) |
- | + | ||
- | For error handling its acceptable to use ''goto''. Thus the indention level isn't increased by error handling: | + | |
- | <code c> | + | |
- | int addConfigValue(const char *key, int value) { | + | |
- | int err= 0; // always initalize because of function of type fopen()! | + | |
- | FILE file; | + | |
- | + | ||
- | if (! file= fopen("values.conf")) { /* exemplary function call */ | + | |
- | err= FIRST_ERR; | + | |
- | goto do_cleanup; | + | |
- | } | + | |
- | + | ||
- | value += getValFromKey(key, &err); /* exemplary function call */ | + | |
- | if (err) { | + | |
- | err= SECOND_ERR; | + | |
- | goto do_cleanup; | + | |
- | } | + | |
- | + | ||
- | if (err= setValFromKey(key, value);) { /* exemplary function call */ | + | |
- | err= THIRD_ERR; | + | |
- | goto do_cleanup; /* not really necesarry but saver if something is added later */ | + | |
- | } | + | |
- | + | ||
- | do_cleanup: | + | |
- | /* clean up: close files, etc and print debug message */ | + | |
- | return err; | + | |
- | } | + | |
</code> | </code> | ||
- | ==== Error Reporting in Functions == | + | ===== C Program Structure == |
- | This rules only apply if the function is stand-alone i.e. it is not an instance method of a class. | + | {{page>becki/my/linux/design_blog/2017-01-20_c_program_structure&noheader}} |
- | If possible use always ''int'' as error variable. If the function... | + | ===== Position of Pointer Sign ====== |
- | * doesn't need to return anything, use int as return value as error indicator | + | |
- | * 0 means ok | + | |
- | * positive values mean an error happend, the return value is the error code | + | |
- | * returns a pointer, than returning null indicates error (like fopen() and malloc()). To get the exact reason for the error a pointer to int as error indicator can passed as argument. | + | |
- | * returns only positive values or 0 (e.g. an array index) than negative values indicate error | + | |
- | * return anything else, a pointer to int as error indicator can passed as argument | + | |
- | ==== Error Reporting in Classes or Librarys == | + | Some investigations: |
- | + | ||
- | FIXME Needs reworking | + | |
- | + | ||
- | === Error Member Variable in Classes == | + | |
- | + | ||
- | If the function is an instance method of a class, the error code should be written to an instance variable of type int (eg this->err), so that the return value remains alwas free for other informations. To get an integrated error handling in the class, all instance methods shall use this mechanism rather than many different mechanisms mentioned above :!: | + | |
- | + | ||
- | FIXME Verify: Keep in mind that all public methods eighter have to set this->err to 0 or check this->err at first, because otherwise the method may return falsely with an err code which did not happen at all! | + | |
- | + | ||
- | Proposal: Favor resetting over checking this->err because: --- 2007-10-23 | + | |
- | * There is less code and runtime overhead | + | |
- | * Client code is responsible for correct err handling like in standard C | + | |
- | + | ||
- | === Error Message Array == | + | |
- | + | ||
- | Have a central place for error codes and error texts. Public functions must use these error codes to indicate errors for client code: | + | |
<code c> | <code c> | ||
- | /* json.h */ | + | int main(int argc, char* argv[]) // Stroustrup p.126 |
- | enum Json_ErrorCodes { | + | int main(int argc, char* argv[]) // Josuttis p.21 |
- | JSONERR_OK, | + | int main(int argc, char* argv[], char *env[]) // (!) Breymann p.216 |
- | JSONERR_WRONG_TYPE, | + | int main(int argc, char *argv[]) // glibc reference |
- | JSONERR_CREATESTRING, | + | int main(int argc, char *argv[]) // Kernighan / Ritchie |
- | JSONERR_FROM_LIB_A, /* An undocumented error (errmsg is missing) */ | + | int main(int argc, char *argv[]) // Harbison / Steele |
- | JSONERR_FROM_LIB_B, /* An undocumented error (errmsg is missing) */ | + | int main(int argc, char **argv) // gtk reference |
- | JSONERR_ERRCNT, /* To get the number of error codes */ | + | int main(int argc, char **argv) // qt reference |
- | }; | + | int main(int argc, char **argv) // fluxbox source |
- | + | ||
- | /* json.c */ | + | |
- | static const char *errmsgs[]= { | + | |
- | NULL, | + | |
- | "Element %u is not a JsonValue of type %s!", | + | |
- | "String creation failed!", | + | |
- | NULL, | + | |
- | NULL | + | |
- | } | + | |
- | /* #define ERRMSG_CNT (sizeof(errmsgs)/sizeof(errmsgs[0])) obsolete use errcnt */ | + | |
</code> | </code> | ||
- | FIXME Becaus of inheritance count of error codes and size of error messages must match! | + | -> I use |
- | + | <code c>char *cp</code> | |
- | During development, when the centralized array with the error messages is still missing, the error reporter function falls back to just printing a default message like: | + | because |
- | Json ERROR: json.c 33: Code 4 | + | - it seems to be more common |
- | at ... | + | - declarations like "int a, *ap;" are possible |
- | + | ||
- | === Error Types == | + | |
- | + | ||
- | We distinguish between //primary errors// and //follow-up errors//. | + | |
- | + | ||
- | A primary error typically results from a failed logical test, eg. if the value of a variable isn't within a certain range. The function always aborts after a primary error. //All// primary errors must be reported. | + | |
- | + | ||
- | A follow-up error results from a failed call to a function of the same or another class. The function may abort after a follow-up error or handle that error (see below). But if the function aborts, it must be reported. | + | |
- | + | ||
- | Primary errors are reported with an error text, the file name and the line number where they occured. The centralized error codes are always used. Errors may reported directly where they occor or centralized at the end of the function (see above). | + | |
- | + | ||
- | If a follow-up error is not handled (i.e. function aborts), it will also be reported, but indented. Only file name and line number is printed. Example: | + | |
- | + | ||
- | ArrayList ERROR: Index out of range! <== primary error | + | |
- | at arrayList.c:69 <== follow-up errors | + | |
- | at json.c:137 | + | |
- | at json.c:267 | + | |
- | + | ||
- | + | ||
- | === Follow-up Errors == | + | |
- | == Error code == | + | |
- | + | ||
- | If a function must be aborted because of a follow-up error, the error code of the called function will //not// be returned. Instead one error code of the own lib or class, is returned (exception translation). That way the error codes of different classes and libs are independent from each other. Typically every used class get only one error code in the client class. | + | |
- | + | ||
- | This also means that a class normally has //more error codes than error messages//. If this is the case, the error message array has to be filled with NULL-Pointers where messages are missing. This is necessary because //with subclass usage// (see below) the size of the array must be the same as the error message count. | + | |
- | + | ||
- | If the follwow-up error results from a call to a function of same class or lib, the set error code is not touched. It code is only set once, the error text is only reported once, directly where the true error happens. The callers of the function where the error occured just report the line number and file of the funciton call. | + | |
- | + | ||
- | == Delegate Error Report to called Function == | + | |
- | + | ||
- | This is the default. A follow up is reported as described above (indendet, with filename and line number, but without error message). The error message comes from the called function. | + | |
- | + | ||
- | == Handle == | + | |
- | + | ||
- | DELETEME If a function of a client class C does not abort after a follow-up error e.g. library class L it handles these error. In this case the error reporting of L mutst be switched of completly. | + | |
- | + | ||
- | But this also means, that //all// follow up errors of L must be eighter handled or reported as primary error in C | + | |
- | + | ||
- | To switch off error reporting of class L, L must provide a function for that, like ''L_setVerbosity()''. In the body of this function all error reporting in classes that L on his part uses, must also switched of. | + | |
- | + | ||
- | === Sublasses == | + | |
- | + | ||
- | If class B is derived form class A, than b also uses the Diagnose member of class A insted of creatin an own. Class B does this by adding it's custom error messages to the Diagnose member of class A. | + | |
- | + | ||
- | === Summary == | + | |
- | + | ||
- | - FIXME | + | |
- | + | ||
- | ==== Further Proposals and Ideas == | + | |
- | + | ||
- | [[http://library.gnome.org/devel/glib/2.22/glib-Error-Reporting.html|Error handling in Glib]] | + | |
- | + | ||
- | === dependend (nested) errors == | + | |
- | + | ||
- | Suppress the output of deeper nested primary errors as soon as less deeper nested code calls itself the primary error reporter function ''Diag_err()'' | + | |
- | + | ||
- | For example if an item is searched in an container by the help of the method of the container class, the error messages of the container are irrelevant. It schould just be reported that the item wasn't found in the client code, not in the container class. The analogy in Java would be a catched exception which isn't reported neither. | + | |
- | + | ||
- | This could be done by adding all error messages to a char array. Each call to the report function of a primary error empties that array. The array must be secured against overflow (a growable container is too expensive) and reporting the content of the array needs an extra function call (at the end of main() :?:). | + | |
- | + | ||
- | FIXME Think about: Make an error class which contains an int code, a char[80] message and a pointer to the previous error object. No stack, no heap necessary??? Problem: all objects must still exist, when err chain is reported --- 2008-01-10 | + | |
===== Loops == | ===== Loops == | ||
Zeile 199: | Zeile 66: | ||
statement | statement | ||
</code> | </code> | ||
- | ist according to "The C programming language" p.59 the same as: | + | |
+ | is the same as((The C programming language p.59)): | ||
<code c> | <code c> | ||
expr1; | expr1; | ||
Zeile 208: | Zeile 77: | ||
</code> | </code> | ||
- | ===== CLI args and main() == | + | ===== Standard In- and Output == |
+ | ==== stdin == | ||
<code c> | <code c> | ||
- | int main(int argc, char **argv){ | + | /* Read a character from stdin: */ |
- | char * progName= argv[0]; // Filename of programm itself | + | int i= getchar(); |
- | if (argc >= 2) char * firstArg= argv[1]; // First Argument | + | |
- | } | + | |
- | + | ||
- | int main(void){} | + | |
- | // is also possible (Carm p.416) | + | |
- | // don't omit void! (Carm p.278) | + | |
</code> | </code> | ||
- | ===== Position of Pointer Sign ====== | ||
- | Some investigations: | ||
<code c> | <code c> | ||
- | int main(int argc, char* argv[]) // Stroustrup p.126 | + | /* Read stdin line by line into the array lin: */ |
- | int main(int argc, char* argv[]) // Josuttis p.21 | + | char lin[MAX_LINE_LEN]; |
- | int main(int argc, char* argv[], char *env[]) // (!) Breymann p.216 | + | while (fgets(lin, MAX_LINE_LEN, stdin)) {/* do some thing */} |
- | int main(int argc, char *argv[]) // glibc reference | + | |
- | int main(int argc, char *argv[]) // Kernighan / Ritchie | + | |
- | int main(int argc, char *argv[]) // Harbison / Steele | + | |
- | int main(int argc, char **argv) // gtk reference | + | |
- | int main(int argc, char **argv) // qt reference | + | |
- | int main(int argc, char **argv) // fluxbox source | + | |
</code> | </code> | ||
- | -> I use | + | <note tip>If input line lenght is unknown, use ''getline()'' instead of ''fgets()''! |
- | <code c>char *cp</code> | + | (See ''freesigs/io_modbus'')</note> |
- | because | + | |
- | - it seems to be more common | + | |
- | - declarations like "int a, *ap;" are possible | + | |
- | ===== Standard In- and Output == | + | ''scanf'' can also be used for reading from ''stdin''. |
- | ==== stdin == | + | |
- | + | ||
- | <code c> | + | |
- | char lin[MAX_LINE_LEN]; | + | |
- | while (fgets(lin, MAX_LINE_LEN, stdin)) {/* do some thing */} | + | |
- | /* Reads stdin line by line into the array lin */ | + | |
- | </code> | + | |
==== stdout, stderr == | ==== stdout, stderr == | ||
<code c> | <code c> | ||
- | printf("%s=%u\n", key, value); // writing to stdout | + | /* Write a character to stdout and stderr: */ |
- | fprintf(stderr, "%s=%u\n", key, value); // writing to stderr | + | int i= 'c'; |
+ | putchar(i); | ||
+ | putc(i, stderr): | ||
</code> | </code> | ||
- | ==== Colorizing stdout == | ||
- | |||
- | Just an example: | ||
<code c> | <code c> | ||
- | printf("\033[32mgreen text\033[0m"); | + | /* Write a line to stdout and stderr: */ |
- | printf("\033[1;31mbold red text\033[0m"); | + | printf("%s=%u\n", key, value); // writing to stdout (buffered) |
+ | fprintf(stderr, "%s=%u\n", key, value); // writing to stderr (unbuffered) | ||
</code> | </code> | ||
- | See ''color_indicator[]'' in sourcecode of ''ls'', [[http://ldp.bootet.net/LDP/abs/html/colorizing.html|ABS Guide]] and C&R S.38 | + | |
+ | Writing to ''stderr'' is unbuffered, writing to ''stdout'' is buffered ((CARM p.351)) | ||
+ | ⇒ Always use ''stderr'' for debugging messages ((RRUSP p.68)) | ||
===== Constants ====== | ===== Constants ====== | ||
Zeile 373: | Zeile 221: | ||
[static] const char *mystring= "String" // false, needs 6 Bytes + Pointer | [static] const char *mystring= "String" // false, needs 6 Bytes + Pointer | ||
</code> | </code> | ||
+ | |||
* ''static'' to limit the scope for global strings. Not necessary inside a code block. | * ''static'' to limit the scope for global strings. Not necessary inside a code block. | ||
* see also [[http://www.dclc-faq.de/kap2.htm|de.comp.lang.c FAQ]] -> Frage 2.2 / K&R p.101 / Carm p.124 | * see also [[http://www.dclc-faq.de/kap2.htm|de.comp.lang.c FAQ]] -> Frage 2.2 / K&R p.101 / Carm p.124 | ||
+ | * //Writable// strings must be declared as array (eg ''char a[]= "aff";'') not as pointer. See Carm p 32 | ||
=== Detailed Description == | === Detailed Description == | ||
Zeile 531: | Zeile 381: | ||
- Always use ''int'' (also when int is way too big or the value will never be never negative, eg. counter variable in for loops), unless one or more of the following exceptions apply: | - Always use ''int'' (also when int is way too big or the value will never be never negative, eg. counter variable in for loops), unless one or more of the following exceptions apply: | ||
- | - FIXME Test this: Use ''unsigned int'' as parameter type in function declarations when [[http://www.dclc-faq.de/kap10.htm|only positive values are allowed]]. This saves you from the overhead of testing for nonnegative values. | + | - Use ''unsigned int'' when [[http://www.dclc-faq.de/kap10.htm|only positive values are allowed]]. This saves you from the overhead of testing for negative values in function arguments and returned values from functions. |
- Use ''unsigned'' types when bit operations are performed (unsigned types with proper size recommended) | - Use ''unsigned'' types when bit operations are performed (unsigned types with proper size recommended) | ||
- Use another, smaller type when space is relevant (eg in ''struct'' definitions or large arrays) | - Use another, smaller type when space is relevant (eg in ''struct'' definitions or large arrays) | ||
- Use a bigger type when the space in ''int'' is not sufficient | - Use a bigger type when the space in ''int'' is not sufficient | ||
+ | |||
+ | ===== Private Members in structs == | ||
+ | |||
+ | Any pointer type may be an incomplete type. This can be used to get private members in an object: | ||
+ | |||
+ | ==== Library Code == | ||
+ | |||
+ | <code c> | ||
+ | /* file: point-private.h */ | ||
+ | |||
+ | struct point { | ||
+ | int x; | ||
+ | int y; | ||
+ | }; | ||
+ | </code> | ||
+ | |||
+ | <code c> | ||
+ | /* file: point.c */ | ||
+ | #include <stdlib.h> | ||
+ | #include "point-private.h" | ||
+ | |||
+ | struct point * point_new(int x, int y) { | ||
+ | struct point *p= malloc(sizeof(struct point)); | ||
+ | p->x= x; | ||
+ | p->y= y; | ||
+ | return p; | ||
+ | } | ||
+ | |||
+ | int point_get_x(struct point *this) { | ||
+ | return this->x; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | <code c> | ||
+ | /* file: point.h */ | ||
+ | |||
+ | struct point * point_new(int x, int y); | ||
+ | |||
+ | int point_get_x(struct point *this); | ||
+ | </code> | ||
+ | |||
+ | ==== Client Code == | ||
+ | |||
+ | <code c> | ||
+ | #include <stdio.h> | ||
+ | #include "point.h" | ||
+ | |||
+ | int main(void) { | ||
+ | struct point *p = point_new(7,4); | ||
+ | |||
+ | printf("x=%d\n", point_get_x(p)); | ||
+ | //printf("x=%d\n", p->x); // Error: incomplete type | ||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | </code> | ||
===== Unsorted Tips == | ===== Unsorted Tips == | ||
* Use ''stdint.h''. It offers exact length types; smallest types of at least given length and most efficient types | * Use ''stdint.h''. It offers exact length types; smallest types of at least given length and most efficient types | ||
- | * [[http://library.gnome.org/devel/glib/stable/|Glib]] is to C what the STL is for C++ or the Platform API for Java. IBM has an [[http://www.ibm.com/developerworks/linux/library/l-glib.html|intro]] | + | * Use ''limits.h'' to get the ranges of integer types ((CARM p.112)) |
+ | * [[http://library.gnome.org/devel/glib/stable/|Glib]] is to C what the STL is for C++ or the Platform API for Java. IBM has an intro: [[http://www.ibm.com/developerworks/linux/library/l-glib.html|The wonders of GLib]] (currently broken --- 2010-10-20) | ||
* Buffer for strings: Define a constant for the maximal stringlen eg: ''#define MAXFOOLEN 80'' and reserve memory with one byte more for the 0-terminator: ''char sbf[MAXFOOLEN+1];'' | * Buffer for strings: Define a constant for the maximal stringlen eg: ''#define MAXFOOLEN 80'' and reserve memory with one byte more for the 0-terminator: ''char sbf[MAXFOOLEN+1];'' | ||
* :?: Test: Include header files which are necessary for the interface in ''foo.h''. Include header files necessary only for the implementation in the ''foo.c''. The dependencies for the makefile is the sum of both. | * :?: Test: Include header files which are necessary for the interface in ''foo.h''. Include header files necessary only for the implementation in the ''foo.c''. The dependencies for the makefile is the sum of both. |