The simplest way to do this is to make one or both of the forms of B local to the complex type of its parent. If A and C are both top-level elements, and we make both forms of B local, for example, you might write:
<xs:element name="A">
<xs:complexType>
<xs:sequence>
<xs:element name="B" type="B-in-A"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="C">
<xs:complexType>
<xs:sequence>
<xs:element name="C" type="B-in-C"/>
</xs:sequence>
</xs:complexType>
</xs:element>
The two types B-in-A and B-in-C are declared however you like, e.g.
<xs:complexType name="B-in-A">
<xs:sequence/>
<xs:attribute name="AttrA" type="xs:integer"/>
<xs:attribute name="AttrB" type="xs:integer"/>
</xs:complexType>
<xs:complexType name="B-in-C">
<xs:sequence/>
<xs:attribute name="AttrB" type="xs:integer"/>
<xs:attribute name="AttrC" type="xs:integer"/>
</xs:complexType>
In some contexts, you may want to declare a generic type for B and derive the two types B-in-A and B-in-C from it, so that they have some sort of genetic relation to each other; it depends on whether you have way to exploit such derivation relations.
In XSD 1.1, you also have the option of declaring B with a required AttrB and optional AttrA and AttrC, and then putting assertions on the types of A and C to check that a B inside an A carries AttrA and not AttrC, and the converse for a B inside a C.
Which of these feels clearer and simpler may depend on whether you think of the difference in the attributes of B as (a) a difference in the behavior of B based on its context, or (b) a constraint on A and/or C that takes the form of requiring a particular form for its child.