gcc actually produces assembler and assembles it using the as assembler. Not all compilers do this – the MS compilers produce object code directly, though you can make them generate assembler output. Translating assembler to object code is a pretty simple process, at least compared with C→Assembly or C→Machine-code translation.
Some compilers produce other high-level language code as their output – for example, cfront, the first C++ compiler, produced C as its output which was then compiled to machine code by a C compiler.
Note that neither direct compilation or assembly actually produce an executable. That is done by the linker, which takes the various object code files produced by compilation/assembly, resolves all the names they contain and produces the final executable binary.