struct introduces a new type, but typedef merely creates an alias for another type. This declaration creates a new type called struct Foo:
struct Foo { int bar; };
This declaration is simply invalid:
typedef Foo { int bar; };
However, this declaration is valid, and creates a new, unnamed type, and also creates an alias to that type called Foo:
typedef struct { int bar; } Foo;
You can also create an alias to a named type - this creates an alias to the type struct Foo called Foo:
typedef struct Foo Foo;
These two names are completely interchangeable - you can pass a struct Foo to a function declared as taking an argument of type Foo, for example.
You can create aliases for any types, including built-in types. For example, you can create an alias for int called Foo:
typedef int Foo;