It may help in thinking about this to translate between the "<<" operator syntax and the "operator<<" function syntax. Your C++ example is equivalent to this bit of C++ code:
operator<< ( operator<< (cout, "Hello World!"), endl);
The first thing that you should notice here is that there is not actually a lot of cleverness in cout at all. What is clever is the operator<< function -- specifically, the version of the operator<< function that takes a stream object (which is what cout is, but many other things are too) as its first argument. Or, even more precisely, the range of operator<< functions that take a stream object as the first argument, and take a particular thing as the second argument -- there's one for each type of object that you can put into the cout stream. You can see one of the C++ tricks in that syntax, too; the operator<< functions on stream objects always return the stream object that they were given, thereby allowing chaining of this nature.
In order to put C++ code into linkers and system ABIs that expect C-like function syntax, most C++ compilers "mangle" the function names, in order to encode in them the type of arguments that they have. (Also, of course, the "<<" isn't a valid C-like function name.) So, if you looked at the generated assembly for this bit of function, you'd see that the names of the two functions were different from each other -- they'd have suffixes indicating the argument types. You could do something like that manually:
operator_lshift__stream__endl(
operator_lshift__stream__string(cout, "Hello World!"), endl);
And there you've got something that you could implement in C.