(See alsoarithmetic types for the details on most built-in types andthe list of type-related utilities that are provided by the C library.)
Objects,functions, andexpressions have a property calledtype, which determines the interpretation of the binary value stored in an object or evaluated by the expression.
Contents |
The C type system consists of the following types:
| (since C23) |
| (since C99) |
| (since C23) |
| (since C99) |
| (since C23) |
| (since C99) |
(since C11) |
For every type listed above several qualified versions of its type may exist, corresponding to the combinations of one, two, or all three of theconst
,volatile
, andrestrict
qualifiers (where allowed by the qualifier's semantics).
Constructing a complete object type such that the number of bytes in its object representation is not representable in the typesize_t (i.e. the result type ofsizeof
operator), including forming such a VLA type at runtime,(since C99) is undefined behavior.
In a C program, the declarations referring to the same object or function indifferent translation units do not have to use the same type. They only have to use sufficiently similar types, formally known ascompatible types. Same applies to function calls and lvalue accesses; argument types must becompatible with parameter types and lvalue expression type must becompatible with the object type that is accessed.
The typesT
andU
are compatible, if
typedef
)
| (until C23) |
The typechar is not compatible withsignedchar and not compatible withunsignedchar.
If two declarations refer to the same object or function and do not use compatible types, the behavior of the program is undefined.
// Translation Unit 1struct S{int a;};externstruct S*x;// compatible with TU2's x, but not with TU3's x // Translation Unit 2struct S;externstruct S*x;// compatible with both x's // Translation Unit 3struct S{float a;};externstruct S*x;// compatible with TU2's x, but not with TU1's x // the behavior is undefined
// Translation Unit 1#include <stdio.h> struct s{int i;};// compatible with TU3's s, but not TU2'sexternstruct s x={0};// compatible with TU3's xexternvoid f(void);// compatible with TU2's f int main(){ f();return x.i;} // Translation Unit 2struct s{float f;};// compatible with TU4's s, but not TU1's sexternstruct s y={3.14};// compatible with TU4's yvoid f()// compatible with TU1's f{return;} // Translation Unit 3struct s{int i;};// compatible with TU1's s, but not TU2's sexternstruct s x;// compatible with TU1's x // Translation Unit 4struct s{float f;};// compatible with TU2's s, but not TU1's sexternstruct s y;// compatible with TU2's y // the behavior is well-defined: only multiple declarations// of objects and functions must have compatible types, not the types themselves
Note: C++ has no concept of compatible types. A C program that declares two types that are compatible but not identical in different translation units is not a valid C++ program.
A composite type can be constructed from two types that are compatible; it is a type thatis compatible with both of the two types and satisfies the following conditions:
| (since C99) |
| (until C23) |
These rules apply recursively to the types from which the two types are derived.
// Given the following two file scope declarations:int f(int(*)(),double(*)[3]);int f(int(*)(char*),double(*)[]);// C23: Error: conflicting types for 'f'// The resulting composite type for the function is:int f(int(*)(char*),double(*)[3]);
For an identifier with internal or externallinkage declared in a scope in which a prior declaration of that identifier is visible, if the prior declaration specifies internal or external linkage, the type of the identifier at the later declaration becomes the composite type.
An incomplete type is an object type that lacks sufficient information to determine the size of the objects of that type. An incomplete type may be completed at some point in the translation unit.
The following types are incomplete:
externchar a[];// the type of a is incomplete (this typically appears in a header)char a[10];// the type of a is now complete (this typically appears in a source file)
struct node{struct node* next;// struct node is incomplete at this point};// struct node is complete at this point
A type may have to be named in context other than thedeclaration. In these situations,type name is used, which is, grammatically, exactly the same as a list oftype-specifiers andtype-qualifiers, followed by thedeclarator (seedeclarations) as would be used to declare a single object or function of this type, except that the identifier is omitted:
int n;// declaration of an intsizeof(int);// use of type name int*a[3];// declaration of an array of 3 pointers to intsizeof(int*[3]);// use of type name int(*p)[3];// declaration of a pointer to array of 3 intsizeof(int(*)[3]);// use of type name int(*a)[*]// declaration of pointer to VLA (in a function parameter)sizeof(int(*)[*])// use of type name (in a function parameter) int*f(void);// declaration of functionsizeof(int*(void));// use of type name int(*p)(void);// declaration of pointer to functionsizeof(int(*)(void));// use of type name int(*const a[])(unsignedint, ...)={0};// array of pointers to functionssizeof(int(*const[])(unsignedint, ...));// use of type name
Except the redundant parentheses around the identifier are meaningful in a type-name and represent "function with no parameter specification":
int(n);// declares n of type intsizeof(int());// uses type "function returning int"
Type names are used in the following situations:
(since C99) | |
(since C11) |
A type name may introduce a new type:
void* p=(void*)(struct X{int i;}*)0;// type name "struct X {int i;}*" used in the cast expression// introduces the new type "struct X"struct X x={1};// struct X is now in scope
C++ documentation forType |