C99 Features
Introduction
The C99 standard (ISO/IEC 9899:1999) introduced numerous significant enhancements to the C programming language, making it more powerful, expressive, and suitable for modern programming tasks. These features addressed many limitations of the previous C89/C90 standard and brought C closer to contemporary programming language capabilities.
C99 features include new data types, improved control structures, enhanced initialization syntax, and better support for numerical computing. Understanding these features is essential for writing modern, efficient C code.
Variable-Length Arrays (VLAs)
One of the most significant additions in C99 was Variable-Length Arrays (VLAs), which allow the size of an array to be determined at runtime rather than compile time.
Basic VLA Declaration
#include <stdio.h>
int main() {
int size;
printf("Enter array size: ");
scanf("%d", &size);
// Variable-length array - size determined at runtime
int array[size];
// Initialize and use the array
for (int i = 0; i < size; i++) {
array[i] = i * i;
printf("array[%d] = %d\n", i, array[i]);
}
return 0;
}Multi-dimensional VLAs
#include <stdio.h>
int main() {
int rows, cols;
printf("Enter rows and columns: ");
scanf("%d %d", &rows, &cols);
// Multi-dimensional variable-length array
int matrix[rows][cols];
// Initialize the matrix
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = i * cols + j;
}
}
// Print the matrix
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%4d ", matrix[i][j]);
}
printf("\n");
}
return 0;
}VLA Function Parameters
#include <stdio.h>
// Function that accepts VLA parameters
void process_matrix(int rows, int cols, int matrix[rows][cols]) {
printf("Processing %dx%d matrix:\n", rows, cols);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] *= 2; // Double each element
}
}
}
// Function to print a matrix
void print_matrix(int rows, int cols, int matrix[rows][cols]) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%4d ", matrix[i][j]);
}
printf("\n");
}
}
int main() {
int rows = 3, cols = 4;
// Declare and initialize a VLA
int matrix[rows][cols] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
printf("Original matrix:\n");
print_matrix(rows, cols, matrix);
// Process the matrix
process_matrix(rows, cols, matrix);
printf("\nModified matrix:\n");
print_matrix(rows, cols, matrix);
return 0;
}Designated Initializers
C99 introduced designated initializers, which allow you to initialize specific elements of arrays and structures by specifying their indices or member names.
Array Designated Initializers
#include <stdio.h>
int main() {
// Traditional initialization
int array1[10] = {0, 0, 5, 0, 0, 0, 0, 8, 0, 0};
// Designated initializers - only initialize specific elements
int array2[10] = {[2] = 5, [7] = 8};
// Mixed initialization
int array3[10] = {1, 2, [5] = 10, 11, 12};
printf("Array 1: ");
for (int i = 0; i < 10; i++) {
printf("%d ", array1[i]);
}
printf("\n");
printf("Array 2: ");
for (int i = 0; i < 10; i++) {
printf("%d ", array2[i]);
}
printf("\n");
printf("Array 3: ");
for (int i = 0; i < 10; i++) {
printf("%d ", array3[i]);
}
printf("\n");
return 0;
}Structure Designated Initializers
#include <stdio.h>
#include <string.h>
typedef struct {
int id;
char name[50];
double salary;
int age;
} Employee;
int main() {
// Traditional initialization (order matters)
Employee emp1 = {1, "John Doe", 50000.0, 30};
// Designated initializers (order doesn't matter)
Employee emp2 = {
.id = 2,
.name = "Jane Smith",
.salary = 60000.0,
.age = 28
};
// Partial initialization with designated initializers
Employee emp3 = {
.id = 3,
.name = "Bob Johnson"
// salary and age will be zero-initialized
};
printf("Employee 1: ID=%d, Name=%s, Salary=%.2f, Age=%d\n",
emp1.id, emp1.name, emp1.salary, emp1.age);
printf("Employee 2: ID=%d, Name=%s, Salary=%.2f, Age=%d\n",
emp2.id, emp2.name, emp2.salary, emp2.age);
printf("Employee 3: ID=%d, Name=%s, Salary=%.2f, Age=%d\n",
emp3.id, emp3.name, emp3.salary, emp3.age);
return 0;
}Compound Literals
Compound literals allow you to create unnamed objects of any data type, including arrays and structures.
Array Compound Literals
#include <stdio.h>
#include <string.h>
void print_array(int size, int arr[size]) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
// Traditional approach
int temp_array[] = {1, 2, 3, 4, 5};
print_array(5, temp_array);
// Using compound literal
print_array(5, (int[]){1, 2, 3, 4, 5});
// Compound literal with VLA
int size = 3;
print_array(size, (int[size]){10, 20, 30});
return 0;
}Structure Compound Literals
#include <stdio.h>
#include <string.h>
typedef struct {
int x, y;
} Point;
typedef struct {
Point center;
double radius;
} Circle;
void print_point(Point p) {
printf("Point(%d, %d)\n", p.x, p.y);
}
void print_circle(Circle c) {
printf("Circle(center=(%d, %d), radius=%.2f)\n",
c.center.x, c.center.y, c.radius);
}
int main() {
// Traditional approach
Point p1 = {10, 20};
print_point(p1);
// Using compound literal
print_point((Point){30, 40});
// Structure with nested structure
Circle c1 = {{0, 0}, 5.0};
print_circle(c1);
// Using compound literals for nested structures
print_circle((Circle){{10, 10}, 7.5});
return 0;
}New Data Types
C99 introduced several new data types to improve numerical computing capabilities.
long long Type
#include <stdio.h>
#include <limits.h>
int main() {
long long big_number = 123456789012345LL;
unsigned long long huge_number = 1234567890123456789ULL;
printf("long long value: %lld\n", big_number);
printf("unsigned long long value: %llu\n", huge_number);
printf("LLONG_MIN: %lld\n", LLONG_MIN);
printf("LLONG_MAX: %lld\n", LLONG_MAX);
printf("ULLONG_MAX: %llu\n", ULLONG_MAX);
return 0;
}_Bool Type
#include <stdio.h>
#include <stdbool.h>
int main() {
// Using _Bool (C99)
_Bool flag1 = 1;
_Bool flag2 = 0;
// Using bool with stdbool.h (recommended)
bool is_valid = true;
bool is_error = false;
printf("_Bool values: %d, %d\n", flag1, flag2);
printf("bool values: %d, %d\n", is_valid, is_error);
// Boolean expressions
int x = 10, y = 20;
bool result = (x < y);
printf("x < y: %s\n", result ? "true" : "false");
return 0;
}Inline Functions
The inline keyword suggests to the compiler that a function should be inlined to reduce function call overhead.
#include <stdio.h>
// Inline function suggestion
inline int square(int x) {
return x * x;
}
// Static inline function (more portable)
static inline int cube(int x) {
return x * x * x;
}
int main() {
int num = 5;
printf("Square of %d: %d\n", num, square(num));
printf("Cube of %d: %d\n", num, cube(num));
// Inline functions work in loops without function call overhead
for (int i = 1; i <= 5; i++) {
printf("Square of %d: %d\n", i, square(i));
}
return 0;
}restrict Keyword
The restrict keyword is a hint to the compiler that a pointer is the only way to access a particular memory region, allowing for better optimization.
#include <stdio.h>
// Without restrict - compiler must be conservative
void add_arrays_normal(int n, int *a, int *b, int *result) {
for (int i = 0; i < n; i++) {
result[i] = a[i] + b[i];
}
}
// With restrict - compiler can optimize more aggressively
void add_arrays_restrict(int n, int *restrict a, int *restrict b, int *restrict result) {
for (int i = 0; i < n; i++) {
result[i] = a[i] + b[i];
}
}
int main() {
int a[5] = {1, 2, 3, 4, 5};
int b[5] = {10, 20, 30, 40, 50};
int result1[5], result2[5];
add_arrays_normal(5, a, b, result1);
add_arrays_restrict(5, a, b, result2);
printf("Normal addition: ");
for (int i = 0; i < 5; i++) {
printf("%d ", result1[i]);
}
printf("\n");
printf("Restrict addition: ");
for (int i = 0; i < 5; i++) {
printf("%d ", result2[i]);
}
printf("\n");
return 0;
}Flexible Array Members
C99 introduced flexible array members, allowing structures to have arrays of variable size at the end.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int count;
double data[]; // Flexible array member (must be last)
} DynamicArray;
DynamicArray* create_dynamic_array(int count) {
// Allocate memory for structure + array
DynamicArray *arr = malloc(sizeof(DynamicArray) + count * sizeof(double));
if (arr != NULL) {
arr->count = count;
}
return arr;
}
void free_dynamic_array(DynamicArray *arr) {
free(arr);
}
int main() {
int size = 5;
DynamicArray *arr = create_dynamic_array(size);
if (arr != NULL) {
// Initialize the flexible array
for (int i = 0; i < arr->count; i++) {
arr->data[i] = i * 1.5;
}
// Print the array
printf("Dynamic array contents: ");
for (int i = 0; i < arr->count; i++) {
printf("%.1f ", arr->data[i]);
}
printf("\n");
free_dynamic_array(arr);
}
return 0;
}Practical Examples
Matrix Library Using C99 Features
#include <stdio.h>
#include <stdlib.h>
// Matrix structure using flexible array member
typedef struct {
int rows, cols;
double data[]; // Flexible array member
} Matrix;
// Create matrix
Matrix* create_matrix(int rows, int cols) {
Matrix *m = malloc(sizeof(Matrix) + rows * cols * sizeof(double));
if (m != NULL) {
m->rows = rows;
m->cols = cols;
}
return m;
}
// Free matrix
void free_matrix(Matrix *m) {
free(m);
}
// Initialize matrix with designated initializers
void init_matrix(Matrix *m, double values[m->rows][m->cols]) {
for (int i = 0; i < m->rows; i++) {
for (int j = 0; j < m->cols; j++) {
m->data[i * m->cols + j] = values[i][j];
}
}
}
// Print matrix
void print_matrix(const Matrix *m) {
for (int i = 0; i < m->rows; i++) {
for (int j = 0; j < m->cols; j++) {
printf("%8.2f ", m->data[i * m->cols + j]);
}
printf("\n");
}
}
// Matrix multiplication using VLAs
Matrix* multiply_matrices(const Matrix *a, const Matrix *b) {
if (a->cols != b->rows) {
printf("Error: Incompatible matrix dimensions\n");
return NULL;
}
Matrix *result = create_matrix(a->rows, b->cols);
if (result == NULL) {
printf("Error: Memory allocation failed\n");
return NULL;
}
// Use VLA for computation
int rows = a->rows, cols = b->cols, inner = a->cols;
double a_data[rows][inner], b_data[inner][cols], result_data[rows][cols];
// Copy data to VLAs for easier access
for (int i = 0; i < rows; i++) {
for (int j = 0; j < inner; j++) {
a_data[i][j] = a->data[i * a->cols + j];
}
}
for (int i = 0; i < inner; i++) {
for (int j = 0; j < cols; j++) {
b_data[i][j] = b->data[i * b->cols + j];
}
}
// Perform multiplication
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
result_data[i][j] = 0;
for (int k = 0; k < inner; k++) {
result_data[i][j] += a_data[i][k] * b_data[k][j];
}
}
}
// Copy result back to matrix
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
result->data[i * cols + j] = result_data[i][j];
}
}
return result;
}
int main() {
// Create matrices using compound literals
Matrix *m1 = create_matrix(2, 3);
Matrix *m2 = create_matrix(3, 2);
if (m1 == NULL || m2 == NULL) {
printf("Error: Memory allocation failed\n");
return 1;
}
// Initialize matrices
double values1[2][3] = {{1, 2, 3}, {4, 5, 6}};
double values2[3][2] = {{7, 8}, {9, 10}, {11, 12}};
init_matrix(m1, values1);
init_matrix(m2, values2);
printf("Matrix 1:\n");
print_matrix(m1);
printf("\nMatrix 2:\n");
print_matrix(m2);
// Multiply matrices
Matrix *result = multiply_matrices(m1, m2);
if (result != NULL) {
printf("\nResult of multiplication:\n");
print_matrix(result);
free_matrix(result);
}
free_matrix(m1);
free_matrix(m2);
return 0;
}Summary
C99 introduced several powerful features that significantly enhanced the C programming language:
- Variable-Length Arrays (VLAs): Runtime-determined array sizes
- Designated Initializers: Initialize specific array elements or structure members
- Compound Literals: Create unnamed objects of any type
- New Data Types:
long long,_Boolfor better numerical computing - Inline Functions: Suggest function inlining for performance
- restrict Keyword: Hint for compiler optimization
- Flexible Array Members: Variable-size arrays at structure end
These features make C99 code more expressive, efficient, and easier to write and maintain. Understanding and utilizing these features is essential for modern C programming.