This question should be moved to stackoverflow. Maybe someone will do that.
Am I correct?
Essentially, yes.
Each BAR holds the address of a communication area. This address can be set and read by the operating system as part of the larger device configuration.
For each BAR (independently), the device stores only some of the bits that the processor might write and it ignores others. Of the 28 possible bits (in 32-bit BAR configuration) the device stores only some number of upper bits and ignores the remainder lower bits.
Due to this mechanism, the specification allows only for sizes of the communication area that are powers of 2, and only alignment within the address space of the same power of 2.
If the operating system writes a -1 (i.e. all 1's) to a BAR, and reads it back, it will get back those 1's only for the (upper) bits that this BAR in the device stores/represents, and get back zeros for lower bits this BAR doesn't store/represent.
The number of ones (zeros) it gets back tells the operating system what it needs to know, which is how many bits are (or aren't) represented in the BAR. From that it can easily determine the size of the address space that the device wants, and the alignment it expects. See here for more detail.
After allocating appropriate address space, it writes that real value to the BAR (replacing the prior written -1). The BAR is now properly configured.
NB: The operating system allocates address space, not actual memory: it is as if the memory for the communication area is being supplied by the device, and that the processor can address it starting at this location.
The operating system doesn't really formally ask the hardware for size & alignment of the communication area, but rather uses this trick of writing all 1's and observing the effect.
The hardware doesn't have to know that the operating system is "just kidding" about the -1 (or otherwise that it is actually going about the making of an inquiry); it simply does what it always does with a write to a BAR (i.e. storing the upper N bits), and it honors a (BAR) read request as it normally would (by returning those upper N bits concatenated with zeros for the unstored lower bits).