Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/MicrosoftDocs/cpp-docs/llms.txt

Use this file to discover all available pages before exploring further.

C provides a standard set of fundamental data types from which all other types are derived. In MSVC, every variable and function must be declared before it can be used; those declarations establish the type, storage class, and linkage of the named entity. You can also create new named types — called derived types — by basing them on types already defined in the language. This page covers the full type system available in Microsoft C, from primitive scalars through aggregates and user-defined type names.

Fundamental Type Specifiers

Type specifiers in declarations define the type of a variable or function. The keywords recognized by the MSVC C compiler as type specifiers are:
SpecifierDescription
voidNo value; used for functions that return nothing, empty parameter lists, and generic pointers
charSingle byte, typically used to hold a character; may be signed or unsigned depending on /J
shortShort integer, at least 16 bits
intDefault integer type, at least 16 bits (32 bits on all MSVC targets)
longAt least 32 bits
long longAt least 64 bits
floatSingle-precision floating point (32 bits)
doubleDouble-precision floating point (64 bits)
long doubleSame as double in MSVC (64 bits)
signedExplicit signed modifier; signed alone means signed int
unsignedUnsigned modifier; unsigned alone means unsigned int
_BoolBoolean type (C99+); holds 0 or 1
The Microsoft C compiler no longer accepts implicit int declarations (where the type specifier is omitted). Every declaration must include an explicit type specifier. Type short and type int are treated as distinct types, conforming to ANSI requirements.

Signed and Unsigned Variants

The signed and unsigned keywords can precede any integral type except enum. When used alone they are understood as signed int and unsigned int respectively. For example:
signed char   sc;   // -128 to 127
unsigned char uc;   // 0 to 255
unsigned int  ui;   // 0 to 4,294,967,295
unsigned long ul;   // 0 to 4,294,967,295
MSVC generates warnings for pointer assignments that mix signed and unsigned variants, in conformance with the ANSI standard:
signed int   *pi;
unsigned int *pu;

pi = pu;  /* Warning: pointer type mismatch */

The void Type

The void keyword serves three distinct roles in C:
  1. Function return type — declares a function that returns no value: void log_message(const char *msg);
  2. Empty parameter listvoid inside parentheses means the function accepts no arguments: int get_count(void);
  3. Generic pointervoid * is a pointer to an unspecified type, used in allocators and generic APIs.
void ** cannot be used as int **. Only void * can serve as a generic pointer. Attempting to assign void ** to int ** generates a type error in ANSI-conformant mode.

Storage Sizes

The storage size of each basic type on MSVC (all platforms) is fixed:
#include <stdio.h>

int main(void) {
    printf("char:        %zu byte(s)\n", sizeof(char));        // 1
    printf("short:       %zu byte(s)\n", sizeof(short));       // 2
    printf("int:         %zu byte(s)\n", sizeof(int));         // 4
    printf("long:        %zu byte(s)\n", sizeof(long));        // 4
    printf("long long:   %zu byte(s)\n", sizeof(long long));   // 8
    printf("float:       %zu byte(s)\n", sizeof(float));       // 4
    printf("double:      %zu byte(s)\n", sizeof(double));      // 8
    printf("long double: %zu byte(s)\n", sizeof(long double)); // 8
    printf("_Bool:       %zu byte(s)\n", sizeof(_Bool));       // 1
    return 0;
}

Type Qualifiers

Type qualifiers add semantic properties to a declaration. The qualifiers const, volatile, and restrict can appear with any type specifier but can appear only once per declaration.
The const qualifier declares an object as non-modifiable. The compiler may place const objects in read-only memory and will warn or error on any write attempt.
const int MAX_SIZE = 256;     // read-only integer
int const *p_ci;              // pointer to constant int
int *const cp_i = &MAX_SIZE;  // constant pointer to int (illegal - int* to const int*)

/* Correct usage of const pointer to const */
const int *const cpc = &MAX_SIZE;
const is especially useful for function parameters to signal that the function does not modify the pointed-to object:
size_t safe_strlen(const char *str) {
    size_t len = 0;
    while (*str++) len++;
    return len;
}

