If you are declaring a variable inside a function, the question makes no sense. The distinction exists only if the item can be used in a place where it is not defined. I would suggest not thinking about that as some opposing concepts either. Those are not two ends of a single idea, but two separate concepts that are only related to each other.
First, you can define something. By that you are providing a concrete definition, a concrete instance. Using a car analogy someone mentioned earlier, you are building an actual car.
Now imagine you must tell about that car to someone, who has never seen it and the car is not anywhere around. That is the declaration. You are telling the compiler that there exists something with such and such features. So the compiler knows it exists, and you can refer to it. But it is not actually creating anything.
A situation like that arises commonly in two cases. First, if there are multiple compilation units and they must share some definitions. Only one compilation unit contains the definition. But others, which use it, must somehow know that definition exists and what it is. For those units you provide a declaration. For example if you have function doSomething in compilation unit fun.c, and you want main.c to use it, main.c must be told such a thing exists:// fun.c
#include <stdio.h>
// Definition of `doSomething`
void doSomething(int x) {
printf("Do something: %d\n", x);
}
// main.c
void doSomething(int); // Declaration of `doSomething`
int main(void) {
doSomething(12);
return 0;
}The declaration tells compiler, while it compiles “main.c”, that there is some function called doSomething, which accepts a single int argument and returns nothing. It doesn’t tell what it does, how it is implemented or where to find it. It just provides minimum information required for the later invocation to be syntactically valid.
Similarly, for variables:// var.c
int x = 12; // Definition of `x`
// main.c
#include <stdio.h>
extern int x; // Declaration of `x`
int main(void) {
printf("x = %d\n", x);
return 0;
}
Another case is a single compilation unit, where e.g. functions are referencing each other. Obviously the earlier one will not know about the latter one. For this reason the latter one must be declared earlier, so it is known to the compiler.
The situation with extern and static is a bit complicated, because of C’s history. Basically it’s a mix of shorthand notation, where linkage is implied, and assumptions from decades ago. It’s not illogical: it’s just permitting things to be not explicitly stated, with the defaults being defined by ancient conventions.