C/C++ Fundamentals Reference

Table of Contents

  1. Data Types
  2. Memory Organization
  3. Basic Data Handling
  4. Byte Addressable Memory
  5. C-Style Strings
  6. String Handling Operations
  7. Common Patterns & Best Practices

Data Types

Fundamental Data Types

Type Size (bytes) Range Description
char 1 -128 to 127 or 0 to 255 Single character or small integer
unsigned char 1 0 to 255 Unsigned character
short (short int) 2 -32,768 to 32,767 Short integer
unsigned short 2 0 to 65,535 Unsigned short integer
int 4 -2,147,483,648 to 2,147,483,647 Standard integer
unsigned int 4 0 to 4,294,967,295 Unsigned integer
long 8 -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 Long integer
unsigned long 8 0 to 18,446,744,073,709,551,615 Unsigned long integer
float 4 ±3.4E±38 (7 digits precision) Single precision floating point
double 8 ±1.7E±308 (15 digits precision) Double precision floating point
long double 16 Extended precision Extended precision floating point
#include <stdio.h> #include <limits.h> #include <float.h> int main() { printf("char: %zu bytes, range: %d to %d\n", sizeof(char), CHAR_MIN, CHAR_MAX); printf("int: %zu bytes, range: %d to %d\n", sizeof(int), INT_MIN, INT_MAX); printf("long: %zu bytes, range: %ld to %ld\n", sizeof(long), LONG_MIN, LONG_MAX); printf("float: %zu bytes, range: %E to %E\n", sizeof(float), FLT_MIN, FLT_MAX); printf("double: %zu bytes, range: %E to %E\n", sizeof(double), DBL_MIN, DBL_MAX); return 0; }

Derived Data Types

Memory Organization

Memory Layout

┌─────────────────┐ ← High Address
│ Stack │
│ (grows down) │
├─────────────────┤
│ │
│ Free Space │
│ │
├─────────────────┤
│ Heap │
│ (grows up) │
├─────────────────┤
│ BSS Segment │
│ (uninitialized)│
├─────────────────┤
│ Data Segment │
│ (initialized) │
├─────────────────┤
│ Text Segment │
│ (code) │ ← Low Address
└─────────────────┘

Memory Segments

#include <stdio.h> #include <stdlib.h> int global_var = 100; // Data segment int uninitialized_var; // BSS segment int main() { int local_var = 50; // Stack int *heap_var = malloc(sizeof(int)); // Heap *heap_var = 75; printf("Global var address: %p\n", &global_var); printf("Local var address: %p\n", &local_var); printf("Heap var address: %p\n", heap_var); free(heap_var); return 0; }

Basic Data Handling

Variables and Constants

// Variable declarations and initialization int x = 10; // Initialize at declaration int y; // Declare, initialize later y = 20; const int MAX_SIZE = 100; // Constant (cannot be changed) #define PI 3.14159 // Preprocessor constant // Type qualifiers volatile int sensor_data; // Value may change unexpectedly register int counter; // Suggest storing in CPU register static int file_scope; // Internal linkage extern int global_from_other_file; // External linkage

Arrays

// Array declaration and initialization int numbers[5]; // Uninitialized array int values[5] = {1, 2, 3, 4, 5}; // Initialized array int partial[5] = {1, 2}; // Partially initialized (rest are 0) int auto_size[] = {1, 2, 3}; // Size determined by initializer // Multidimensional arrays int matrix[3][4]; // 3x4 matrix int cube[2][3][4] = { // 3D array with initialization {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}, {{13, 14, 15, 16}, {17, 18, 19, 20}, {21, 22, 23, 24}} }; // Array access for (int i = 0; i < 5; i++) { numbers[i] = i * 10; }

Pointers

