Arrays in C hold a fixed number of elements of the same type, declared with a compile-time size like int arr[5]. Indexing starts at 0, and unlike some languages C performs no automatic bounds checking, so out-of-bounds access is undefined behavior. An array name acts as a pointer to its first element, so *(p + i) and arr[i] are equivalent and pointer arithmetic naturally scales by element size. However, the array name itself is not a modifiable lvalue and cannot be reassigned. Strings in C are arrays of characters terminated by a null character '\0', and the <string.h> library provides functions for working with them; for example, char name[] = "Hello"; reserves six bytes for five characters plus the terminator.
Several string functions require careful use. strlen returns the number of characters before the null terminator and does not count '\0' itself; if the string is not properly terminated, strlen reads past the buffer and triggers undefined behavior. strcpy copies a string with no bounds checking and risks buffer overflow, while strncpy copies at most n characters but may not null-terminate when the source is longer than n, requiring manual termination. For formatted output, snprintf writes at most n-1 characters and always null-terminates, making it far safer than sprintf, which has no length limit; its return value indicates the length that would have been written. Comparison and search helpers include strcmp and strncmp (for prefix checks like http://), strchr and strrchr (for first and last occurrence of a character), strstr for substring search, and strtok for splitting on delimiters, though strtok modifies its input and is not thread-safe, so strtok_r is preferred for reentrancy. For input, fgets(buf, n, stdin) is always preferable to gets, which was removed in C11 because it had no size limit.
For raw memory rather than null-terminated strings, memcpy copies exactly n bytes but requires non-overlapping regions, memmove handles overlapping regions safely through a temporary buffer, and memset fills a buffer with a byte value, which is the standard idiom for zeroing a struct or buffer. Numeric parsing benefits from strtol and strtod, which report errors through errno and an end pointer and accept a base for strtol, unlike atoi, which silently returns 0 on failure and has undefined behavior on overflow. printf and its relatives accept width and precision specifiers like %5d for right-aligned padding, %-5d for left alignment, and %.3f to control digits after the decimal, while the length modifiers %zu and %zd print size_t and ptrdiff_t portably, and the PRIu64-style macros from <inttypes.h> print fixed-width integers without bugs on 64-bit systems. Always pass user-controlled data as an argument, never as the format string itself, because a format string vulnerability allows attackers to read or write arbitrary memory through specifiers like %s and %n.