Typedef Declarations

A typedef declaration creates a synonym for an existing type. It is interpreted like an ordinary variable declaration, except that the identifier becomes the new type name rather than a variable name.
/* Simple typedef */
typedef int WHOLE;
WHOLE count = 0;           /* same as: int count = 0; */

/* Typedef for a structure */
typedef struct {
    char  name[64];
    int   age;
    float salary;
} Employee;

Employee e1;               /* no need to write "struct ..." */

/* Typedef for a pointer */
typedef Employee *PEmployee;
PEmployee ptr = &e1;

/* Typedef for a function pointer */
typedef int (*CompareFunc)(const void *, const void *);
CompareFunc cmp = strcmp;  /* compatible function pointer */

/* Typedef for an array */
typedef char PathBuf[260];
PathBuf system_path;       /* char system_path[260]; */
Typedef names can be combined with const and volatile qualifiers: const WHOLE max = 100; is valid. However, long WHOLE x; is illegal because long cannot be applied to an int alias as an additional modifier.

Struct and Union Declarations

Structures (struct) group heterogeneous members into a single named type. Unions (union) allow different members to share the same storage.
/* Named struct with tag */
struct Point {
    double x;
    double y;
};

/* Declare variables */
struct Point origin = { 0.0, 0.0 };
struct Point p1;
p1.x = 3.5;
p1.y = 7.2;

/* Typedef hides the struct keyword */
typedef struct {
    int   width;
    int   height;
    char *title;
} Window;

Window main_window = { 800, 600, "My App" };

Bit Fields

C allows members of a struct to be declared as bit fields, specifying an exact number of bits:
struct Flags {
    unsigned int read    : 1;
    unsigned int write   : 1;
    unsigned int execute : 1;
    unsigned int padding : 5;  /* pad to byte boundary */
};

struct Flags perms = { 1, 1, 0, 0 };
if (perms.read) {
    printf("Read allowed\n");
}

Enumeration Types

An enumeration (enum) defines a set of named integer constants. In ANSI C, enumerator values always have type int.
/* Basic enum */
enum Day {
    SUNDAY = 0,
    MONDAY,      /* 1 */
    TUESDAY,     /* 2 */
    WEDNESDAY,   /* 3 */
    THURSDAY,    /* 4 */
    FRIDAY,      /* 5 */
    SATURDAY     /* 6 */
};

enum Day today = WEDNESDAY;

/* Enum with explicit values */
enum HttpStatus {
    HTTP_OK          = 200,
    HTTP_NOT_FOUND   = 404,
    HTTP_SERVER_ERR  = 500
};

/* Typedef enum */
typedef enum {
    COLOR_RED,
    COLOR_GREEN,
    COLOR_BLUE
} Color;

Color bg = COLOR_BLUE;

/* Enum in switch */
switch (today) {
case SATURDAY:
case SUNDAY:
    printf("Weekend\n");
    break;
default:
    printf("Weekday\n");
    break;
}
Enumeration identifiers live in the same namespace as ordinary variable identifiers. They must be distinct from other identifiers in the same scope. Enumeration tags (the name after enum) must also be distinct from other struct, union, and enum tags.

Storage-Class Specifiers

Storage-class specifiers control the lifetime and linkage of variables and functions:
SpecifierScopeLifetimeLinkage
autoBlockUntil end of blockNone (local)
registerBlockUntil end of blockNone (hints CPU register)
staticBlock or fileProgram lifetimeInternal (file scope only)
externAnyProgram lifetimeExternal
/* static variable retains value across calls */
void count_calls(void) {
    static int call_count = 0;
    call_count++;
    printf("Called %d times\n", call_count);
}

/* extern references a variable defined in another translation unit */
extern int global_config;

/* static at file scope limits visibility to this .c file */
static int internal_helper(int x) {
    return x * x;
}

See Also

Build docs developers (and LLMs) love