// Pointer declaration and usage int value = 42; int *ptr = &value; // Pointer to int, initialized with address of value int **double_ptr = &ptr; // Pointer to pointer printf("Value: %d\n", value); // 42 printf("Address of value: %p\n", &value); printf("Pointer value: %p\n", ptr); printf("Value through pointer: %d\n", *ptr); // Dereferencing // Pointer arithmetic int arr[5] = {10, 20, 30, 40, 50}; int *p = arr; // Points to first element printf("First element: %d\n", *p); // 10 printf("Second element: %d\n", *(p+1)); // 20 printf("Third element: %d\n", p[2]); // 30 (equivalent to *(p+2)) // Moving through array for (int i = 0; i < 5; i++) { printf("Element %d: %d at address %p\n", i, *p, p); p++; }
Tip: Array names are essentially constant pointers to the first element. arr and &arr[0] are equivalent.

Byte Addressable Memory

Understanding Memory Addresses

In C/C++, memory is byte-addressable, meaning each byte has a unique address. Understanding this is crucial for pointer arithmetic and memory manipulation.

#include <stdio.h> int main() { char c = 'A'; short s = 1000; int i = 50000; double d = 3.14159; printf("char 'A' at address %p, size: %zu bytes\n", &c, sizeof(c)); printf("short 1000 at address %p, size: %zu bytes\n", &s, sizeof(s)); printf("int 50000 at address %p, size: %zu bytes\n", &i, sizeof(i)); printf("double 3.14159 at address %p, size: %zu bytes\n", &d, sizeof(d)); // Examining memory byte by byte unsigned char *byte_ptr = (unsigned char*)&i; printf("Integer %d stored as bytes: ", i); for (size_t j = 0; j < sizeof(i); j++) { printf("0x%02X ", byte_ptr[j]); } printf("\n"); return 0; }

Memory Layout Example

Array: int arr[4] = {0x12345678, 0x9ABCDEF0, 0x11111111, 0x22222222}

Address │ Byte 0 │ Byte 1 │ Byte 2 │ Byte 3 │ Value
─────────┼────────┼────────┼────────┼────────┼──────────
0x1000 │ 78 │ 56 │ 34 │ 12 │ arr[0]
0x1004 │ F0 │ DE │ BC │ 9A │ arr[1]
0x1008 │ 11 │ 11 │ 11 │ 11 │ arr[2]
0x100C │ 22 │ 22 │ 22 │ 22 │ arr[3]
Important: The byte order shown above assumes little-endian architecture (least significant byte first). On big-endian systems, the byte order would be reversed.

Pointer Arithmetic and Byte Addressing

#include <stdio.h> int main() { int arr[4] = {0x12345678, 0x9ABCDEF0, 0x11111111, 0x22222222}; printf("Array addresses and values:\n"); for (int i = 0; i < 4; i++) { printf("arr[%d] = 0x%08X at address %p\n", i, arr[i], &arr[i]); } // Pointer arithmetic - moves by sizeof(type) bytes int *ptr = arr; printf("\nPointer arithmetic:\n"); printf("ptr points to %p, value: 0x%08X\n", ptr, *ptr); ptr++; // Moves by sizeof(int) = 4 bytes printf("ptr++ points to %p, value: 0x%08X\n", ptr, *ptr); // Byte-level access unsigned char *byte_ptr = (unsigned char*)arr; printf("\nByte-level access of first integer:\n"); for (int i = 0; i < 4; i++) { printf("Byte %d: 0x%02X at address %p\n", i, byte_ptr[i], &byte_ptr[i]); } return 0; }

C-Style Strings

String Representation

C-style strings are arrays of characters terminated by a null character ('\0' or ASCII 0).

