Classes are structs, but Class is a pointer type, being defined as
typedef struct objc_class *Class;
and this answers the first part of the question.
Now, if you take a look at <objc/objc.h> you'll find
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
and in <obj/runtime.h> you'll find
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
meaning that in Objective-C 2.0 objc_object and objc_class are identical structs, both having an isa field.
That's why you can pass a Class where an id is required: classes are objects after all.
You would normally expect a warning about the incompatible pointers type, but apparently the Obj-C compiler uses an ad-hoc treatment this specific case.
I have no references to support this, though.
EDIT
I finally found a reference in the clang source code:
In ASTContext.cpp, this is the Class declaration
TypedefDecl *ASTContext::getObjCClassDecl() const {
if (!ObjCClassDecl) {
QualType T = getObjCObjectType(ObjCBuiltinClassTy, 0, 0);
T = getObjCObjectPointerType(T);
TypeSourceInfo *ClassInfo = getTrivialTypeSourceInfo(T);
ObjCClassDecl = TypedefDecl::Create(const_cast<ASTContext &>(*this),
getTranslationUnitDecl(),
SourceLocation(), SourceLocation(),
&Idents.get("Class"), ClassInfo);
}
return ObjCClassDecl;
}
and this is the id declaration
TypedefDecl *ASTContext::getObjCIdDecl() const {
if (!ObjCIdDecl) {
QualType T = getObjCObjectType(ObjCBuiltinIdTy, 0, 0);
T = getObjCObjectPointerType(T);
TypeSourceInfo *IdInfo = getTrivialTypeSourceInfo(T);
ObjCIdDecl = TypedefDecl::Create(const_cast<ASTContext &>(*this),
getTranslationUnitDecl(),
SourceLocation(), SourceLocation(),
&Idents.get("id"), IdInfo);
}
return ObjCIdDecl;
}
In both cases the type is set to the result of getObjCObjectPointerType. This causes the compiler to consider both Class and id as pointers to Objective-C objects.