C Data Types
C - Data Types
Section titled “C - Data Types”In C, data types define the kind of data a variable can hold and the operations that can be performed on it. They specify the amount of memory allocated for a variable and how the stored bit pattern is interpreted.
C data types can be broadly categorized as follows:
| Category | Description |
|---|---|
| 1 | Fundamental Types: These are the basic building blocks for data manipulation. They include: a) Integer types (like int, char, short, long, long long, including unsigned variants and _Bool)b) Floating-point types ( float, double, long double) |
| 2 | Enumerated Types (enum): User-defined types where variables can only take values from a predefined set of integer constants, improving code readability and maintainability. |
| 3 | The void Type: Represents the absence of type. Used primarily for functions that return nothing, accept no arguments, or for generic pointers (void *). |
| 4 | Derived Types: Types constructed from fundamental types or other derived types. These include: a) Pointer types (*) b) Array types ([]) c) Structure types ( struct)d) Union types ( union)e) Function types |
Arrays and structures are often grouped as aggregate types because they can hold multiple values. Function types describe the return type and parameters of a function. We’ll focus on fundamental types here; others are covered later.
Integer Types
Section titled “Integer Types”Integer types store whole numbers. The C standard guarantees minimum ranges for these types, but their exact size (in bytes) and precise range can vary depending on the compiler and architecture (e.g., 32-bit vs 64-bit systems).
Common integer types and their characteristics (minimum ranges mandated by the C standard):
| Type | Typical Size | Minimum Value Range (Standard Guarantee) | Common Use |
|---|---|---|---|
char | Typically 1 byte | Usually -128 to 127 (signed) or 0 to 255 (unsigned), implementation-defined | Smallest addressable unit, often used for characters (ASCII) or small integers. |
signed char | Typically 1 byte | -127 to 127 | Explicitly signed character type. |
unsigned char | Typically 1 byte | 0 to 255 | Explicitly unsigned character type, useful for raw byte manipulation. |
short (short int) | At least 2 bytes | -32,767 to 32,767 | For smaller integer values. |
unsigned short | At least 2 bytes | 0 to 65,535 | For non-negative smaller integers. |
int | Typically matches the ‘natural’ word size of the machine (often 4 bytes on modern systems), at least 2 bytes | -32,767 to 32,767 (minimum guaranteed, often much larger like ±2 billion) | Default integer type, commonly used. |
unsigned int | Same size as int | 0 to 65,535 (minimum guaranteed, often up to 4 billion) | For non-negative integers, bit manipulation. |
long (long int) | At least 4 bytes | -2,147,483,647 to 2,147,483,647 | For larger integer values. |
unsigned long | Same size as long | 0 to 4,294,967,295 | For large non-negative integers. |
long long (long long int) (C99+) | At least 8 bytes | ±9 x 10^18 (approx) | For very large integer values. |
unsigned long long (C99+) | Same size as long long | 0 to 1.8 x 10^19 (approx) | For very large non-negative integers. |
_Bool (C99+) | Typically 1 byte | 0 or 1 | For boolean values (true/false). Include <stdbool.h> for bool, true, false macros. |
Fixed-Width Integers (C99+): For situations requiring predictable sizes across platforms, use types defined in <stdint.h>, such as int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t. These are highly recommended for portable code dealing with binary data formats or hardware interfaces.
To get the exact size of a type or variable on your specific system, use the sizeof operator. It returns the size in bytes as a size_t value (an unsigned integer type). Use the %zu format specifier to print size_t values.
// Include standard I/O and limits headers#include <stdio.h>#include <limits.h> // Provides constants for integer type ranges (e.g., INT_MIN, INT_MAX)#include <stdint.h> // Provides fixed-width integer types
int main(void) { // Use void to indicate no arguments printf("Size of int on this system: %zu bytes\n", sizeof(int)); printf("Size of long long on this system: %zu bytes\n", sizeof(long long)); printf("Size of int32_t: %zu bytes\n", sizeof(int32_t)); // Guaranteed to be 4 bytes (32 bits)
// Example using limits printf("Minimum value for int: %d\n", INT_MIN); printf("Maximum value for int: %d\n", INT_MAX);
return 0;}Compile and run this code. The output for sizeof(int) might be 4 on most modern systems, but could differ.
Example Output (typical 64-bit Linux):
Size of int on this system: 4 bytesSize of long long on this system: 8 bytesSize of int32_t: 4 bytesMinimum value for int: -2147483648Maximum value for int: 2147483647Floating-Point Types
Section titled “Floating-Point Types”Floating-point types represent real numbers (numbers with a fractional part). They typically follow the IEEE 754 standard for representation.
| Type | Typical Size | Approximate Range | Approximate Precision | Common Use |
|---|---|---|---|---|
float | 4 bytes | Approx ±1.2E-38 to ±3.4E+38 | Approx 6-7 decimal digits | Single-precision floating-point. Use when memory is tight and high precision isn’t critical. |
double | 8 bytes | Approx ±2.3E-308 to ±1.7E+308 | Approx 15-16 decimal digits | Double-precision floating-point. The default for floating-point literals and generally preferred for calculations. |
long double | Often 10 or 16 bytes (implementation-defined) | Varies, typically wider range than double | Varies, typically higher precision than double | Extended-precision floating-point. Use when maximum precision is needed. |
The <float.h> header file defines macros for floating-point characteristics, like minimum/maximum values (FLT_MIN, FLT_MAX, DBL_MIN, DBL_MAX) and precision (FLT_DIG, DBL_DIG).
#include <stdio.h>#include <float.h> // Provides constants for float type limits
int main(void) { printf("Size of float: %zu bytes\n", sizeof(float)); printf("Smallest positive float value: %E\n", FLT_MIN); // Smallest normalized positive value printf("Largest positive float value: %E\n", FLT_MAX); printf("Float precision (decimal digits): %d\n", FLT_DIG);
printf("\nSize of double: %zu bytes\n", sizeof(double)); printf("Double precision (decimal digits): %d\n", DBL_DIG);
return 0;}Example Output (typical system):
Size of float: 4 bytesSmallest positive float value: 1.175494E-38Largest positive float value: 3.402823E+38Float precision (decimal digits): 6
Size of double: 8 bytesDouble precision (decimal digits): 15The void Type
Section titled “The void Type”The void type has a special meaning: it indicates the absence of a type or value. It’s used in three main contexts:
| Context | Description and Example |
|---|---|
| 1 | Function Return Type: void func(...) declares a function that does not return any value. Example: void print_hello(void); |
| 2 | Function Arguments: int func(void) declares a function that takes no arguments. This is the standard way in C to specify an empty parameter list (required since C99, though many compilers accept () for compatibility). Example: int getchar(void); |
| 3 | Generic Pointers (void *): A pointer of type void * can hold the address of any object type, but not a function pointer. It’s a generic pointer used for manipulating raw memory or passing addresses of unknown type, like with malloc and free. You must explicitly cast a void * to another pointer type before dereferencing it. Example: void *malloc(size_t size); returns a generic pointer to allocated memory. |
Understanding void, especially void *, becomes clearer when learning about functions and pointers in later chapters.