#include <stdio.h> #include <string.h> int main() { // Different ways to declare and initialize strings char str1[] = "Hello"; // Size automatically calculated (6 bytes) char str2[10] = "World"; // Fixed size array, partially filled char str3[] = {'H', 'e', 'l', 'l', 'o', '\0'}; // Character array char *str4 = "Constant string"; // Pointer to string literal printf("String lengths:\n"); printf("str1: \"%s\" length: %zu, array size: %zu\n", str1, strlen(str1), sizeof(str1)); printf("str2: \"%s\" length: %zu, array size: %zu\n", str2, strlen(str2), sizeof(str2)); printf("str3: \"%s\" length: %zu, array size: %zu\n", str3, strlen(str3), sizeof(str3)); printf("str4: \"%s\" length: %zu\n", str4, strlen(str4)); // Examining string memory layout printf("\nMemory layout of str1 (\"%s\"):\n", str1); for (size_t i = 0; i < sizeof(str1); i++) { if (str1[i] == '\0') { printf("Index %zu: '\\0' (null terminator)\n", i); } else { printf("Index %zu: '%c'\n", i, str1[i]); } } return 0; }

String Memory Layout

char str[] = "Hello";

Index │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │
──────┼───┼───┼───┼───┼───┼───┤
Char │ H │ e │ l │ l │ o │\0 │
ASCII │72 │101│108│108│111│ 0 │
Warning: String literals are stored in read-only memory. Attempting to modify them through a pointer results in undefined behavior.

String Handling Operations

Standard String Functions

#include <stdio.h> #include <string.h> #include <stdlib.h> int main() { char source[] = "Hello World"; char destination[50]; char buffer[100]; // String copying strcpy(destination, source); printf("strcpy result: %s\n", destination); // Safe string copying (with size limit) strncpy(buffer, source, sizeof(buffer) - 1); buffer[sizeof(buffer) - 1] = '\0'; // Ensure null termination printf("strncpy result: %s\n", buffer); // String concatenation strcat(destination, " from C"); printf("strcat result: %s\n", destination); // Safe string concatenation strncat(buffer, " - safe version", sizeof(buffer) - strlen(buffer) - 1); printf("strncat result: %s\n", buffer); // String comparison int cmp = strcmp("Apple", "Banana"); printf("strcmp(\"Apple\", \"Banana\"): %d\n", cmp); // Negative cmp = strcmp("Hello", "Hello"); printf("strcmp(\"Hello\", \"Hello\"): %d\n", cmp); // Zero // String searching char *found = strchr(source, 'W'); if (found) { printf("Found 'W' at position: %ld\n", found - source); } found = strstr(source, "World"); if (found) { printf("Found \"World\" at position: %ld\n", found - source); } return 0; }

Manual String Operations

// Custom string length function size_t my_strlen(const char *str) { size_t length = 0; while (str[length] != '\0') { length++; } return length; } // Custom string copy function char *my_strcpy(char *dest, const char *src) { char *original_dest = dest; while (*src != '\0') { *dest = *src; dest++; src++; } *dest = '\0'; // Add null terminator return original_dest; } // Custom string comparison function int my_strcmp(const char *str1, const char *str2) { while (*str1 && (*str1 == *str2)) { str1++; str2++; } return *(unsigned char*)str1 - *(unsigned char*)str2; } // Reverse a string in-place void reverse_string(char *str) { if (!str) return; size_t len = strlen(str); for (size_t i = 0; i < len / 2; i++) { char temp = str[i]; str[i] = str[len - 1 - i]; str[len - 1 - i] = temp; } }

Dynamic String Handling

#include <stdio.h> #include <stdlib.h> #include <string.h> // Dynamic string creation char *create_string(const char *source) { if (!source) return NULL; size_t len = strlen(source); char *new_str = malloc(len + 1); // +1 for null terminator if (!new_str) return NULL; // Check for allocation failure strcpy(new_str, source); return new_str; } // String concatenation with dynamic allocation char *concat_strings(const char *str1, const char *str2) { if (!str1 || !str2) return NULL; size_t len1 = strlen(str1); size_t len2 = strlen(str2); char *result = malloc(len1 + len2 + 1); if (!result) return NULL; strcpy(result, str1); strcat(result, str2); return result; } int main() { char *dynamic_str = create_string("Hello"); printf("Dynamic string: %s\n", dynamic_str); char *combined = concat_strings(dynamic_str, " World!"); printf("Combined string: %s\n", combined); // Always free dynamically allocated memory free(dynamic_str); free(combined); return 0; }
Memory Management: Always pair malloc() with free(), and set pointers to NULL after freeing to avoid dangling pointers.

