TI Compilers and Industry Standards
C and C++ standards
All TI compilers support:
- C Standard: ANSI X3.159-1989 (C89), which is the same as ISO/IEC 9899:1990.
- C++ Standard: ISO/IEC 14882:1998
TI performs extensive validation tests to ensure that the compiler generates correct ANSI C (C89) and C++ (1998) code.
C standard variants
See this Wikipedia page for a history of C variants.
The original ANSI standard is called "C89". This was adopted by ISO with minor revisions (mostly section number changes), and the ISO version is called "C90".
The so-called "C95" (actually C89 + Normative Addendum 1) adds just a few things to C89: iso646.h, digraphs, and a bunch of wide- and extended-character functions.
The C99 revision of the C standard adds many new features. See the Wikipedia entry.
Only C89 is officially supported, but we do provide some of these features as language extensions. Examples are the restrict keyword and the header file stdint.h from C99, and iso646.h from C95.
C++ standard variants
We do not support: C++ 2003, C++ TR1.
Variations from the C standard
There are a few points for which the TI compilers don't conform to the requirements for a hosted implementation. The TI implementation is not technically a hosted implementation, it is a freestanding implementation, for which the standard allows the implementation some leeway. However, the implementation is very close to meeting the requirements of a hosted implementation. The exceptions are shown below. C99 sections are cited because the C99 standard is easier to obtain and is usually substantively the same as C89.
This section does not list features which are temporarily broken due to bugs.
- Uninitialized static-storage-duration objects are not initialized to zero by the compiler. The user must do this explicitly in the linker. (Exceptions: ARM EABI and C6000 EABI zero-initialize all such objects.) Static storage duration objects are any global or local variables declared static. The standard says (C99 6.7.8 "Initialization", para 10) that if such objects are not explicitly initialized, they must be initialized to zero or NULL as appropriate. If an initializer for a struct or array doesn't have enough entries, the remaining uninitialized entries should be initialized as above. The TI compiler does not do this; this is left up to the user to perform in the linker command file with a fill value for sections representing initializers. This is not technically a violation of the C standard, since the linker is part of the implementation, and a method is provided for the user to get the job done, but this method is very unintuitive, and many consider it a violation. It is certainly an obstacle to ease of use.
- Some targets do not support a 64-bit double floating point type. (The C89 standard does not actually require exactly 64-bits, but that's pedantic.) Specifically, for C2700, C5400, C5500, and MSP430 double is only 32 bits, which is not in conformance with the standard (C99 18.104.22.168.2 "Characteristics of floating types <float.h>" para 8). C2800, C6000, ARM have a 64-bit double.
- Some targets have long long (an extension from C99), but not a conforming one. C99 requires at least 64 bits, but C2700 has 32-bit long long, and C5500 has 40-bit long long. C2800, C6000, and ARM have 64-bit long long, and C5400 and MSP430 do not support long long. This is not technically a violation of C89, since this is actually an extension, but if we start supporting C99, this would be a violation of C99 (C99 22.214.171.124.1 "Sizes of integer types <limits.h>" para 1).
Misunderstandings about TI C
There are quite a few things that TI compilers do a little bit differently than hosted compilers which cause confusion. The following things are legal in terms of the C standard, but are frequently mistaken for standards violations:
- char is 16 bits on C2700, C2800, C5400, and C5500. (Exception: C55x+ byte mode has 8-bit char.) Furthermore, the C standard uses the term byte to mean the minimum addressable unit in the implementation, which is char, which means a byte on these targets is 16 bits. This is in conflict with the widespread use of byte to mean 8 bits exactly. This is an unfortunate disagreement between C terminology and widespread industry terminology that TI can't do anything about. It's best to avoid the term byte on these targets.
- Having a 16-bit char can lead to unintuitive results like sizeof(long)==2 and sizeof(int)==1, but this is perfectly legal, and on these targets, is the best choice for type sizes based on the CPU arithmetic the targets can support.
- Plain char (that is, not signed char or unsigned char) is (by default) an unsigned type on ARM. This doesn't often cause problems, but it can. (example?)
- int is 16 bits on C2700, C2800, C5400, C5500, and MSP430. int is 32 bits on C6000 and ARM. This is far more likely to cause problems. First, it's not hard to overflow an int, and bad things can happen. Second, multiplication of two int operands can overflow if not written correctly. Third, int is a central type in the default integer promotion rules, expressions which work fine on a hosted implementation may overflow on one of these targets.
int a = 300; int b = 200; int c = a * b; /* oops, this overflows on 16-bit targets */ long d = a * b; /* oops, this overflows, too */ long e = (long)a * (long)b; /* correct version */
- long is
- 40 bits or 5 bytes for C6000 COFF. This is fully compliant with any major C/C++ standard as those standards are all defining a minimum requirement of 4 byte for long (aka. long int). Programmers are often falsely assuming this type having a size of exactly 4 bytes.
- 32 bits or 4 bytes for C6000 EABI/ELF. See the C6000 EABI migration page for a discussion of the impact when porting code that was coded with 40-bit long in mind but only sees 32-bit on the migration target platform.
- On targets where sizeof(char)==sizeof(int) (C2700, C2800, C5400, C5500), you still can't reliably use the return value of getc() to check for end of file, because 0xffff will be mistaken for the end of file. Use feof() instead.
Misunderstandings about C in general
For many more items like these, see comp.lang.c Frequently Asked Questions
- -4294967296 is not a signed number. -1 is not an integer constant, it is the unary negation of the integer constant 1. 4294967296 is too big to be a signed long (or int on C6000 or ARM) so it is considered an unsigned long (or unsigned int). The result type of unary negation of any unsigned value is still an unsigned value.
- The constant 1.0 is a double-precision constant. 1.0f is a single-precision constant.
- You must include the correct headers when calling library functions - especially math.h, stdio.h, and stdlib.h
- You must use volatile to declare any variable that might be modified by an interrupt, or other external event.
- You must use volatile to declare local variables if there is a setjmp in the function and you need the value to survive.
- getc() returns an int, not a char. This is because it can return EOF, which is an integer. On targets where char is 8 bits, if you mistakenly assign the return value of getc() to a char before testing the value, you will not be able to distinguish EOF from the valid data ((char)-1).
FILE *binary_data = fopen("data", "rb"); char c; while ((c = getc(binary_data)) != EOF) /* oops, 0xff in "data" will break this code */ do_something();
Other industry standards
The currently supported TI compilers use IEEE 754 format to store 32- and 64-bit floating point numbers, but do not implement every part of IEEE 754. The C89 standard does not even require IEEE format, and for C99, full IEEE 754 support (Inf, NaN, denormals) is optional.
See the Wikipedia page for more information about IEEE 754.
IEC61508, TÜV, MISRA-C
Additional industry specific tests such as IEC61508 safety tests, automotive TÜV safety tests, and MISRA-C are the responsibility of the user.