Journal Articles
Browse in : |
All
> Journals
> CVu
> 012
(9)
All > Topics > Programming (877) Any of these categories - All of these categories |
Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.
Title: Everything You Wanted To Know About C ......
Author: Martin Moene
Date: 20 June 2010 08:58:00 +01:00 or Sun, 20 June 2010 08:58:00 +01:00
Summary: Tips and Tricks from Steven Palmer
Body:
The following tips and tricks were collected together after a study of some potential problems encountered by beginners to C. Space does not permit me to include many other, similiarily useful, tips. If you have any to contribute, please send them to the editor.
1. Constants
All integer literal constants are stored as integer, unless you supply the L suffix. Beware of calling a function that expects a argument of type long using a literal. For example
lseek(fh, 0, SEEK_BEG);
may not work because lseek() expects the second argument, the offset, to be a long integer. It should be re-written as
lseek(fh, 0L, SEEK_BEG);
All floating-point literal constants are stored as double, rather than just plain float. So the following
float count; ... count = 8.95;
will elicit a 'Data Conversion' warning from some compilers because the 8.95 has type double which has to be converted down to float. If you feel picky about minor warnings, you can get round this by casting the literal
count = (float)8.95;
Again, beware of calling functions with literals if they are expecting an argument of type float. All mathematical libraries tend to use arguments of type double as a sort of safety catch. You should also #include <math.h> so that the compiler can catch and warn about unintended conversions.
2. Operator Precedence
You have probably come across the case where assignments inside a conditional, such as
while ((c = getchar()) != ' ')have to be bracketed, otherwise they will be interpreted by the compiler as
while (c = (getchar() != ' '))Take some time to learn the C operator precedence levels. There are some other catches for the unwary. For example
if (p & 0xC0 == 0xC0)does NOT mean what it seems. The == operator has higher precedence than the &, so the expression must be re-written as
if ((p & 0xC0) == 0xC0)
A good C compiler should catch the former and warn the user about possible missing parenthesis.
3. #define macros
Except for simple macros, place the definition part of all #define preprocessor statements inside brackets. This avoids any ambiguity arising from apparently innocent usage such as the following
#define TABLE_BASE 0x2000 #define TABLE_LIMIT TABLE_BASE+0x1000 #define TABLE_SIZE TABLE_LIMIT-TABLE_BASE/sizeof(long)
TABLE_SIZE is definitely NOT set to the correct value. The macro will actually be interpreted as
0x2000+0x1000-0x2000/4
(assuming sizeof(long) is 4 bytes), or 0x2800. The second and third #defines must be rewritten as
#define TABLE_LIMIT (TABLE_BASE+0x1000) #define TABLE_SIZE (TABLE_LIMIT-TABLE_BASE)/sizeof(long)
If the macro takes any arguments, always place the arguments inside the definition in brackets.
#define isdigit(c) ((c) >= '0' && (c) <= '9')
Finally, never call a macro with an expression that modifies itself, or any other part of the expression in that same macro. For example, the following are very suspect
isdigit(*c++) isdigit(*c += 1) isdigit(getchar())
4. Type declarations
Complex type declarations, while they are fun to explore and show off in listings, are one of the biggest headaches in C maintainability. Beware that if you make use of complex declarations, your code may be difficult to port to another language, such as Pascal or Ada. However if this is not too much of an issue, you can sometimes create very sophiscated and efficent data structures this way. For example
typedef char (* fchr)[4]; fchr (*routine)(char *);
declares a pointer to a function that returns a pointer to a character array of four elements. It can be assigned an address by
fchr lookup(char *); ... routine = lookup;
and called by
fchr cptr; cptr = (*routine)("HELP")
On return, cptr will point to the start of a 4-element character array, and can be used to skip over those characters to the start of the next 4 characters using
++cptr;
If your compiler allows you to examine the generated object code in assembler form, you can get some idea of how much more efficent the object code is over a more straightforward approach.
5. Debugging
If you are unlucky enough not to be using a proper C development system which includes a source-code debugger, like C-trace or Codeview, you will probably be wasting time tracking down obscure bugs that a source-code debugger would track down within minutes. However, there are a few tricks that you can use to help debug your program without having to restort to a machine-code debugger. Here are some...
(1) Use the serial or printer port to route diagnostic messages. If you have a spare terminal, you can connect it to the serial port of your computer and direct all diagnostics so that they appear on the terminal. You can also use a printer, but be careful of placing the diagnostics inside large loops ... you can end up wasting a lot of good printer paper!
(2) Use assertions. Assertions allow you to check that a specific condition is true before you execute the next statement. If your compiler does not support assertions, you can use the following. Place it in a file called "assert.h" in your header directory
#ifdef DEBUG #define assert(n,e) {if (!(e)) { \ fprintf(stderr,"Assertion number %d failed!\n", (n)); \ exit(1); } } #else #define assert(n,e) #endif
Use it like this
/* Switch on assertions */ #define DEBUG #include <assert.h> ... /* Check that x is non-zero */ assert(1,x != 0); p = q/x; ...
If the assertion failed because x has the value 0, then the program will print the following and stop.
Assertion number 1 failed!
(3) Predefine all functions. By predefining functions, you are telling the compiler what types of arguments it expects and what type it returns. This
way, you will be warned if you accidently pass a floating-point value to a function that expects an integer, or try to use the result of a function that does not return a value. An example of a predefined function is
char *malloc(unsigned int);
This says that function malloc() takes one argument of type unsigned int, and returns a pointer to a character.
(4) LINT your source code, or use the highest possible warning level on your compiler. This ensures that the compiler will report on possible bugs that are normally ignored at the default warning level, on non-portable constructs or on wasteful code. LINT is a utility available under UNIX and some other operating systems that performs a very strict check of your source code.
(5) Use the C-Vu technical help section. If you are stuck with a really persistent bug, get in contact with either Martin Houston or Steven Palmer. If they can't help, then they'll do their best to find someone who can, or even contact the software house who developed your compiler to see if the bug could be a product of the compiler itself. That is what a user group is for!
Notes:
More fields may be available via dynamicdata ..