Common Patterns & Best Practices

Safe String Input

#include <stdio.h> #include <string.h> // Safe string input function void safe_gets(char *buffer, size_t buffer_size) { if (fgets(buffer, buffer_size, stdin)) { // Remove newline if present size_t len = strlen(buffer); if (len > 0 && buffer[len-1] == '\n') { buffer[len-1] = '\0'; } } } int main() { char name[50]; printf("Enter your name: "); safe_gets(name, sizeof(name)); printf("Hello, %s!\n", name); return 0; }

Error Handling Patterns

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> // Function with proper error handling char *safe_string_duplicate(const char *source) { if (!source) { fprintf(stderr, "Error: NULL pointer passed to safe_string_duplicate\n"); return NULL; } size_t len = strlen(source); char *duplicate = malloc(len + 1); if (!duplicate) { fprintf(stderr, "Error: Memory allocation failed: %s\n", strerror(errno)); return NULL; } memcpy(duplicate, source, len + 1); // Include null terminator return duplicate; } // File reading with error checking int read_file_to_string(const char *filename, char **content) { FILE *file = fopen(filename, "r"); if (!file) { fprintf(stderr, "Error opening file '%s': %s\n", filename, strerror(errno)); return -1; } // Get file size fseek(file, 0, SEEK_END); long size = ftell(file); fseek(file, 0, SEEK_SET); *content = malloc(size + 1); if (!*content) { fprintf(stderr, "Memory allocation failed\n"); fclose(file); return -1; } size_t bytes_read = fread(*content, 1, size, file); (*content)[bytes_read] = '\0'; fclose(file); return 0; }

Memory Layout Debugging

#include <stdio.h> // Function to print memory contents as hex void print_memory(const void *ptr, size_t size) { const unsigned char *byte_ptr = (const unsigned char*)ptr; printf("Memory contents at %p:\n", ptr); printf("Offset: "); for (size_t i = 0; i < size; i++) { printf("%02zX ", i); } printf("\n"); printf("Value: "); for (size_t i = 0; i < size; i++) { printf("%02X ", byte_ptr[i]); } printf("\n"); printf("ASCII: "); for (size_t i = 0; i < size; i++) { if (byte_ptr[i] >= 32 && byte_ptr[i] <= 126) { printf(" %c ", byte_ptr[i]); } else { printf(" . "); } } printf("\n\n"); } int main() { char string[] = "Hello\0Hidden"; int number = 0x12345678; printf("String analysis:\n"); print_memory(string, sizeof(string)); printf("Integer analysis:\n"); print_memory(&number, sizeof(number)); return 0; }

Performance Considerations

Performance Tips:
  • Use memcpy() instead of strcpy() when you know the length
  • Cache string lengths in loops to avoid repeated strlen() calls
  • Use const for read-only string parameters
  • Consider using static buffers for temporary strings in frequently called functions
  • Use string builders or dynamic arrays for string concatenation in loops

Common Pitfalls

Watch Out For:
  • Buffer Overflows: Always check bounds when copying strings
  • Missing Null Terminators: strncpy() doesn't always add '\0'
  • Dangling Pointers: Don't use pointers after freeing memory
  • Memory Leaks: Every malloc() needs a corresponding free()
  • Modifying String Literals: String literals are read-only
  • Uninitialized Arrays: Local arrays contain garbage values

Quick Reference

Essential Functions

Function Purpose Example
strlen() Get string length size_t len = strlen("hello");
strcpy() Copy string strcpy(dest, src);
strncpy() Copy n characters strncpy(dest, src, n);
strcat() Concatenate strings strcat(dest, src);
strcmp() Compare strings int cmp = strcmp(s1, s2);
strchr() Find character char *p = strchr(str, 'c');
strstr() Find substring char *p = strstr(str, "sub");
malloc() Allocate memory char *p = malloc(100);
free() Free memory free(p);