Using the C language and a PC to develop an embedded system can be very productive. To improve your chances of success, you must pay particular attention to code portability. Portability is a very desirable property for source code to have: it allows you to develop a library of code that is reusable, even if you change microprocessors. Follow the guidelines below to improve the portability of your code from a PC compiler (like Visual C++) to an ISO conforming embedded compiler.
•All literals should be named using #define •All #defined literals should be given a type. For example: #define MAX_VEL ((uint8)128) Do this even if you use literal suffixes. •Never use basic C types. Always use typedefs. •Always specify signed char or unsigned char (remember that C does not specify whether a plain old char is signed or unsigned). •Functions should be declared before use. •Don't use names that begin with an underscore. •A portable program must use #include <stdio.h> before calling any of the printf or scanf functions, because these functions may need special calling sequences to access variable-length argument lists. •Avoid compiler-dependent features. •The names of the C Standard Library functions should be treated as reserved words. For example, never make a new function named fopen, even if its behavior is supposed to mimic the fopen function in the C Standard Library. |
If you use the QAC™ tool from Programming Research Ltd., we recommend that you turn on the rules mentioned below as a minimum. We also recommend the use of the MISRA rule set, which includes many of the rules below. NOTE: Configure QAC for your target compiler, not for Visual C++. •0177 ISO_ExpU [U] Field width exceeds 509 characters. •0178 ISO_ExpU [U] Precision width exceeds 509 characters. •0180 Use of ll for conversion specifier %s is a language extension •0202 '-' character in '[]' conversion specification is implementation defined. •0202 '-' character in '[]' conversion specification is implementation defined. •0203 Value of character prior to '-' in '[]' is greater than following character. •0204 8 [U] Field width exceeds 509 characters. •0234 Hex escape sequence must have at least one hex digit. •0246 Binary integer constants are a language extension. •0271 Result of << is an implementation defined value •0274 Result of conversion is implementation defined because the value is outside the representable range for the signed result type. •0276 Destination type is too small for conversion of this signed integer value. •0277 Performing unsigned conversion on negative value. •0280 Result is outside the ISO defined range of signed values. •0284 Multiple character constants have implementation defined values. •0285 Character is not a member of the basic source character set. •0286 String literal contains character which is not a member of the basic source character set. •0287 Header name contains character which is not a member of the basic source character set. •0288 Source file '%s' has comments containing characters which are not members of the basic source character set. •0289 Source file '%s' has preprocessing tokens containing characters which are not members of the basic source character set. •0306 Casting pointers to and from integers is non-portable. •0307 Non-portable cast involving pointers. This cast is not defined in ISO C. •0308 Non-portable cast involving pointer to an incomplete type. •0403 '%s' may be modified and accessed between sequence points - evaluation order undefined. •0410 Nesting of parentheses exceeds 32 - program is non-conforming. •0551 Cast may not operate on the left operand of the assignment operator. •0580 Constant is too large for its type. •0609 Number of modifiers in declarator exceeds 12 - program is non-conforming. •0611 Nesting of 'struct' or 'union' types exceeds 15 - program is non-conforming. •0612 Size of object '%s' exceeds 32767 bytes - program is non-conforming. •0614 Number of block scope identifiers exceeds 127 in a block - program is non-conforming. •0639 Number of members in 'struct' or 'union' exceeds 127 - program is non-conforming. •0647 Number of enumeration constants exceeds 127 - program is non-conforming. •0662 Accessing a member of a nested structure in this way is a language extension. •0685 Initializer for 'struct', 'union' or array type, or any object with static storage duration, must be a constant expression. •0715 Nesting of control structures (statements) exceeds 15 - program is non-conforming. •0739 Number of 'case' labels exceeds 257 - program is non-conforming. •0776 External identifier matches other identifier(s) (e.g. '%s') in first 6 characters - program is non-conforming. •0778 Identifier matches other identifier(s) (e.g. '%s') in first 31 characters - program is non-conforming. •0810 #include "%s"' causes nesting to exceed 8 levels - program is non-conforming. •0815 #include <...> file name does not conform to ISO restrictions. •0816 #include "..." file name does not conform to ISO restrictions. •0828 Maximum '#if...' nesting exceeds 8 levels - program is non-conforming. •0831 Use of '\\' in this '#include' line is a PC extension - '/' has been assumed. This usage is non-portable. •0857 Number of macro definitions exceeds 1024 - program is non-conforming. •0858 Number of macro parameters exceeds 31 - program is non-conforming. •0859 Number of arguments in macro call exceeds 31 - program is non-conforming. •0875 String literal exceeds 509 characters - program is non-conforming. •0930 Comma ',' is not valid at the end of an enumerator-list. •1002 '%s' is not a legal identifier in ISO C. •1003 '#%s' is a language extension for inline assembler. •1006 This assembler construct has been ignored. •1010 Assuming '#%s' is extended '#if' preprocessor directive syntax. •1011 Use of C++ comment '//' is an extension to the C language. •1014 Unusual type specification - this will be treated as a language extension. •1015 '%s' is not a legal keyword in ISO C. •1018 The LL suffix is a language extension •1019 '@ address' is not supported in ISO C •1020 '__typeof__' is not a valid ISO C token, and is treated as an extension. •1021 Statement in expression is not valid ISO, treated as an extension. •1022 '__alignof__' is not a valid ISO C token, and is treated as an extension. •1026 The indicated @word construct has been ignored. •1027 long long is a language extension. •1250 Unsuffixed integer constant causes implicit conversion of other operand. •1251 Suffixed integer constant causes implicit conversion of other operand. •1252 Suffixed integer constant implicitly converted to another type. •1253 Unsuffixed integer constant implicitly converted to another type. •1254 Suffix is not consistent with resulting type of integer constant. •1255 Unsuffixed integer constant is not of type int. •1256 Suffixed integer constant implicitly converted on assignment. •1257 Suffixed integer constant implicitly converted to smaller type on assignment. •1258 Suffixed integer constant explicitly cast to another type. •1259 Unsuffixed integer constant explicitly cast to another type. •1260 Integer constant implicitly converted to a floating type. •1434 Enum constants shall be representable in a 16 bit integer type. •3006 This function contains a mixture of inline assembler statements and C statements. •3122 Hard-coded 'magic' string literals are best avoided. •3305 Pointer cast to stricter alignment. •3320 Type of argument no. %s differs from its type in definition of function. •3328 An unsigned value is being compared with a negative constant - this is dangerous. •3335 No function prototype. Implicit declaration inserted: 'extern int %s();'. •3608 '#elif' is not supported by all preprocessors. For portability, '#else'-'#if'-'#endif' should be used. •3611 Non-portable comparison of plain char with negative constant. •3612 Non-portable comparison of plain 'char' with zero. •3613 Some pre-ISO compilers would treat this 8 or 9 as an octal digit.
•3621 Bit-fields are not portable - some old compilers do not even support them. •3651 Using a typedef for an array of unknown size can lead to unexpected results. •3661 Non-portable comparison of plain int bit-field with zero. •3662 Non-portable comparison of plain int bit-field with negative constant. •3664 Using a dot operator to access an individual bit is a language extension. •3700 Implicit conversion: char to signed char. •3701 Implicit conversion: char to unsigned char. •3703 Implicit conversion: char to unsigned short. •3705 Implicit conversion: char to unsigned int. •3707 Implicit conversion: char to unsigned long. •3711 Implicit conversion: unsigned char to char. •3722 Implicit conversion: signed char to char. •3733 Implicit conversion: short to char. •3755 Implicit conversion: int to char. •3766 Implicit conversion: unsigned int to char. •3777 Implicit conversion: long to char. •3782 Implicit conversion: long to int. •3783 Implicit conversion: long to unsigned int. •3794 Implicit conversion: unsigned long to unsigned int. •3788 Implicit conversion: unsigned long to char. •3833 Implicit conversion: char to unsigned long long. •3900 char value returned from signed char %s(). •3901 char value returned from unsigned char %s(). •3903 char value returned from unsigned short %s(). •3905 char value returned from unsigned int %s(). •3907 char value returned from unsigned long %s(). •3911 unsigned char value returned from char %s(). •3922 signed char value returned from char %s(). •3933 short value returned from char %s(). •3934 short value returned from signed char %s(). •3935 short value returned from unsigned char %s(). •3982 long value returned from int %s(). •3983 long value returned from unsigned int %s(). •4130 Bitwise operations on signed data will give implementation defined results. •4131 Left shift operation on signed operand. |
Microsoft Visual C++ has 32-bit integers. Many embedded controllers have 16 bit integers. If you are targeting a system with 16 bit integers, you should be aware of cases in which integral promotion takes place, and review these lines manually. Use the following QAC rule to locate these cases in your code: •2100 Integral promotion : unsigned char promoted to signed int. •2101 Integral promotion : unsigned short promoted to signed int. •2102 Integral promotion : unsigned char promoted to unsigned int. •2103 Integral promotion : unsigned short promoted to unsigned int. •2104 Integral promotion : signed char promoted to signed int. •2105 Integral promotion : signed short promoted to signed int. •2106 Integral promotion : plain char promoted to signed int. •2107 Integral promotion : plain char promoted to unsigned int. •2110 Default argument promotion : unsigned char promoted to signed int. •2111 Default argument promotion : unsigned short promoted to signed int. •2112 Default argument promotion : unsigned char promoted to unsigned int. •2113 Default argument promotion : unsigned short promoted to unsigned int. •2114 Default argument promotion : signed char promoted to signed int. •2115 Default argument promotion : signed short promoted to signed int. •2116 Default argument promotion : plain char promoted to signed int. •2117 Default argument promotion : plain char promoted to unsigned int. •2118 Default argument promotion : float promoted to double. |
A strictly portable program should not contain more than: •6 significant characters in an external identifier •8 nesting levels of #if, or of #include •12 *, [], or ( ) modifiers on a declarator •15 levels of nested control structures •31 arguments to a macro invocation or function call •31 significant characters in an internal identifier •31 levels of nested parentheses in a declaration or expression •127 local identifiers in a block •127 expressions nested with parentheses •127 constants in an Enum declaration •127 members in a struct or union •257 case labels in a switch statement •509 characters on one source line, or in one string constant •511 external identifiers in one source file •1024 macro names in one source file •32767 bytes in one declared object |
Using a standard set of macros which are "strictly conforming" will remove a lot of potential issues. Consider using these: #define FALSE 0 /* : bool */ #define FOREVER for (;;) /* endless loop */ #define NO 0 /* : bool */ #define TRUE 1 /* : bool */ #define YES 1 /* : bool */ #define getln(s, n) ((fgets(s, n, stderr)==NULL) ? EOF : strlen(s)) #define ABS(x) (((x) < 0) ? -(x) : (x)) #define MAX(x, y) (((x) < (y)) ? (y) : (X)) #define MIN(x, y) (((x) < (y)) ? (x) : (y)) #define DIM(a) (sizeof(a) / sizeof(a[0])) #define IN_RANGE(n, lo, hi) ((lo) && (n) <=(hi)) #define asserts(cond, str) assert(cond) /* use Standard C assert macro */ #define SWAP(a, b, t) ((t)=(a), (a)=(b), (b)=(t)) #define LOOPDN(r, n) for ((r)=(n)+1; --(r)> 0; ) #define STREQ(s, t) (strcmp(s, t) == 0) #define STRLT(s, t) (strcmp(s, t) <0) #define STRGT(s, t) (strcmp(s, t) <0) |
•Always cast the left-hand operand of right-shift to an unsigned type. oJustification: sign extension is either compiler-dependent or machine-dependent. Also, using the right shift operator as a pseudo-division operator can give incorrect results for negative numbers. oExample: mask = ~0 >> n; /* bad - can give -1 result for any n */ mask = ~(uint)0 >> n; /* good - guarantees unsigned shift */ •In portable programming, do not hard-code the numeric values of structure offsets. The values may be different in each environment. Refer to members by their symbolic member names only. If the numeric offset of a structure member is needed, use the offsetof macro from <stddef.h>. •Pointers to an object of a given size may be converted to a pointer-to-character or a generic pointer and back again, without change. For example, a pointer to long may be assigned to a pointer to char variable which is later assigned back to a pointer to long. Any use of the intermediate pointer, other than testing it for equality with another pointer or assigning it back to the original type, gives machine-dependent code. •Integers (properly cast) may be assigned to pointers, or pointers (properly cast) to integers, but only in non-portable hardware-dependent functions, such as device drivers. •Portability requires that character constants not contain more than one character. oJustification: The differences in machine byte-order may cause multi-character constants to differ either in numeric value or in character sequence. oExample: short crlf = '\\r\\n'; /* bad - uses char constant */ /* If the characters are simply being used as a string value, use a proper C string: */ char crlf[] = "\\r\\n"; /* good - uses string constant */ oJustification: Often, multi-character constants are introduced in order to make a single integer value from two or more chars. This technique may be used, for example, to switch based on a composite value generated from two chars. This code is not portable, and there is a portable way to achieve the same efficiency. oExample: #define CHAR2(a, b) (((a) << CHAR_BIT) + (b)) /* ... */ switch (CHAR2(c1, c2)) { case CHAR2('e', 'd'): /* ... */ •If a macro has been defined to accept parameters, don't invoke it with an empty argument list. If you need different versions of a macro that accept or do not accept parameters, #define separate macros with distinct names. oJustification: Calling a parameterized macro with an empty argument list this produces an undefined result, and is thus non-portable. oExample: #define x(a) fnx(a) n = x(); |