C89 Support in TI Compilers

From Texas Instruments Wiki
Jump to: navigation, search

C standard

The TI compilers for all devices support

  • C89 (ISO/IEC 9899:1990, also known as ANSI X3.159-1989)
  • C95 (actually C89 + NA1) adds just a few things to C89: iso646.h, digraphs, and a bunch of wide- and extended-character functions.
  • C99 (ISO/IEC 9899:1999). C99 support is incomplete.

The TI compiler does not support

  • C11 (ISO/IEC 9899:2011)

See also

  • C and C++ Language References. TI does not produce documentation for standard language features, as high-quality introductory and reference material is readily available.

External references

C language extensions

The TI compiler provides many language extensions not specified by the C standard.

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 when using COFF. The user must do this explicitly in the linker. (When using EABI, the compiler does zero-initialize all such objects. EABI is not available for C2000.) Static storage duration objects are global variables or local variables declared static. The standard says if such objects are not explicitly initialized, they must be initialized to zero or NULL as appropriate. 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 5.2.4.2.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 5.2.4.2.1 "Sizes of integer types <limits.h>" para 1).
  • Unsupported library functions: system, signal, ...

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();