Strings

C has no built-in string type. A "string" is just an array of char with one special rule: it ends with a hidden '\0' byte, the null terminator, which is how every string function knows where the text stops.

Declaring and printing a string

C main.c
#include <stdio.h>

int main(void) {
    char name[] = "Maya";

    printf("Hello, %s!\n", name);
    printf("First letter: %c\n", name[0]);

    return 0;
}
Output
Hello, Maya!
First letter: M

char name[] = "Maya" actually allocates 5 bytes, not 4 — 'M', 'a', 'y', 'a', and a trailing '\0' the compiler adds for you. %s in printf prints characters starting at name and keeps going until it hits that '\0'.

Note: because a string is just an array with a convention, nothing stops you from indexing past the end or overwriting the null terminator by hand — do either and every function that reads the string afterward (including printf) will keep reading into whatever memory happens to follow, printing garbage or crashing.

Common string.h functions

The <string.h> header covers most day-to-day string work: length, copying, comparing, and joining:

C main.c
#include <stdio.h>
#include <string.h>

int main(void) {
    char greeting[32] = "Hello";

    printf("Length: %zu\n", strlen(greeting));
    strcat(greeting, ", world");
    printf("After strcat: %s\n", greeting);
    printf("Compare: %d\n", strcmp("abc", "abd"));

    return 0;
}
Output
Length: 5
After strcat: Hello, world
Compare: -1

strlen counts characters up to (but not including) the null terminator. strcat appends one string onto the end of another, in place — which is why greeting had to be declared with 32 bytes of room, far more than "Hello" needs, to leave space for what gets tacked on later. strcmp returns 0 for equal strings, and a negative or positive number depending on which string sorts first alphabetically — here 'c' comes before 'd', so it returns a negative value.

Copying strings safely

strcpy copies one string into another buffer, but like strcat, it has no idea how big the destination buffer actually is — it will happily write past the end of it. strncpy takes an explicit size limit and is the safer default:

C main.c
#include <stdio.h>
#include <string.h>

int main(void) {
    char dest[8];

    strncpy(dest, "Copy me", sizeof(dest) - 1);
    dest[sizeof(dest) - 1] = '\0';
    printf("%s\n", dest);

    return 0;
}
Output
Copy me

Reserving one byte for the terminator (sizeof(dest) - 1) and then setting it explicitly afterward is a defensive habit worth keeping: strncpy will not add a null terminator itself if the source string is exactly as long as the limit you gave it, leaving dest without one and every later %s read running off the end.