I'm surprised, though, that there is no mention of struct or malloc in the Arduino Language Reference, which are very important features of C. Are they allowed by the compiler?
Yes, the "Arduino compiler" is just a plain, somewhat older avr-gcc, so it will accept every legal C or C++ code.
Actually the only thing the Arduino IDE does with ".ino" sketches is to add an "#include <arduino.h>" at the top of the file and then pass it on to the compiler. The IDE also looks at the other includes to determine which libraries the sketch uses and copies the code for these libraries to the temporary working directory for compiling and linking with the transformed sketch.
The Arduino core framework is a static library which gets linked in as well as the normal avr-libc, so all functions of the avr-libc are available.
The Arduino core also has the main() function, so the sketches don't need one. But this main() is really simple, basically it just contains
main() {
init(); // Set up the arduino hardware, mainly the timers
setup(); // call the setup function of the sketch
while(true) {
loop(); // call the loop function of the sketch
}
}
I am just finishing a slightly more complex Arduino project, driving a stepper and a few motors, written in plain C++ and a bit of C, using the Eclipse IDE with the AVR Plugin instead of the overly simplistic Arduino IDE. Other than the lack of the main() function and the use of some Arduino convenience functions (Serial port stuff and the digitalWrite() functions) it should be indistinguishable from any other AVR project. It even uses Timer interrupts which are not supported by the Arduino Framework (which was a bit of a struggle because the Arduino core assumes it has full control over the timers for its PWM stuff).
That malloc(), structs and other stuff is not documented in the Arduino Reference Library is probably deliberate. They target beginners and the use of these "advanced" features is bound to cause problems for novices. For my project I had some trouble with dynamically created C++ Objects (the C++ way of mallocing structs) causing memory fragmentation so I removed them and made every Object static, which is much more predictable and deterministic in such a limited environment.