Volatile Pointer to Non Volatile Data - c++

Suppose I have the following declaration:
int* volatile x;
I believe that this defines a volatile pointer "normal" variable.
To me this could mean one of two things:
First Guess
The pointer can change, but the number will not change without notice. This means that some other thread (that the compiler doesn't know about) can change the pointer, but if the old pointer was pointing to a "12" then the new pointer (the new value of the pointer, because the thread changes it) would point to another "12".
To me this seems fairly useless, and I would assume that this is not what the real operation is.
Second Guess
The pointer can change, and thus if the pointer changes, the compiler must reload the value in the pointer before using it. But if it verifies that the pointer did not change (with an added check), it can then assume that the value it points to remained the same also.
So my question is this:
What does declaring a volatile pointer to non volatile data actually do?

int *volatile x; declares a volatile pointer to a non-volatile int.
Whenever the pointer is accessed, the volatile qualifier guarantees that its value (the value of the pointer) is re-read from memory.
Since the pointed-to int is non-volatile, the compiler is allowed to reuse a previously cached value at the address pointed to by the current value of the pointer. Technically this is allowed regardless of whether the pointer has changed or not, as long as there exists a cached value originally retrieved from the current address.
[ EDIT ] To address #DavidSchwartz's comment, I should note that "re-read from memory" is a (not pedantically precise, but AFAIK commonly used) shorthand for "as if it were re-read from memory in the abstract machine".
For example, C11 draft N1570 6.7.3/7 says:
An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects. Therefore any expression referring to such an object shall be evaluated strictly according to the rules of the abstract machine, as described in 5.1.2.3. Furthermore, at every sequence point the value last stored in the object shall agree with that prescribed by the abstract machine, except as modified by the unknown factors mentioned previously (134). What constitutes an access to an object that has volatile-qualified type is implementation-defined.
The same draft has a footnote for 6.5.16/3 (assignment operators):
The implementation is permitted to read the object to determine the value but is not required to, even when the object has volatile-qualified type
So in the end volatile does not require a physical memory read, but the observable behavior of a compliant implementation must be as if one was made regardless.

The volatile means that the value of the pointer (i.e., the memory location that it points to) can change; consequently, the compiler must ensure that the various caches have the same value for that pointer or load the pointer from memory for every read and write it to memory for every write.
The volatile says nothing about the pointed-to value, however. So it can change and may have different values in different threads.

Related

Could reinterpret_cast turn an invalid pointer value into a valid one?

Consider this union:
union A{
int a;
struct{
int b;
} c;
};
c and a are not layout-compatibles types so it is not possible to read the value of b through a:
A x;
x.c.b=10;
x.a+x.a; //undefined behaviour (UB)
Trial 1
For the case below I think that since C++17, I also get an undefined behavior:
A x;
x.a=10;
auto p = &x.a; //(1)
x.c.b=12; //(2)
*p+*p; //(3) UB
Let's consider [basic.type]/3:
Every value of pointer type is one of the following:
a pointer to an object or function (the pointer is said to point to the object or function), or
a pointer past the end of an object ([expr.add]), or
the null pointer value ([conv.ptr]) for that type, or
an invalid pointer value.
Let's call this 4 pointer values categories as pointer value genre.
The value of a pointer may transition from of the above mentioned genre to an other, but the standard is not really explicit about that. Fill free to correct me if I am wrong. So I suppose that at (1) the value of p is a pointer to value. Then in (2) a life ends and the value of p becomes an invalid pointer value. So in (3) I get UB because I try to access the value of an object (a) out of its lifetime.
Trial 2
Now consider this weird code:
A x;
x.a=10;
auto p = &x.a; //(1)
x.c.b=12; //(2)
p = reinterpret_cast<int*>(p); //(2')
*p+*p; //(3) UB?
Could the reinterpret_cast<int*>(p) change the pointer value genre from invalid pointer value to a pointer to value.
reinterpret_cast<int*>(p) is defined to be equivalent to static_cast<int*>(static_cast<void*>(p)), so let's consider how is defined the static_cast from void* to int*, [expr.static.cast]/13:
A prvalue of type “pointer to cv1 void” can be converted to a prvalue of type “pointer to cv2 T”, where T is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. If the original pointer value represents the address A of a byte in memory and A does not satisfy the alignment requirement of T, then the resulting pointer value is unspecified. Otherwise, if the original pointer value points to an object a, and there is an object b of type T (ignoring cv-qualification) that is pointer-interconvertible with a, the result is a pointer to b. Otherwise, the pointer value is unchanged by the conversion.
So in our case the original pointer pointed to the object a. So I suppose the reinterpret_cast will not help because a is not within its lifetime. Is my reading to strict? Could this code be well defined?
Then in (2) a life ends and the value of p becomes an invalid pointer value.
Incorrect. Pointers only become invalid when they point into memory that has ended its storage duration.
The pointer in this case becomes a pointer to an object outside of its lifetime. The object it points to is gone, but the pointer is not "invalid" in the way the specification means it. [basic.life] spends quite a bit of time explaining what you can and cannot do to pointers to objects outside of their lifetime.
reinterpret_cast cannot turn a pointer to an object outside of its lifetime into a pointer to a different object that is within its lifetime.
The notion of objects in the standard is rather abstract and differs somewhat from intuition. An object may be within its lifetime or not, and objects not within their lifetimes can have the same address, this is why unions work at all: the definition of active member is "the member that is within its lifetime".
A pointer to an object not within its lifetime is still a pointer to object. reinterpret_cast only casts between the type of the pointer, but not its validity. The UB you get with casting to non-pointer-interconvertible types are due to the strict-aliasing rule, not due to the validity of the pointer.
In all your trials, including your follow up question, you are using an object not within its lifetime in ways that aren't allowed, ie accessing it, and are consequently UB.
Every version to date of the C and C++ Standards has been ambiguous or contradictory with regard to what can be done with addresses of union members. The authors of the C Standard didn't want to require that compilers make pessimistic allowances for the possibility that functions might be invoked by constructs like:
someFunction(&myUnion.member1, &myUnion.member2);
in cases where function would cause the value one member of myUnion would be changed between access made via the other. While the ability to take union members' addresses would have been pretty useless if code couldn't do things like:
someFunction1(&myUnion.member1);
someFunction2(&myUnion.member2);
someFunction3(&myUnion.member1);
the authors of the Standard expected that quality implementations intended for various purposes would process constructs that Undefined Behavior "in a documented fashion characteristic of the environment" when doing so would best serve those purposes, and thus thought that making support for such constructs be a quality-of-implementation issue would be simpler than trying to formulate precise rules for which patterns must be supported. A compiler that generated code for the called functions in the second example without knowing their calling context wouldn't be able to interleave accesses performed by the two functions, and a quality compiler that expanded them inline while processing the above code would have no trouble noticing when each pointer was derived from myUnion.
The authors of the C89 Standard didn't think it necessary to define precise rules for how pointers to union members behave, because they thought compiler writers' desire to produce quality implementations would drive them to handle appropriate cases sensibly even without such rules. Unfortunately, some compiler writers were too lazy to handle cases like the second example above, and rather than recognizing that there was never any reason for quality compilers to be incapable of handling such cases, the authors of later C and C++ Standards have bent over backward to come up with weirdly contorted, ambiguous, and contradictory rules that justify such compiler behavior.
As a result, the address-of operator should only be regarded as meaningfully applicable to union members in cases where the resulting pointer will be used for accessing individual bytes of storage, either using character-types directly, or passing to functions like memcpy that are defined in such fashion. Unless or until there's a major revamp of the Standard, or an appendix that describes means by which implementations can offer optional guarantees beyond what the Standard requires, it would be best to pretend that union members are--like bitfields--lvalues that don't have addresses.

Unaligned access through reinterpret_cast

I'm in the middle of a discussion trying to figure out whether unaligned access is allowable in C++ through reinterpret_cast. I think not, but I'm having trouble finding the right part(s) of the standard which confirm or refute that. I have been looking at C++11, but I would be okay with another version if it is more clear.
Unaligned access is undefined in C11. The relevant part of the C11 standard (§ 6.3.2.3, paragraph 7):
A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined.
Since the behavior of an unaligned access is undefined, some compilers (at least GCC) take that to mean that it is okay to generate instructions which require aligned data. Most of the time the code still works for unaligned data because most x86 and ARM instructions these days work with unaligned data, but some don't. In particular, some vector instructions don't, which means that as the compiler gets better at generating optimized instructions code which worked with older versions of the compiler may not work with newer versions. And, of course, some architectures (like MIPS) don't do as well with unaligned data.
C++11 is, of course, more complicated. § 5.2.10, paragraph 7 says:
An object pointer can be explicitly converted to an object pointer of a different type. When a prvalue v of type “pointer to T1” is converted to the type “pointer to cv T2”, the result is static_cast<cv T2*>(static_cast<cv void*>(v)) if both T1 and T2 are standard-layout types (3.9) and the alignment requirements of T2 are no stricter than those of T1, or if either type is void. Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified.
Note that the last word is "unspecified", not "undefined". § 1.3.25 defines "unspecified behavior" as:
behavior, for a well-formed program construct and correct data, that depends on the implementation
[Note: The implementation is not required to document which behavior occurs. The range of possible behaviors is usually delineated by this International Standard. — end note]
Unless I'm missing something, the standard doesn't actually delineate the range of possible behaviors in this case, which seems to indicate to me that one very reasonable behavior is that which is implemented for C (at least by GCC): not supporting them. That would mean the compiler is free to assume unaligned accesses do not occur and emit instructions which may not work with unaligned memory, just like it does for C.
The person I'm discussing this with, however, has a different interpretation. They cite § 1.9, paragraph 5:
A conforming implementation executing a well-formed program shall produce the same observable behavior as one of the possible executions of the corresponding instance of the abstract machine with the same program and the same input. However, if any such execution contains an undefined operation, this International Standard places no requirement on the implementation executing that program with that input (not even with regard to operations preceding the first undefined operation).
Since there is no undefined behavior, they argue that the C++ compiler has no right to assume unaligned access don't occur.
So, are unaligned accesses through reinterpret_cast safe in C++? Where in the specification (any version) does it say?
Edit: By "access", I mean actually loading and storing. Something like
void unaligned_cp(void* a, void* b) {
*reinterpret_cast<volatile uint32_t*>(a) =
*reinterpret_cast<volatile uint32_t*>(b);
}
How the memory is allocated is actually outside my scope (it is for a library which can be called with data from anywhere), but malloc and an array on the stack are both likely candidates. I don't want to place any restrictions on how the memory is allocated.
Edit 2: Please cite sources (i.e., the C++ standard, section and paragraph) in answers.
Looking at 3.11/1:
Object types have alignment requirements (3.9.1, 3.9.2) which place restrictions on the addresses at which an object of that type may be allocated.
There's some debate in comments about exactly what constitutes allocating an object of a type. However I believe the following argument works regardless of how that discussion is resolved:
Take *reinterpret_cast<uint32_t*>(a) for example. If this expression does not cause UB, then (according to the strict aliasing rule) there must be an object of type uint32_t (or int32_t) at the given location after this statement. Whether the object was already there, or this write created it, does not matter.
According to the above Standard quote, objects with alignment requirement can only exist in a correctly aligned state.
Therefore any attempt to create or write an object that is not correctly aligned causes UB.
EDIT This answers the OP's original question, which was "is accessing a misaligned pointer safe". The OP has since edited their question to "is dereferencing a misaligned pointer safe", a far more practical and less interesting question.
The round-trip cast result of the pointer value is unspecified under those circumstances. Under certain limited circumstances (involving alignment), converting a pointer to A to a pointer to B, and then back again, results in the original pointer, even if you didn't have a B in that location.
If the alignment requirements are not met, than that round trip -- the pointer-to-A to pointer-to-B to pointer-to-A results in a pointer with an unspecified value.
As there are invalid pointer values, dereferencing a pointer with an unspecified value can result in undefined behavior. It is no different than *(int*)0xDEADBEEF in a sense.
Simply storing that pointer is not, however, undefined behavior.
None of the above C++ quotes talk about actually using a pointer-to-A as a pointer-to-B. Using a pointer to the "wrong type" in all but a very limited number of circumstances is undefined behavior, period.
An example of this involves creating a std::aligned_storage_t<sizeof(T), alignof(T)>. You can construct your T in that spot, and it will live their happily, even though it "actually" is an aligned_storage_t<sizeof(T), alignof(T)>. (You may, however, have to use the pointer returned from the placement new for full standard compliance; I am uncertain. See strict aliasing.)
Sadly, the standard is a bit lacking in terms of what object lifetime is. It refers to it, but does not define it well enough last I checked. You can only use a T at a particular location while a T lives there, but what that means is not made clear in all circumstances.
All of your quotes are about the pointer value, not the act of dereferencing.
5.2.10, paragraph 7 says that, assuming int has a stricter alignment than char, then the round trip of char* to int* to char* generates an unspecified value for the resulting char*.
On the other hand, if you convert int* to char* to int*, you are guaranteed to get back the exact same pointer as you started with.
It doesn't talk about what you get when you dereference said pointer. It simply states that in one case, you must be able to round trip. It washes its hands of the other way around.
Suppose you have some ints, and alignof(int) > 1:
int some_ints[3] ={0};
then you have an int pointer that is offset:
int* some_ptr = (int*)(((char*)&some_ints[0])+1);
We'll presume that copying this misaligned pointer doesn't cause undefined behavior for now.
The value of some_ptr is not specified by the standard. We'll be generous and presume it actually points to some chunk of bytes within some_bytes.
Now we have a int* that points to somewhere an int cannot be allocated (3.11/1). Under (3.8) the use of a pointer to an int is restricted in a number of ways. Usual use is restricted to a pointer to an T whose lifetime has begun allocated properly (/3). Some limited use is permitted on a pointer to a T which has been allocated properly, but whose lifetime has not begun (/5 and /6).
There is no way to create an int object that does not obey the alignment restrictions of int in the standard.
So the theoretical int* which claims to point to a misaligned int does not point to an int. No restrictions are placed on the behavior of said pointer when dereferenced; usual dereferencing rules provide behavior of a valid pointer to an object (including an int) and how it behaves.
And now our other assumptions. No restrictions on the value of some_ptr here are made by the standard: int* some_ptr = (int*)(((char*)&some_ints[0])+1);.
It is not a pointer to an int, much like (int*)nullptr is not a pointer to an int. Round tripping it back to a char* results in a pointer with unspecified value (it could be 0xbaadf00d or nullptr) explicitly in the standard.
The standard defines what you must do. There are (nearly? I guess evaluating it in a boolean context must return a bool) no requirements placed on the behavior of some_ptr by the standard, other than converting it back to char* results in an unspecified value (of the pointer).

Is it legal to compare dangling pointers?

Is it legal to compare dangling pointers?
int *p, *q;
{
int a;
p = &a;
}
{
int b;
q = &b;
}
std::cout << (p == q) << '\n';
Note how both p and q point to objects that have already vanished. Is this legal?
Introduction: The first issue is whether it is legal to use the value of p at all.
After a has been destroyed, p acquires what is known as an invalid pointer value. Quote from N4430 (for discussion of N4430's status see the "Note" below):
When the end of the duration of a region of storage is reached, the values of all pointers representing the address of any part of the deallocated storage become invalid pointer values.
The behaviour when an invalid pointer value is used is also covered in the same section of N4430 (and almost identical text appears in C++14 [basic.stc.dynamic.deallocation]/4):
Indirection through an invalid pointer value and passing an invalid pointer value to a deallocation function have undefined behavior. Any other use of an invalid pointer value has implementation-defined behavior.
[ Footnote: Some implementations might define that copying an invalid pointer value causes a system-generated runtime fault. — end footnote ]
So you will need to consult your implementation's documentation to find out what should happen here (since C++14).
The term use in the above quotes means necessitating lvalue-to-rvalue conversion, as in C++14 [conv.lval/2]:
When an lvalue-to-rvalue conversion is applied to an expression e, and [...] the object to which the glvalue refers contains an invalid pointer value, the behaviour is implementation-defined.
History: In C++11 this said undefined rather than implementation-defined; it was changed by DR1438. See the edit history of this post for the full quotes.
Application to p == q: Supposing we have accepted in C++14+N4430 that the result of evaluating p and q is implementation-defined, and that the implementation does not define that a hardware trap occurs; [expr.eq]/2 says:
Two pointers compare equal if they are both null, both point to the same function, or both represent the same address (3.9.2), otherwise they compare unequal.
Since it's implementation-defined what values are obtained when p and q are evaluated, we can't say for sure what will happen here. But it must be either implementation-defined or unspecified.
g++ appears to exhibit unspecified behaviour in this case; depending on the -O switch I was able to have it say either 1 or 0, corresponding to whether or not the same memory address was re-used for b after a had been destroyed.
Note about N4430: This is a proposed defect resolution to C++14, that hasn't been accepted yet. It cleans up a lot of wording surrounding object lifetime, invalid pointers, subobjects, unions, and array bounds access.
In the C++14 text, it is defined under [basic.stc.dynamic.deallocation]/4 and subsequent paragraphs that an invalid pointer value arises when delete is used. However it's not clearly stated whether or not the same principle applies to static or automatic storage.
There is a definition "valid pointer" in [basic.compound]/3 but it is too vague to use sensibly.The [basic.life]/5 (footnote) refers to the same text to define the behaviour of pointers to objects of static storage duration, which suggests that it was meant to apply to all types of storage.
In N4430 the text is moved from that section up one level so that it does clearly apply to all storage durations. There is a note attached:
Drafting note: this should apply to all storage durations that can end, not just to dynamic storage duration. On an implementation supporting threads or segmented stacks, thread and automatic storage may behave in the same way that dynamic storage does.
My opinion: I don't see any consistent way to interpret the standard (pre-N4430) other than to say that p acquires an invalid pointer value. The behaviour doesn't seem to be covered by any other section besides what we have already looked at. So I am happy to treat the N4430 wording as representing the intent of the standard in this case.
Historically, there have been some systems where using a pointer as an rvalue might cause the system to fetch some information identified by some bits in that pointer. For example, if a pointer could contain the address of an object's header along with an offset into the object, fetching a pointer could cause the system to also fetch some information from that header. If the object has ceased to exist, the attempt to fetch information from its header could fail with arbitrary consequences.
That having been said, in the vast majority of C implementations, all pointers that were alive at some particular moment in time will forever hold the same relationships with regard to the relational and subtraction operators as they had at that particular time. Indeed, in most implementations if one has char *p, one may determine whether it identifies part of an object identified by char *base; size_t size; by checking whether (size_t)(p-base) < size; such comparison will work even retrospectively if there is any overlap in the objects' lifetime.
Unfortunately, the Standard defines no means by which code can indicate that it requires any of the latter guarantees, nor is there a standard means by which code can ask whether a particular implementation can promise any of the latter behaviors and refuse compilation if it does not. Further, some hyper-modern implementations will regard any use of relational or subtraction operators on two pointers as a promise by the programmer that the pointers in question will always identify the same live object, and omit any code which would only be relevant if that assumption didn't hold. Consequently, even though many hardware platforms would be able to offer guarantees that would be useful to many algorithms, there's no safe way by which code can exploit any such guarantees even if code will never need to run on hardware which does not naturally provide them.
The pointers contain the addresses of the variables they reference. The addresses are valid even when the variables that used to be stored there are released / destroyed / unavailable.
As long as you don't try to use the values at those addresses you are safe, meaning *p and *q will be undefined.
Obviously the result is implementation defined, therefore this code example can be used to study the features of your compiler if one doesn't want to dig into to assembly code.
Whether this is a meaningful practice is totally different discussion.

Do C and C++ standards imply that a special value in the address space must exist solely to represent the value of null pointers?

Following discussion from this question about null pointers in C and C++, I'd like to have the ending question separated here.
If it can be inferred from C and C++ standards (answers can target both standards) that dereferencing a pointer variable whose value is equal to the nullptr (or (void *)0) value is undefined behavior, does it imply that these languages require that a special value in the address space is dead, meaning that it's unusable except for the role of representing nullptr? What if the system has a really useful function or data structure at the same address that's equal to nullptr? Should this never happen because it's a compiler's writer responsibility to figure out a non-conflicting null pointer value for each system the compiler compiles to? Or should the programmer that needs to access such function or data structure be content while programming in "undefined behavior mode" to achieve its intents?
This looks like blurring the lines of the roles of a compiler and a computer system. I would ask whether it's right to do so, but I guess there's no room for this here.
This blog post digs about tackling the problem situation
That depends on what is meant by the phrase "address space". The C standard uses the phrase informally, but doesn't define what it means.
For each pointer type, there must be a value (the null pointer) that compares unequal to a pointer to any object or function. That means, for example, that if a pointer type is 32 bits wide, then there can be at most 232-1 valid non-null values of that type. There could be fewer than that if some addresses have more than one representation, or if not all representations correspond to valid addresses.
So if you define the "address space" to cover 2N distinct addresses, where N is the width in bits of a pointer, then yes, one of those values must be reserved as the null pointer value.
On the other hand, if the "address space" is narrower than that (for example, typical 64-bit systems can't actually access 264 distinct memory locations), then the value reserved as the null pointer can easily be outside the "address space".
Some things to note:
The representation of a null pointer may or may not be all-bits-zero.
Not all pointer types are necessarily the same size.
Not all pointer types necessarily use the same representation for a null pointer.
On most modern implementations, all pointer types are the same size, and all represent a null pointer as all-bits-zero, but there are valid reasons to, for example, make function pointers wider than object pointers, or make void* wider than int*, or use a representation other than all-bits-zero for the null pointer.
This answer is based on the C standard. Most of it also applies to C++. (One difference is that C++ has pointer-to-member types, which are typically wider than ordinary pointers.)
does it imply that these languages require that a special value in the address space is dead, meaning that it's unusable except for the role of representing nullptr?
No.
The compiler needs a special value to represent a null pointer, and must take care that it does not place any object or function at that address, because all pointers to objects and functions are required to compare unequal to the null pointer. The standard library must take similar precautions in its implementation of malloc and friends.
However, if there is something at that address already, something that no strictly conforming program can access, then an implementation is allowed to support dereferencing the null pointer to access it. Dereferencing the null pointer is undefined in standard C, so an implementation can make it do anything it likes, including the obvious.
Both the C and the C++ standards understand the concept of the as-if rule, which basically means that if to valid input, an implementation is indistinguishable from one that conforms to the standard, then it does conform to the standard. The C standard uses a trivial example:
5.1.2.3 Program execution
10 EXAMPLE 2 In executing the fragment
char c1, c2;
/* ... */
c1 = c1 + c2;
the "integer promotions" require that the abstract machine promote the value of each variable to int size and then add the two ints and truncate the sum. Provided the addition of two chars can be done without
overflow, or with overflow wrapping silently to produce the correct result, the actual execution need only produce the same result, possibly omitting the promotions.
Now, if c1 and c2's values come from registers, and it's possible to force values outside of char's range into those registers (e.g. by inline assembly), then the fact that the implementation optimises away the integer promotions might be observable. However, since the only way to observe it is through undefined behaviour or implementation extensions, there is no way for any standard code to be affected by this, and an implementation is allowed to do it.
This is the same logic that applies to getting useful results when dereferencing null pointers: there are only two ways to see, from code, that there is something meaningful at that particular address: getting a null pointer from an evaluation that is guaranteed to produce a pointer to an object, or by just trying it. The former is what I mentioned the compiler and standard library must take care of. The latter is not something that can affect a valid standard program.
A well-known example is the interrupt vector table on DOS implementations, which resides at address zero. It is typically accessed simply by dereferencing a null pointer. The C and C++ standards don't, shouldn't and cannot cover access to the interrupt vector table. They do not define such behaviour, but they do not restrict access to it either. Implementations should be and are allowed to provide extensions to access it.
does it imply that these languages require that a special value in the address space is dead, meaning that it's unusable except for the role of representing nullptr?
Yes.
C has requirements for null pointer that make it different to object pointers:
(C11, 6.3.2.3p3) "[...] If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function."
What if the system has a really useful function or data structure at the same address that's equal to nullptr? Should this never happen because it's a compiler writer responsibility to figure out a non-conflicting null pointer value for each system the compiler compiles to?
The New C Standard by Derek M. Jones provides the following commentary on implementations:
All bits zero is a convenient execution-time representation of the null pointer constant for many implementations because it is invariably the lowest address in storage. (The INMOS Transputer[632] had a signed
address space, which placed zero in the middle.) Although there may be program bootstrap information at
this location, it is unlikely that any objects or functions will be placed here. Many operating systems leave
this storage location unused because experience has shown that program faults sometimes cause values to be
written into the location specified by the null pointer constant (the more developer-oriented environments try
to raise an exception when that location is accessed).
Another implementation technique, when the host environment does not include address zero as part of a
processes address space, is to create an object (sometimes called _ _null) as part of the standard library. All
references to the null pointer constant refer to this object, whose address will compare unequal to any other
object or function.
Yes, that's precisely what it means.
[C++11: 4.10/1]: [..] A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type. [..]
The null pointer value doesn't need to be 0x00000000, but it does need to be unique; there's no other way to make this rule work.
It's certainly not the only rule of the abstract machine that implicitly emplaces strict limitations upon practical implementations.
What if the OS puts a really useful function or data structure at the same address that's equal to nullptr?
The OS won't do that but it can be exploited.

Are there any uses with unitialized variables?

In C++ Primer Plus (6th. edition), page 73, it states:
If you don't initialize a variable that is defined inside a function,
the variable's value is indeterminate. That means the value of
whatever happened to be sitting at that memory location prior to the
creation of that variable.
Does that mean I can use an uninitialized variable to get data on the memory location at that point in the program? If true, are there any instances where this property is useful?
The standard's wording for what you quoted is as follows (§8.5 [dcl.init]):
If no initializer is specified for an object, the object is default-initialized; if no initialization is performed, an object with automatic or dynamic storage duration has indeterminate value.
The book is a little misleading about the previous value being available at that location. It is often true, but not necessarily.
The important point is that accessing the value of such a object results in undefined behaviour. Accessing the value of a object is formally known as lvalue-to-rvalue conversion, which says this (§4.1 [conv.lval]):
If the object to which the glvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior.
This occurs only when an operator in an expression requires an rvalue operand, which is usually the case. However, the unary & operator requires an lvalue operand, so lvalue-to-rvalue conversion is not applied. That means taking the address of an uninitialized variable is fine. This makes logical sense, because the object exists and has a valid address, it just isn't initialized. Taking the address doesn't require accessing the object's value.
Why would you do this? It's hard to think of a specific example because the idea is very broad and because we don't often leave our variables uninitialized (if at all). If you needed to store the address of an object before you assigned a value to it though, this is what you would need to do. You could later access the object through that pointer (once it has been assigned to). In fact, you could assign to it through the pointer.
While the standard effectively forbids ever using the value of an uninitialized variable, you are usually able to read it anyway, and it will usually indeed contain whatever value was last stored at that location.
However, this information is not useful in any way. Since your program always runs in its own private virtual address space, anything you might read has been written by your own program. I. e., it's information that you have already anyway, and which you can pass around in much more secure ways. This is especially true if you are using an uninitialized variable within a function: all you will ever be able to see that way is information that was written to a local variable by some function that you called earlier.
You might be able to find out some internals of the standard C library and other libraries that you use because their code runs within your address space. But most information will not be stored on the stack anyway, so you would need to read data from some other memory locations than an uninitialized variable. You would need to dereference pointers to stuff that you don't own, and that is really deep down in undefined behaviour. If you try this, you will most likely get segfaults.
So, yes, it's possible, but...