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.
Related
First I need to note that I have asked a similar question several times, and last time I have got an almost satisfactory answer.
However, the wording of the c++20 standard (draft) is not clear to me, and I am curious whether it is a defect within the standard or in the comprehension of the standard.
C++20 standard introduced the notion of implicit-lifetime types.
Scalar types, implicit-lifetime class types ([class.prop]), array types, and cv-qualified versions of these types are collectively called implicit-lifetime types.
Further the standard (draft) says https://eel.is/c++draft/basic.memobj#intro.object-10
Some operations are described as implicitly creating objects within a
specified region of storage. For each operation that is specified as
implicitly creating objects, that operation implicitly creates and
starts the lifetime of zero or more objects of implicit-lifetime types
([basic.types]) in its specified region of storage if doing so would
result in the program having defined behavior. If no such set of
objects would give the program defined behavior, the behavior of the
program is undefined. If multiple such sets of objects would give the
program defined behavior, it is unspecified which such set of objects
is created.
[Note 3: Such operations do not start the lifetimes of subobjects of
such objects that are not themselves of implicit-lifetime types.— end
note]
and https://eel.is/c++draft/basic.memobj#intro.object-11.
Further, after implicitly creating objects within a specified region
of storage, some operations are described as producing a pointer to a
suitable created object. These operations select one of the
implicitly-created objects whose address is the address of the start
of the region of storage, and produce a pointer value that points to
that object, if that value would result in the program having defined
behavior.
If no such pointer value would give the program defined behavior, the
behavior of the program is undefined.
If multiple such pointer values would give the program defined
behavior, it is unspecified which such pointer value is produced.
The paper P0593R6
Implicit creation of objects for low-level object manipulation provides motivation and more examples for these.
One of the motivations for introduction of implicit-lifetime objects into standard was dynamic construction of arrays in order to make the pointer arithmetic in the example non-UB.
One of the proposed changes to the standard 5.9. 16.5.3.5 Table 34: Cpp17Allocator requirements, (which is the same as Example 1 in allocator requirements ) gives the following example.
Example 1: When reusing storage denoted by some pointer value p,
launder(reinterpret_cast<T*>(new (p)byte[n * sizeof(T)])) can be used to implicitly create a suitable
array object and obtain a pointer to it.
Which basically implies that the idea is to make the syntax (T*)malloc(...) and (T*)(::operator new(... ) ) (and similar cases) valid for subsequent pointer arithmetic.
I also understand the idea of "superposition" - that the suitable created object to which we return the pointer is determined on the usage, as long as an object that allows to use that pointer "legally" exists, we are fine.
Hence, I believe that the following snippets are fine
int* numbers = (int*)::operator new(n * sizeof(int) );
// Fine. Operator new implicitly creates an int array of a size smaller than `n` that is determined later
// and returns a pointer to the first element Both byte and byte array are implicitly constructed, so no
// problem here.
// the pointer to suitable created object points at numbers[0] which is implicitly constructed, so no
// problem here
A slightly modified example from the cited paper..
alignof(int) alignof(float) char buffer[max(sizeof(int), sizeof(float))];
*(int*)buffer = 5;
// First usage implicitly created an int and we have a pointer to that int, (int*)buffer is the pointer
// to suitable created object.
new (buffer) std::byte[sizeof(buffer)];
// ends the lifetime of the int and implicitly creates a float instead of
// it since next usage (float*) buffer implies implicit float creation
*(float*)buffer = 2.0f; // (float*)buffer is the pointer to the suitable created object (float) that was
// implicitly created by previous statement
However, note that we did not have a problem yet - all the types are implicitly constructed, so returning a pointer to first element of the array is fine - it is has implicitly constructed itself.
But what do we do to in order to create an array of type T if T is not an implicit-lifetime type?
Well, that should not be a problem.
auto memory = ::operator new(sizeof(T) * n, std::alignval_t{alignof(T)});
// implicitly creates a T array of size n.
auto arr_ptr = reinterpret_cast<T(*)[n]>(memory); // returns a pointer to that array
auto arr = *arr_ptr; // dereferences the pointer to our implicitly constructed array, and the result
// should be valid
However, there is a problem with this construct.
First of all - one of the motivations in the proposal paper was to make the usage of casting to T* valid, not to T(*)[] or T(*)[n].
The examples in the standard and the proposal paper suggest that cast to T* is a valid usage.
Let's look at the following snippet.
T* arr = (T*)::operator new(sizeof(T) * n, std::align_val_t{alignof(T)});
Now we have a problem. arr points at an object that is not constructed. No matter how we look at it, no matter what the size of the implicitly constructed T array is, its first element is not an implicit-lifetime object and therefore a pointer value that points at arr[0] is not a pointer to one of the implicitly-created objects whose address is the address of the start of the region of storage, hence the requirement for a pointer to suitable created object is not satisfied. I believe that same reasoning makes the example in allocator.requirements invalid. More than that - if n in example is replaced with 1, we basically produce a pointer to T object of non implicit-lifetime type (it is not even array, but just a pointer to T that has not yet been constructed) which is just plain wrong.
So where is the problem? Is the example indeed invalid? But then, the example in proposal paper is still invalid (UB) and making it valid (not UB), was one the motivations for introduction of implicit-lifetime into the standard.
So, how is this conflict solved? Is there an "error" in the wording in the standard and a pointer to suitable created object does not actually have to point to an object, but any valid pointer value that results in defined behavior is fine (and a pointer to an array element that has not yet begun its lifetime is a valid value for pointer arithmetic as far as I am aware, and pointer arithmetic is want we want here, after all) or pointer to first element before beginning of its lifetime a suitable created object (despite the fact that does not have implicit-lifetime) for reasons I do not understand? Or the examples in the motivation and allocator.requirements snippet are wrong, and despite being one of the main motivations for introduction of implicit-lifetime into the standard, they are still UB? Something else?
Diclaimer: Language-lawyer type of question.
An explanation in the answer to the cited question is practically fine. Besides that we have the standard library allocator facilities that allow creation of uninitialized arrays which should cover most of cases. After all, casting to (T*) (along with some laundering may be) work in practice, and the intention of the proposal paper to make it work. I believe that is the intention of the standard authors as well. This a question is how does the standard resolve this contradiction.
PS:
I do not mind if the answer to this question is merged with the cited one (as long as my question here gets answered), but I do believe that these questions are different - that one asked how to create an array without initializing its elements and this one asks about something that is (I believe so at least) an ambiguity (or contradiction) in the c++20 standard (well draft of it) and how to resolve it.
What is the rationale behind returning unique addresses for allocations of zero size in C++?
Background: the C11 standard says about malloc (7.20.3 Memory management functions):
If the size of the space requested is zero, the behavior is implementation defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.
That is, as I see it, malloc always succeeds for allocations of zero size since the only you can do with the pointer of a zero-sized allocation is call some other memory allocation function like free with it:
if malloc returns NULL, free(NULL) is ok so this can be considered a success,
if it returns some other value, that's also a success (because it isn't NULL), the only condition is that free on the value should also work.
Also, C11 (also 7.20.3) does not specify that the address returned from malloc must be unique, only that they must point to disjoint memory regions:
The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object and then used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated). The lifetime of an allocated object extends
from the allocation until the deallocation. Each such allocation shall yield a pointer to an object disjoint from any other object.
All objects of zero size are disjoint AFAICT, and that would mean that malloc can return the same pointer for multiple zero-sized allocations (e.g. NULL would be fine), or different pointers each time, or the same pointer for some, etc.
Then C++98 came along with two raw memory allocation functions:
void* operator new(std::size_t size);
void* operator new(std::size_t size, std::align_val_t alignment);
Note that these functions only return raw memory: they do not create or initialize any objects of any type AFAICT.
You call them like this:
#include <iostream>
#include <new>
int main() {
void* ptr = operator new(std::size_t{0});
std::cout << ptr << std::endl;
operator delete(ptr, std::size_t{0});
return 0;
}
The [new.delete.single] section of the C++17 standard explains them, but the key guarantee as I see it is given in [basic.stc.dynamic.allocation]:
Even if the size of the space requested is zero, the request can fail. If the request succeeds, the value returned shall be a non-null pointer value (7.11) p0 different from any previously returned value p1, unless that value p1 was subsequently passed to an operator delete. Furthermore, for the library allocation functions in 21.6.2.1 and 21.6.2.2, p0 shall represent the address of a block of storage disjoint from the storage for any other object accessible to the caller. The effect of indirecting through a pointer returned as a request for zero size is undefined.38
That is, they must always return distinct pointers on success. That's a bit change from malloc.
My question is: What is the rationale behind this change? (that is, behind returning unique addresses for allocations of zero size in C++)
Ideally the answer would be just a link to the paper (or some other source) that explored the alternatives and motivated their semantics. Typically I go for The Design and Evolution of C++ for these C++98 questions, but Section 10 (Memory Management) does not mention anything about it. Otherwise, some sort of authoritative reference would be nice.
Disclaimer: I asked it on reddit but I did not ask nicely enough so I did not got any useful answer. I would like to kindly ask you that if you only have an hypothesis, please feel free to post it as an answer but mention that it is only an hypothesis.
Also, on reddit people went on and on about zero-sized types, whether I have a proposal to change the standard, etc. This question is about the semantics of the raw memory allocation functions when passed a size equal to zero. If topics like zero-sized types are relevant for your answer, please include them! But try not to get too derailed with tangential issues.
Also, on reddit people also threw arguments like "that's for optimization purposes" without really being able to mention anything more concrete. I'd expect something more concrete than "because optimizations" in an answer. For example, one redditor mentioned aliasing optimizations, but I wondered which kind of aliasing optimizations apply to pointers that cannot be dereferenced, and wasn't able to get anyone to comment on that. So maybe if you are going to mention optimizations, a small example that shows it would enrich the discussion.
The problem is that objects (no matter their size) in C++ must have a unique identity. So different coexisting objects (no matter their size) must have different address, since two pointer that compare as equal are assumed to point to a same object.
If you admit that zero-sized objects can have same address you cannot anymore distinguish if two address are or not a same object.
Many comments about the "new does not return objects" issue.
Please FORGET OOP terminology in this context:
C++ specification have a precise definition of what the word "Object" means.
CPP Reference:Object
In particular:
C++ programs create, destroy, refer to, access, and manipulate objects.
An object, in C++, is a region of storage that has
size (can be determined with sizeof);
alignment requirement (can be determined with alignof);
storage duration (automatic, static, dynamic, thread-local);
lifetime (bounded by storage duration or temporary);
type;
value (which may be indeterminate, e.g. for default-initialized non-class types);
optionally, a name.
The following entities are not objects: value, reference, function,
enumerator, type, non-static class member, bit-field, template, class or
function template specialization, namespace, parameter pack, and this.
A variable is an object or a reference that is not a non-static data member,
that is introduced by a declaration.
Objects are created by definitions, new-expressions, throw-expressions, when
changing the active member of a union, and where temporary objects are
required.
The reason is simply that code should not require special handling of boundary conditions. Many, I would say most, algorithms have to deal with zero-sized objects as boundary conditions. Less common is the algorithm that compare pointers to objects to see if they are the same object, but this still should work even for zero-sized objects.
However, your question assumes that this is a change. Apart from a brief hiatus in the late 1980's all C and C++ implementations that I am aware of have always behaved like this.
The original C compiler by dmr behaved like this but then around 1987 the draft C standard specified that malloc of a zero sized object return NULL. This was truly bizarre and even the final C-89 standard made it implementation-defined but I have never since encountered an implementation that did this horrible thing.
I talk more about this in my blog in the section "Malloc Madness".
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.
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).
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.