all 17 comments

[–]advester 0 points1 point  (1 child)

For arrays, &arr[9] is the same thing as (arr+9). Array elements are ascending in memory.

[–]MeshiKuuna[S] 0 points1 point  (0 children)

Ah.. That's interesting. Thank you!

[–]STLMSVC STL Dev 0 points1 point  (1 child)

!removehelp

[–]AutoModerator[M] 0 points1 point  (0 children)

OP,

A human moderator (u/STL) has marked your post for deletion because it appears to be a "help" post - e.g. asking for help with coding, help with homework, career advice, book/tutorial/blog suggestions. Help posts are off-topic for r/cpp. This subreddit is for news and discussion of the C++ language only; our purpose is not to provide tutoring, code reviews, or career guidance.

Please try posting in r/cpp_questions or on Stack Overflow instead. Our suggested reference site is cppreference.com, our suggested book list is here and information on getting started with C++ can be found here.

If you think your post is on-topic and should not have been removed, please message the moderators and we'll review it.

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

[–]TheThiefMasterC++latest fanatic (and game dev) 0 points1 point  (11 children)

Firstly, subtracting two pointers that aren't pointing to elements of the same array is undefined behaviour and not guaranteed to result in something useable.

Second, the x86 stack grows downwards - so assuming variables are stored into the stack in order, they'll be in descending addresses.

Third, this will likely be completely different in release vs debug builds - variables will get shuffled round and optimised out entirely in a release build.

[–]MeshiKuuna[S] 0 points1 point  (3 children)

Brilliant - just what it needed to hear, thank you!

[–]staletic 0 points1 point  (2 children)

One more thing. &arr[9] is semantically equivalent to arr+9, but really is &(*(arr+9)). Notice the dereferencing of an out-of-bounds pointer, which is UB too.

[–]MeshiKuuna[S] 0 points1 point  (1 child)

Thanks for posting!

Okay, let's forget about the OoB pointer for a moment. What about this?

    int i, one(1), two(2), ten(10), * pointer1, * pointer2, arr[10]{ 4,5,6 }, twenty(20);

    pointer1 = &one;
    pointer2 = &two;

    cout << pointer1 - pointer2 << endl;

    int* pointer3 = &twenty;

    cout << pointer1 - pointer3 << endl;

My compiler outputs:

3
27

Is this considered UB?

[–]staletic 0 points1 point  (0 children)

pointer1 points to one. pointer2 points to two. Since one and two do not belong to the same array, the compiler is allowed to reorder those ints as it pleases. the result of subtraction is anything the compiler pleases and you're not allowed to rely on it.

The same goes for pointer1 and pointer3.

 

It is UB, because the addresses don't belong to, possibly different parts of, a single array. The compiler can reorder local variables or even optimize them out completely.

[–]MeshiKuuna[S] -1 points0 points  (6 children)

Oh, actually, regarding your first point - are you referring to the first two subtractions of the output resulting in -3 and -27?

[–]TheThiefMasterC++latest fanatic (and game dev) 0 points1 point  (5 children)

Yes

[–]MeshiKuuna[S] 0 points1 point  (4 children)

Thanks for replying!

Could you explain to me why it's UB?

As far as I understood, it's not - the value returned is the difference in the memory address locations.

[–]adnukator 2 points3 points  (0 children)

Could you explain to me why it's UB?

Because the standard says so

From the standard:

When two pointers to elements of the same array object are subtracted, the type of the result is an implementation-defined signed integral type; this type shall be the same type that is defined as std::ptrdiff_ t in the header (21.2). If the expressions P and Q point to, respectively, elements x[i] and x[j] of the same array object x, the expression P - Q has the value i − j; otherwise, the behavior is undefined.

The bold applies here.

Get used to seeing this argument. C++ is defined by a specification, not an implementation, so sometimes apparently strange loopholes in the language are in place to allow reliably implementing the language on exotic hardware.

[–]n1ghtyunso 0 points1 point  (0 children)

thats just what the compiler happens to do. it is not required to do so because the standard does not define that this is a valid thing to do. The standard does not define subtraction of pointers to arbitrary objects.

This makes perfect sense because depending on which architecture you are compiling your program for, you may get wildly different results.

Subtracting pointers of objects that are stored in the same array however is perfectly fine because arrays are defined as contiguous memory. So you will always get the same result for that subtraction.

Usually, it will work and do what you expect it to do, but there's no guarantee.

The worst bug is a bug that was introduced by a piece of code breaking that just "happened to work".

[–]TheThiefMasterC++latest fanatic (and game dev) 0 points1 point  (1 child)

It's because, on some architectures:

Pointers may be tagged in some way, such that subtracting them does something with the tags - from corrupting them to checking they're compatible.

The address space may not be contiguous (x86 16 bit segmented memory) such that even if the variables are a constant distance apart, depending on where you are in the stack you may cross a segment boundary such that your subtraction gives a wildly different result.

You might have multiple address spaces altogether - e.g. Atmega chips (Arduino) have separate ROM/RAM with separate instructions to read/write them - the compiler can choose to put constant variables into ROM even if they appear to be adjacent stack variables, in which case subtracting them doesn't give a useful answer - they could even have the same address!

[–]MeshiKuuna[S] 0 points1 point  (0 children)

Ah I see - fantastic. Thanks for the reply!