The standard defines pointer comparison like this basically from [expr.eq#3]
Otherwise, if the pointers are both null, both point to the same function, or both represent the same address, they compare equally.
Representing the same address means this from [basic.compound#def:represents_the_address]
A value of a pointer type that is a pointer to or past the end of an object represents the address of the first byte in memory ([intro.memory]) occupied by the object34 or the first byte in memory after the end of the storage occupied by the object, respectively
So it is really about the address, but if so then why this code does not fail as technically d_ptr and b_ptr point to objects with different addresses? This looks like it is about objects, rather than addresses as complier can figure out both d_ptr and b_ptr point to the same complete object at the end!
struct B { int a;};
struct B1 { int b; };
struct D : B, B1 {};
struct A {};
....
D obj;
D *d_ptr = &obj;
B1 *b_ptr = d_ptr;
assert(d_ptr == b_ptr);//should have failed as d_ptr and b_ptr point to objects with diffrent addresses!
You are missing this passage
If at least one of the operands is a pointer, pointer conversions, function pointer conversions, and qualification conversions are performed on both operands to bring them to their composite pointer type.
A pointer conversion can change the address that the pointer represents, and that's exactly what happens in your case.