All citations in this message are from the C standard, not the C++ one.
The only difference between S1 and S2 involves a feature ("public:") not
supported by C99. If you drop that feature, they are compatible types,
but only if declared in separate translation units (6.2.7p1), and
nothing changed in that regard in C99.
6.2.7p1 is a peculiar feature of the C standard that deserves some
explanation. You might think that two struct types declared identically
in different translation units would be considered to declare the same
type, but "The presence of a struct-declaration-list in a
struct-or-union-specifier declares a new type, within a translation
unit." The fact that the type's scope is restricted to that translation
unit means that the rule that "Two types have compatible type if their
types are the same." (6.2.7p1) doesn't allow you to say that struct
types defined identically in different translation units are compatible
with each other. Therefore, 6.2.7p1 was added to say that they are.
However, the C standard deliberately restricts that rule to types
declared in separate translation units. If you define two struct types
identically in one translation unit, it's assumed that you deliberately
intended them to be distinct types, and that you would therefore want to
get the diagnostic messages you will get if you accidentally mix them up
in contexts where relevant types are required to be compatible.
The fact that they are incompatible allows certain optimizations, based
mainly upon the anti-aliasing rules, but because they would be
compatible with identical declarations in a different translation unit,
an implementation is not free to lay them out differently unless it can
be certain that they won't be used for communication between translation
units.