Before you begin
Why this matters
C arrays and strings are the data of almost every C program: names, labels, measurements, tables. The language checks neither their length nor the space you write into. Code that measures an array in the wrong place, or joins text into too little space, compiles and often passes a test. You can find these bugs by knowing what each declaration creates and what a function receives.
What this helps you reason about
- Predict what sizeof gives for an array, for a pointer, and for an array parameter.
- Tell the space of a char array from the length of the text in it, and give each string function enough space.
- Spot a string compared with ==, text joined without enough space, and a write into a string literal.
In Java, Python, and Go, an array or a string carries its length, and every function you pass it to can ask for that length. A C array carries none. Arrays, lists, and vectors kept taxis in a C array and counted its elements with sizeof where the array was declared. This lesson asks why that count breaks inside a function, and follows the same rule into strings: a C string is an array of char whose text ends at a zero byte, and the string functions trust that byte and the space you give them.
Already know this? The short version
- An array used in an expression is converted to a pointer to its first element, which is called decay, except under sizeof and &. A parameter written int a[] is a pointer, so sizeof a inside the function is the size of a pointer: pass the length.
- A string literal "tower" is 6 bytes, with the '\0'. char s[] = "tower" makes a copy you may change; const char *s = "tower" points at the literal, which must never be written.
- sizeof gives the space of an array; strlen walks to the first '\0' and gives the length of the text.
- strcpy and strcat write past the end of a destination that is too small, which is undefined behavior. snprintf(dst, size, …) never does, and a result of size or more means it cut the text. Compare strings with strcmp, not ==.
- A parameter for a 2-D array needs the row length, as in int a[][4], and the number of rows passed beside it. Bugs from real programs are in “What goes wrong with arrays and strings in real programs?”.
Words in this lesson (7)
- array
- A fixed number of elements of one type, stored side by side, such as int queue[5]. Its size is set when it is declared and never changes.
- string literal
- Text in double quotes in the source code, such as "tower". It is stored as an array of char with a '\0' added at the end, so "tower" takes 6 bytes, and a program must not change it.
- decay
- The automatic conversion of an array, used in an expression, into a pointer to its first element. It does not happen when the array is the operand of sizeof or &.
- array parameter
- A parameter written with brackets, such as int scores[]. C treats it as a pointer to the first element, so the function reaches the caller’s array and does not know its length.
- string
- In C, a sequence of characters that ends with the null character '\0', a byte with value 0. It is usually stored in a char array, and its length is not stored anywhere.
- undefined behavior
- Code for which the C standard sets no requirements. The program may crash, print wrong results, or seem to work, and the result can change between builds.
- two-dimensional array
- An array whose elements are arrays of the same length, such as int visits[2][4]: 2 rows of 4 ints, stored one row after the other.
Examples are focused snippets; imports, enclosing functions, or classes may be omitted. C examples use C11; Haskell examples use Haskell 2010. Expand the notes for language-specific depth.
01 / Declaring
What does an array declaration create?
Arrays, lists, and vectors showed that a C array is a fixed block of elements with no stored length. A declaration decides what is in that block at the start.
int visitors[6] = {40, 55, 31}; makes 6 ints. The values in braces fill the first elements, and every element without a value is 0. When the brackets are empty, as in int hours[] = {9, 10, 11};, the number of values sets the size: 3.
A char array can be filled from a string literal, text in double quotes. A string literal always ends with the null character '\0', a byte with value 0. So "tower" takes 6 bytes, and char copy[] = "tower"; makes an array of 6 chars. copy is a new array with its own copy of the letters, and the program may change them. char tag[10] = "tower"; fills the 4 bytes after the '\0' with zeros.
const char *name = "tower"; makes no array. name is a pointer to the bytes of the literal itself. A program must never change a string literal: writing into one is undefined behavior, and on Linux it usually stops the program with a segmentation fault. The const in const char * makes the compiler refuse such a write.
int visitors[6] = {40, 55, 31};
/* 40 55 31 0 0 0 */
char copy[] = "tower";
/* 6 bytes: t o w e r \0 */
copy[0] = 'T';
/* copy is "Tower" */
const char *name = "tower";
/* name points at the literal: read only */ char copy[] = "…" makes an array you may change; const char * points at a literal you must not change.
Coming from Java: String name = "tower" makes a reference to a String object that cannot change. char copy[] = "tower" is the 6 bytes themselves, and the program may change any of them.
Precisely: C11 6.7.9 and 6.4.5
6.7.9p21: when an initializer has fewer values than the array has elements, the rest are initialized as if they had static storage duration, so to 0. 6.7.9p22: an array of unknown size takes its size from the initializer. 6.7.9p14: a char array may be initialized by a string literal, and the '\0' is stored only if there is space for it. So char five[5] = "tower"; is valid C: five holds the 5 letters and no '\0', so it is not a string, and strlen(five) is undefined. C++ rejects the same line. 6.4.5p7: if a program tries to modify a string literal, the behavior is undefined. In C a literal's type is char[6], not const char[6], so char *name = "tower"; compiles with no warning, even with -Wall -Wextra; gcc's -Wwrite-strings gives literals a const type and warns. gcc stores literals in a read-only section of the program, which is why a write usually ends in a segmentation fault; on a microcontroller, literals often sit in flash memory, where a write may do nothing at all.
Experiment
Measure three declarations
visitors has 6 elements of 4 bytes, so sizeof gives 24, and the elements without a value in the braces are 0. copy has no size in its brackets, so the string literal sets it: 5 letters and the '\0', 6 bytes. copy is a new array, so changing copy[0] changes only copy. name is a pointer, 8 bytes in this build, that points at the literal’s own bytes. Because name is declared const char *, the compiler refuses any write through it.
Why it matters Declare char name[] = "…" when you need text you can change, and const char * when you only read a string literal.
Predict the three sizes on the first line and the three values on the second. Then change copy[0] = 'T'; to name[0] = 'T'; and predict what the compiler says.
#include <stdio.h>
int main(void) {
setvbuf(stdout, NULL, _IOLBF, 0); // line by line, as in a terminal
int visitors[6] = {40, 55, 31};
char copy[] = "tower";
const char *name = "tower";
copy[0] = 'T';
printf("%zu %zu %zu\n", sizeof visitors, sizeof copy, sizeof name);
printf("%d %s %s\n", visitors[5], copy, name);
return 0;
}
What to look for
After line 5 runs, visitors appears in main’s frame as {40, 55, 31, 0, 0, 0}. After line 6, copy shows "tower". After line 7, name shows an address and the text "tower": a char pointer is shown as the text it points to. copy has no address on screen, because copy is the array itself. After line 8, copy shows "Tower" and name still shows "tower". The run prints 24 6 8, then 0 Tower tower. After the edit, the build fails with error: assignment of read-only location ‘*name’, on line 8.
Expected output
24 6 8
0 Tower tower02 / Decay
Why does sizeof give a different answer inside a function?
In almost every expression, the name of an array is converted to a pointer to its first element. This conversion is called decay: the array decays to a pointer. So int *p = visits; makes p point at visits[0].
Two uses of an array do not decay. sizeof visits measures the whole array, in bytes. &visits gives the address of the whole array. (The third is a string literal that fills a char array, as in the section above.)
A function call is an ordinary expression, so an array argument decays too. The function receives a copy of the address of the first element, and nothing about the length. Function calls showed that a parameter written with brackets, such as const int visits[], is a pointer. So inside the function, sizeof visits is the size of a pointer, 8 bytes in the stepper’s build, and sizeof visits / sizeof visits[0] is 8 / 4 = 2, whatever the caller’s array holds.
gcc warns about this line: ‘sizeof’ on array function parameter ‘visits’ will return size of ‘const int *’. The fix is to compute the count where the array is declared, and pass it as a second parameter.
int busy_hours(const int visits[], size_t n) {
/* visits is a pointer: sizeof visits is 8 here */
int busy = 0;
for (size_t i = 0; i < n; i++) {
if (visits[i] >= 50) {
busy++;
}
}
return busy;
}
/* in main, where the array is declared: */
int visits[5] = {40, 55, 31, 62, 70};
size_t n = sizeof visits / sizeof visits[0]; /* 5 */
printf("%d\n", busy_hours(visits, n)); /* 3 */ An array passed to a function arrives as a pointer; pass its length beside it.
Coming from Go: A Go array [5]int is copied whole when it is passed, and len works inside the function. A C array argument passes only the address of its first element.
Coming from Rust: A Rust slice &[i32] carries a pointer and a length together. In C, the pointer and the length are two separate parameters, and the compiler does not check that they match.
Precisely: C11 6.3.2.1p3 and 6.7.6.3p7
6.3.2.1p3: an expression of array type is converted to a pointer to its first element, except when it is the operand of sizeof or unary &, or a string literal used to initialize an array. (N1570 also lists _Alignof there by mistake; C17 removed it.) 6.7.6.3p7 adjusts a parameter declared as “array of T” to “pointer to T”, so a size in the brackets, as in int visits[5], is not checked and does not change sizeof. int visits[static 5] promises the caller passes at least 5 elements; gcc may warn about a call that passes fewer, but nothing is checked at run time. A pointer to the whole array keeps the length in its type: with int (*all)[5] = &visits;, sizeof *all is 20. The stepper builds with gcc 12 on 64-bit ARM Linux (aarch64), where a pointer is 8 bytes and an int 4; on a 32-bit microcontroller the same line gives 4 / 4 = 1. gcc's -Wsizeof-array-argument is on by default.
Experiment
Count busy hours inside a function
main passes visits, which decays to the address of visits[0]. In busy_hours, visits is a pointer parameter, so sizeof visits is the size of a pointer, 8 bytes, and the loop runs while i < 8 / 4, that is, for two hours. Only 55 is counted. n carries the count that main computed where the array is declared, so the edit visits all 5 hours.
Why it matters Inside a function, an array parameter has no length. Use the count the caller passes, and read gcc’s warnings: they name this bug.
main passes 5 hours, and 3 of them have 50 visitors or more. Predict both printed numbers. Then change the for line’s condition i < sizeof visits / sizeof visits[0] to i < n and predict again.
#include <stdio.h>
#include <stddef.h>
int busy_hours(const int visits[], size_t n) {
int busy = 0;
for (size_t i = 0; i < sizeof visits / sizeof visits[0]; i++) {
if (visits[i] >= 50) {
busy++;
}
}
return busy;
}
int main(void) {
setvbuf(stdout, NULL, _IOLBF, 0); // line by line, as in a terminal
int visits[5] = {40, 55, 31, 62, 70};
size_t n = sizeof visits / sizeof visits[0];
printf("%zu %d\n", n, busy_hours(visits, n));
return 0;
}
What to look for
Under Compiler warnings, gcc reports two problems: ‘sizeof’ on array function parameter ‘visits’ will return size of ‘const int *’, and unused parameter ‘n’. In main, visits shows {40, 55, 31, 62, 70} and n shows 5. Step into busy_hours: visits shows an address and → 40, the first element, because it is a pointer. i takes the values 0 and 1, and the loop ends; 31, 62, and 70 are never read. busy ends at 1, and the run prints 5 1. After the edit, both warnings are gone, i runs from 0 to 4, busy ends at 3, and the run prints 5 3.
Expected output
5 103 / Strings
How does a string know where it ends?
A string in C is a char array whose text ends with '\0'. No length is stored. strlen, from <string.h>, walks from the first character to the first '\0' and returns the number of characters before it. Text, bytes, and Unicode showed that these are bytes, not letters.
sizeof and strlen answer different questions. In char name[8] = "cuckoo";, sizeof name is 8, the space in the array, and strlen(name) is 6, the length of the text in it.
Writing '\0' into the array ends the string earlier. After name[3] = '\0';, strlen(name) is 3, and printf with %s prints cuc. The other letters are still in the array, but no string function reads past the first '\0'.
A char array with no '\0' in it is not a string. strlen or printf with %s on it reads past the end of the array, which is undefined behavior.
char name[8] = "cuckoo";
/* 8 6 */
printf("%zu %zu\n", sizeof name, strlen(name));
name[3] = '\0';
/* 3 cuc */
printf("%zu %s\n", strlen(name), name); sizeof gives the space in an array; strlen gives the length of the text, up to the first '\0'.
Coming from Python: len(name) returns a length that Python stores with the string. strlen(name) must find the '\0' by walking every character.
Precisely: C11 7.1.1 and 7.24.6.3
7.1.1 defines a string as “a contiguous sequence of characters terminated by and including the first null character”, and 7.24.6.3 says strlen returns the number of characters that precede it. strlen reads every character each time it is called, so for (size_t i = 0; i < strlen(s); i++) walks the string again on every pass; store the length once. A char array that holds binary data, such as the bytes of an image, can contain zeros anywhere. Such data is not a string: keep its length in a variable, and copy it with memcpy, which takes a count of bytes.
Experiment
Move the end of a string
strlen walks from name[0] to the first '\0'. Writing '\0' at index 3 ends the string there, so strlen gives 3 and %s prints cuc, although the other letters are still in the array. sizeof name does not look at the text: it is always 8. The edit writes '!' over the '\0' at index 6. The next zero byte, from the initializer, is at index 7, the last byte of the array, so strlen gives 7.
Why it matters A string ends at its first zero byte, wherever that is in the array. sizeof tells you the space; strlen tells you the text.
Predict the four printed values. Then change name[3] = '\0'; to name[6] = '!'; and predict again.
#include <stdio.h>
#include <string.h>
int main(void) {
setvbuf(stdout, NULL, _IOLBF, 0); // line by line, as in a terminal
char name[8] = "cuckoo";
size_t before = strlen(name);
name[3] = '\0';
size_t after = strlen(name);
printf("%zu %zu %zu %s\n", sizeof name, before, after, name);
return 0;
}
What to look for
After line 6 runs, name shows "cuckoo\000": the debugger writes a zero byte as \000, and it leaves out the last zero byte of the array. before shows 6. After line 8, name shows "cuc\000oo\000": the letters o and o are still in the array, after a zero byte. after shows 3, and the run prints 8 6 3 cuc. After the edit, name shows "cuckoo!" after line 8, after shows 7, and the run prints 8 6 7 cuckoo!.
Expected output
8 6 3 cuc04 / String functions
What do the string functions need from you?
The string functions in <string.h> read each source string up to its '\0', and write wherever the destination pointer points. They receive only pointers, so they cannot know how much space the destination has.
- strcpy(dst, src) copies src and its '\0' into dst. dst needs strlen(src) + 1 bytes.
- strcat(dst, src) adds src after the text already in dst. dst needs strlen(dst) + strlen(src) + 1 bytes.
When dst is too small, both functions write past the end of the array. That is undefined behavior. C prints no error, and the bytes after the array, often another variable, are overwritten.
snprintf(dst, size, format, …), from <stdio.h>, takes the size of the destination. It writes at most size - 1 characters, then '\0', and returns the length that the whole text has. So a result of size or more means the text was cut. Where dst is an array declared in the same function, pass sizeof dst. A function that receives dst as a pointer needs the size as another parameter.
To compare text, use strcmp(a, b), which returns 0 when the characters are equal. == on two strings compares two addresses, as Equality and comparison showed.
char label[10];
int needed = snprintf(label, sizeof label, "%s %s",
"owl", "clock");
/* needed is 9: "owl clock" fits with its '\0' */
if (needed < 0 || (size_t)needed >= sizeof label) {
printf("the label was cut\n");
} strcpy and strcat trust you to give them enough space; snprintf takes the size and tells you when it cut the text.
Coming from JavaScript: label + " " + name builds a new string of the right length. strcat writes into an array that already exists, and the array does not grow.
Precisely: C11 7.24.2.3, 7.24.3.1, and 7.21.6.5
7.24.2.3 and 7.24.3.1: strcpy and strcat copy up to and including the '\0' of src, and copying between objects that overlap is undefined. 7.21.6.5p2–3: snprintf discards output after the n − 1st character, writes '\0' when n is not 0, and returns the number of characters that would have been written for a large enough n, or a negative value on an encoding error; the text is complete only if the result is nonnegative and less than n. (size_t)needed converts the int result to size_t, the type of sizeof, so that both sides of >= are unsigned; the test for a negative value comes first. strncpy and strncat look safer but have their own traps, shown in C pointer arithmetic and bounds, a later lesson. strlcpy and strlcat are in POSIX.1-2024 and glibc 2.38, not in ISO C. gcc's -Wstringop-overflow reports some writes past an array when it can see both sizes, and -fsanitize=address reports them at run time.
Experiment
Build a label that may not fit
snprintf receives the size of label, 10 bytes. "owl clock" has 9 characters, so all of them fit with the '\0', and the result is 9. "cuckoo clock" has 12 characters. snprintf writes the first 9 and the '\0', and still returns 12, the length of the whole text. gcc can see both lengths here, so it warns; with text that arrives while the program runs, it cannot.
Why it matters Give snprintf the size of the destination, and compare its result with that size: a result of the size or more means the text was cut.
Predict the three printed values. Then change "owl" to "cuckoo" in the snprintf line and predict again.
#include <stdio.h>
#include <string.h>
int main(void) {
setvbuf(stdout, NULL, _IOLBF, 0); // line by line, as in a terminal
char label[10];
int needed = snprintf(label, sizeof label, "%s %s", "owl", "clock");
printf("%d %zu %s\n", needed, strlen(label), label);
return 0;
}
What to look for
The first stop is at line 7: label has no initializer, so the debugger shows whatever bytes were already there, not text the program wrote. After line 7, label shows "owl clock" and needed shows 9. The run prints 9 9 owl clock. After the edit, Compiler warnings shows gcc's warning: ‘%s’ directive output truncated writing 5 bytes into a region of size 3. label shows "cuckoo cl", needed shows 12, and the run prints 12 9 cuckoo cl.
Expected output
9 9 owl clock05 / 2-D arrays
How do you pass a 2-D array?
int visits[2][4] is an array of 2 elements, and each element is an array of 4 ints: 2 floors of the museum, 4 hours each. This is a two-dimensional array. Its 8 ints are stored one row after the other: visits[0][0] to visits[0][3], then visits[1][0] to visits[1][3]. visits[1][2] is floor 1, hour 2.
The outer array decays like any other array. visits becomes a pointer to its first element, and that element is a whole row: an array of 4 ints. The type of that pointer is written int (*)[4].
A parameter for it is written int floors[][4]. The first size may be left empty, because the parameter is a pointer. The row length must be written, because floors[r][h] uses it to find where row r starts. Inside the function, sizeof floors[0] is still 16 bytes, one row. The number of rows is lost, so pass it as another parameter.
int grid_total(int floors[][4], size_t rows) {
int total = 0;
for (size_t r = 0; r < rows; r++) {
for (size_t h = 0; h < 4; h++) {
total += floors[r][h];
}
}
return total;
}
int visits[2][4] = {{40, 55, 31, 62}, {12, 20, 18, 9}};
printf("%d\n", grid_total(visits, 2)); /* 247 */ A 2-D array decays to a pointer to its first row; write the row length in the parameter and pass the number of rows.
Coming from Java: A Java int[][] is an array of references to separate row arrays, and each row has its own length. A C int[2][4] is one block of 8 ints, and every row has the same length.
Precisely: rows of other lengths and const
Passing an array with rows of another length, such as int wide[3][5], to int floors[][4] is a constraint violation: the pointer types differ. gcc reports it with -Wincompatible-pointer-types, which is a warning in gcc 12 and an error by default since gcc 14. The parameter here has no const, because in C11 an int (*)[4] does not convert to const int (*)[4] without a cast; gcc reports this only with -Wpedantic, and C23 allows the conversion. C99 also allows a parameter whose row length is another parameter, as in int grid_total(size_t rows, size_t cols, int floors[rows][cols]). Such variable length arrays are optional in C11: a compiler without them defines __STDC_NO_VLA__. gcc supports them.
Experiment
Pass a table of floors and hours
visits decays to a pointer to its first row, an array of 4 ints. The parameter int floors[][4] has that type, so floors[r][h] can find row r: 16 bytes for each row. sizeof floors[0] is one row, 16 bytes, and the row length is part of the type. The number of rows is not, so it travels as rows.
Why it matters For a 2-D array, write the row length in the parameter and pass the number of rows.
Predict the three printed numbers. Then change grid_total(visits, 2) to grid_total(visits, 1) and predict again.
#include <stdio.h>
#include <stddef.h>
int grid_total(int floors[][4], size_t rows) {
size_t row_bytes = sizeof floors[0];
int total = 0;
for (size_t r = 0; r < rows; r++) {
for (size_t h = 0; h < 4; h++) {
total += floors[r][h];
}
}
printf("%zu bytes per floor\n", row_bytes);
return total;
}
int main(void) {
setvbuf(stdout, NULL, _IOLBF, 0); // line by line, as in a terminal
int visits[2][4] = {{40, 55, 31, 62}, {12, 20, 18, 9}};
int total = grid_total(visits, 2);
printf("%zu %d\n", sizeof visits, total);
return 0;
}
What to look for
In main, visits shows {{40, 55, 31, 62}, {12, 20, 18, 9}}. Step into grid_total: floors shows an address and → {40, 55, 31, 62}, the first row, because floors is a pointer to a row. After line 5, row_bytes shows 16. total grows through the 8 values and ends at 247. The run prints 16 bytes per floor, then 32 247. After the edit, rows shows 1, the outer loop runs once, total ends at 188, and the run prints 16 bytes per floor, then 32 188.
Expected output
16 bytes per floor
32 24706 / In production
What goes wrong with arrays and strings in real programs?
The rules in this lesson explain four bugs that C programmers meet in real programs.
A length computed with sizeof on a parameter. Code that worked in main, such as sizeof title - 1 for the length of a title, is moved into a function and keeps the same line. It still compiles, and it now gives 7, the size of a pointer minus 1, for every title. gcc warns when the parameter is written with brackets, and says nothing when it is written const char *title. Use strlen for a string, and a count parameter for any other array.
strcat into an array without enough space. A program joins parts into a line with strcat in a loop. Every test uses short parts, and the first long part overwrites the variable stored after the line. Give the size to the code that writes, and check it before each write; snprintf does this for you.
== on strings. gcc stores two identical string literals once, so label == wanted can be true in a test where both are literals, and false when the same text comes from input or from a copy. The experiment below shows both. Compare with strcmp.
Writing into a string literal. char *name = "cuckoo"; name[0] = 'C'; compiles without a warning, because in C a literal's type has no const. On Linux it usually stops the program with a segmentation fault. Declare const char * for a literal, and a char array for text you change.
const char *fixed = "cuckoo"; /* read only */
char editable[] = "cuckoo"; /* your own copy */
editable[0] = 'C'; /* "Cuckoo" */ Measure strings with strlen, check the space before you write, compare with strcmp, and never write into a literal.
Precisely: what the tools catch
6.4.5p7 leaves it unspecified whether two string literals with the same text are distinct arrays, so label == wanted may be 1 with one compiler or option and 0 with another; gcc merges identical literals even at -O0. -Wwrite-strings makes gcc warn about char *name = "cuckoo";. -fsanitize=address reports a strcat that writes past a stack or heap array when it happens, but not a write that stays inside a larger struct. No compiler option turns == on two strings into a warning, because comparing two addresses is valid C.
Experiment
Compare two names with ==
wanted and label point at string literals with the same text, and gcc stored that text once, so the two addresses are equal. typed is an array with its own copy of the text, so its address differs, although strcmp finds the same characters. After the edit, typed also points at the shared literal, and == gives 1 three times.
Why it matters == on strings can pass a test by luck when both sides are literals. Compare text with strcmp.
Predict the three numbers. Then change char typed[] = "cuckoo"; to const char *typed = "cuckoo"; and predict again.
#include <stdio.h>
#include <string.h>
int main(void) {
setvbuf(stdout, NULL, _IOLBF, 0); // line by line, as in a terminal
const char *wanted = "cuckoo";
const char *label = "cuckoo";
char typed[] = "cuckoo";
int same_label = label == wanted;
int same_typed = typed == wanted;
int same_text = strcmp(typed, wanted) == 0;
printf("%d %d %d\n", same_label, same_typed, same_text);
return 0;
}
What to look for
After lines 6 and 7, wanted and label show the same address, followed by "cuckoo". typed shows "cuckoo" with no address: it is an array in main’s frame. same_label shows 1, same_typed 0, and same_text 1, and the run prints 1 0 1. After the edit, typed shows the same address as wanted, and the run prints 1 1 1. Which literals share an address is a choice of this build (gcc 12 on aarch64), not a rule.
Expected output
1 0 107 / Try it
Can you tell each size from each length?
Work out each number before you read the printf line. Remember where hall is declared, and what measure receives.
#include <stdio.h>
#include <string.h>
size_t measure(const char text[]) {
return sizeof text;
}
int main(void) {
setvbuf(stdout, NULL, _IOLBF, 0); // line by line, as in a terminal
char hall[10] = "atrium";
int floors[3][4];
hall[2] = '\0';
printf("%zu %zu %zu %zu\n", sizeof hall, strlen(hall),
measure(hall), sizeof floors[1]);
return 0;
}
Precisely: sizeof does not read
floors is never given a value and never read. C11 6.5.3.4p2: the operand of sizeof is not evaluated, unless it is a variable length array, so sizeof floors[1] uses only the type of floors[1], int[4].
What does it print in the stepper’s build? Which of the four numbers would change on a machine with 4-byte pointers?
Reveal the reasoning
10 2 8 16. sizeof hall is the whole array, 10 bytes, because hall is declared in main. hall[2] = '\0' ends the text after "at", so strlen gives 2; the letters "rium" are still in the array. In measure, text is a pointer, so sizeof text is 8, the size of a pointer in this build, and gcc warns about that line. floors[1] is one row of 4 ints, 16 bytes. Only the third number depends on the size of a pointer: with 4-byte pointers it would be 4.
Practice tickets
- C-501 · Book titles print off-centre on library labels → Free
- C-502 · The checkout screen shows the end of the previous title → Sign in · Pro
- C-503 · Tickets for many exhibitions print a damaged price → Sign in · Pro
Keep exploring
C pointer arithmetic and bounds →
Primary references
- C11 draft standard (N1570): 6.3.2.1 Conversions, 6.4.5 String literals, 6.7.6.3 Function declarators, 6.7.9 Initialization, 7.24 String handling ↗
- cppreference: Array declaration (C) ↗
- cppreference: Null-terminated byte strings (C) ↗
- cppreference: snprintf ↗
- GCC manual: Warning Options (-Wsizeof-array-argument, -Wwrite-strings) ↗