24.1.1 The C Language Standard
A C Language Standard, such as ISO/IEC 9899:1999 Standard (C99), has to reconcile two opposing goals: freedom for compilers vendors to target new devices and improve code generation, against the known functional operation of source code for programmers. If both goals can be met, source code can be made portable.
The C Language Standards are implemented as a set of rules that detail not only the syntax that a conforming C program must follow, but the semantic rules by which that program will be interpreted. Thus, for a compiler to conform to the standard, it must ensure that a conforming C program functions as described by the standard.
Language Standards describe implementation as the set of tools and the runtime environment on which the code will run. If any of these change; for example, you build for and run on a different target device, or if you update the version of the compiler you use to build, then you are using a different implementation.
The standards uses the term behavior to mean the external appearance or action of the program. This has nothing to do with how a program is encoded.
Since the Language Standard is trying to achieve goals that could be construed as
conflicting, some specifications appear somewhat vague. For example, C standards states
that an int
type must be able to hold at least a 16-bit value, but it
does not go as far as saying what the size of an int
actually is; and
the action of right-shifting a signed integer can produce different results on different
implementations; yet these different results are still compliant with the standard.
If a standard is too strict, device architectures cannot allow the compiler to conform. But if it is too weak, programmers would see wildly differing results within different compilers and architectures, causing the standard to lose its effectiveness.
For example, the mid-range PIC® microcontrollers do not have a data stack. Because a compiler targeting this device cannot implement recursion, it (strictly speaking) cannot conform to the C Language Standard. This example illustrates a situation in which the standard is too strict for mid-range devices and tools.
Implementation-defined behavior | This is unspecified behavior in which each implementation documents how the choice is made. |
Unspecified behavior | The standard provides two or more possibilities and imposes no further requirements on which possibility is chosen in any particular instance. |
Undefined behavior | This is behavior for which the standard imposes no requirements. |
Code that strictly conforms to a Language Standard does not produce output that is
dependent on any unspecified, undefined, or implementation-defined behavior. The size of
an int
, which was used as an example earlier, falls into the category
of behavior that is defined by implementation. That is to say, the size of an
int
is defined by which compiler is being used, how that compiler
is being used, and the device that is being targeted.
The MPLAB XC compilers are freestanding implementations that conform to the ISO/IEC 9899:1990 Standard (referred to as the C90 standard) as well the ISO/IEC 9899:1999 Standard (C99) for programming languages, unless otherwise stated.
For freestanding implementations (or for what are typically called embedded applications), the standard allows non-standard extensions to the language, but obviously does not enforce how they are specified or how they work. When working so closely to the device hardware, a programmer needs a means of specifying device setup and interrupts, as well as utilizing the often complex world of small-device memory architectures. This cannot be offered by the standard in a consistent way.
While the C Language Standards provides a mutual understanding for programmers and compiler vendors, programmers need to consider the implementation-defined behavior of their tools and the probability that they may need to use extensions to the C language that are non-standard. Both of these circumstances can have an impact on code portability.