Take a look at this program:

#include <stdio.h>
 
int main() {
	int rpm;
	return 0;
}

Here rpm is a variable definition. It lives in the local scope of main. You can also define a variable in the global scope:

#include <stdio.h>
 
int rpm;
 
int main() {
	return 0;
}

When you define a variable, the compiler sets aside memory for it.

A declaration is different. It only tells the compiler that a variable exists somewhere, and no memory gets allocated. Here’s an example:

#include <stdio.h>
 
extern int rpm;
 
int main() {
	rpm = 500;
	printf("Engine rpm is: %d", rpm);
	return 0;
}
ld: /tmp/ccIHzbst.o: warning: relocation against `rpm' in read-only section `.text'
ld: /tmp/ccIHzbst.o: in function `main':
main.c:(.text+0x6): undefined reference to `rpm'
ld: main.c:(.text+0x10): undefined reference to `rpm'
ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status

This fails. The extern keyword tells the compiler that rpm is defined in some other file, not in main.c. We never provided that other file, so the linker found the declaration but no definition. Nothing ever allocated memory for rpm, and that’s why you see undefined reference to rpm.

To fix it, define rpm in another file and compile both together:

// other.c
int rpm;
gcc main.c other.c -o main