#include <iostream> using std::cout; using std::endl; int main(int argc, char ** argv) { int ia[10]; int ib[10]; int * pia = ia; int * pib = ib; cout << (pib - pia) << endl; /* тут выведет на экран 12, почему не 10? */ double da[10]; double db[10]; double * pda = da; double * pdb = db; cout << (pdb - pda) << endl; /* а тут 10, как и должно быть */ return 0; } 

I always thought that subtracting pointers of the same type would result in a count of elements between them, but in this case the int is wrong. Why?

Why, by the way, you can not subtract different types of pointers and display the number of bytes between them?

And the last question. As I know, the maximum addresses are at the beginning of the stack, and the end is in the code segment, the stack itself grows from top to bottom, i.e. in order of decreasing addresses, then why if you declare a lok on the stack. change. int a;int b; then their addresses will be: &a < &b , and not vice versa?

  • one
    The result of the pib-pia , pdb-pda operations is undefined, it may depend on the compiler, optimization options and the phase of the moon. - andy.37
  • Is that so? You can open the first page in Google about the subtraction of pointers and read that the Subtraction of two pointers determines how many variables of this type are placed between the specified cells. These operations are applicable only to pointers of the same type and make sense mainly with structural data types, for example, with arrays. - David
  • one
    What makes you think that if you declare two arrays in a row, it means that they are directly behind each other in memory? This is not defined by the standard, so the compiler developers can do what they like, it may even depend on the optimization flags. For example, try to make a release build. - Vladimir Gamalyan
  • @David is better to open the tutorial, and read about pointer arithmetic. - andy.37
  • Malloc a 20 byte piece of memory for a pointer to an int, and for a pointer to a char. In the first array we can place 5 elements (subject to sizeof (int) = 4), in the second - 20 elements (subject to sizeof (char) = 1). As you can see, with the same amount of allocated memory, we have a different number of elements. So how can we subtract here? - isnullxbh

4 answers 4

The compiler implements some standard C / C ++, which defines the rules and behavior. Anything that is not explicitly stated in this standard is implemented at the discretion of the compiler developer.

You are comparing two different arrays. Subtracting their pointers will give the number of elements between the two memory cells. But the standard never states that when declaring two different arrays in the code one after another, they will also be in memory one after another. Consequently, it is not a fact that these addresses follow each other in a strict order and something else does not get in there, therefore we get UB. You get the correct value, the distance in the types between the addresses, but incorrectly consider the addresses next to each other.

Subtracting different types of pointers does not make sense, it just complicates the architecture and is almost never used. If you really want, you can always count the number of elements and get the difference of their sizeof. Nothing is simply added to the language; every element must carry a conscious utility.

On the third question, again, no one can guarantee the order in which the items are placed on the stack, this is determined by the compiler developer. Therefore, sometimes traps lie where you get the same result and think that this behavior will always and everywhere be the same.

  • How does getting a pointer difference (with an undefined value) result in a UB? - Vladimir Gamalyan
  • @VladimirGamalian, you can never say exactly what result you get as a result of such subtraction of pointers of two different unrelated variables. The compiler can rearrange them in any way in the address space, since this is not regulated. This makes sense for operations inside arrays and other elements where order is guaranteed, but not in this case. - Alex Krass
  • Ok, i.e. such code: int a; int b = a; int a; int b = a; according to your ub? After all, here the value of a can be anything. And if UB, then the compiler has the right to send rockets to the moon? Those. I want to say that there is no UB in the subtraction operation itself, another thing is if you then use this result for memory access. - Vladimir Gamalyan
  • @VladimirGamalian, then decide, are we talking about the result of assigning a == b or the very meaning in memory? The value in memory will definitely be UB. Getting the difference of pointers in the same way as in the question leads to UB. Equating one value to another is a strictly defined behavior. And yes, in the case of operations with UB, a rocket launch to the moon can take place, if you develop software for this in C / C ++)) - Alex Krass
  • Reference to the standard please that the subtraction operation on the values ​​defined by the implementation is UB. Well, I'll change the example a bit: int a; int b = a - 1; int a; int b = a - 1; - added subtraction operation. Now ub according to you? - Vladimir Gamalyan

I agree with previous speakers :) at the expense of UB, 100% - depends on a number of factors. Including from alignment:

 #include <iostream> using std::cout; using std::endl; int main(int argc, char ** argv) { int ia[10]; int ib[10]; int * pia = ia; int * pib = ib; cout << __alignof__(ia) << endl; // 4 на ideone (C++14) double da[10]; double db[10]; double * pda = da; double * pdb = db; cout << __alignof__(da) << endl; // 8 на ideone (C++14) return 0; } 

@Vladimir Gamalian, catch it:

A piece of 5.7 ISO / IEC 14882, Third edition 2011-09-01

  1. It is a type of pointer that has been added. There is no need for any further information. If there is a problem, it can be used to express the points and the expressions (P) + N (equivalently, N + (P)) and (P) –N (where N has the value n) to, respectively, the elements of the array object, they exist. Moreover, if there is one, the (Q) -1 points to the array element. In the case of the object and the object otherwise, the behavior is undefined.
  2. In the case of the two elements of the array of elements. Implementation of the integral integral type; This is a type of std :: ptrdiff_t in the header (18.2). If the result is an overflow, it is undefined. If the expressions were given the expressions, object of type std :: ptrdiff_t. In addition, it can be used as a rule. - (P) has the same value as ((Q) - (P)) + 1 and as - ((P) - ((Q) +1)), and The last element of the array object, even though it does not. It is a. 82
  3. If the value is 0, the value has been added. It is a null point and it is a null point.
  • What is UB? Subtracting pointers will give a difference in their values ​​and this is quite a definite behavior. - Vladimir Gamalyan
  • But this does not mean the difference in the size of sequentially prescribed variables in the code. This means how much one address is far from the other, and no more. And the alignment method is one of the reasons. - Majestio
  • Of course it does not mean. But how does it follow from this that this is a udnefined behavior? - Vladimir Gamalyan
  • The optimizer has the right to do not as you think. Can generally throw out a variable if it is not used. And if this action on the sale of the implementation - it means UB. - Majestio
  • Then, I fear, you do not understand the meaning of the term UB. - Vladimir Gamalyan

Quote from K & R

Permitted pointer operations include:

  • assignment of pointers of the same type
  • addition and subtraction of the pointer and integer
  • subtracting or comparing pointers pointing to the same dataset
  • zero assignment or comparison with it

all other operations are errors and are not allowed.

Those. UB in your program, and formally, it has the right to do anything, even formatting a hard disk. In reality, of course, nothing terrible will happen unless you try to use the result of a forbidden operation for any memory access.

  • The answer I was waiting for. Can you add a reference to the standard? - Vladimir Gamalyan
  • In reality, of course, nothing terrible will happen unless you try to use the result of a forbidden operation for any memory access - or write heuristics for optimization based on subtracting pointers :) - Pavel Mayorov

You are now deep in the field of behavior defined by the implementation. And your attempts to measure the size of arrays by subtracting pointers are UB.

The fact that the stack grows "down" means only that each nested stack frame has an address less than the previous one. But inside the stack frame, the compiler can allocate memory for local variables as it pleases.

In your case, most likely, the compiler has aligned arrays on a multiple of 128 bits (16 bytes). But he could also shove, for example, some pointer between two arrays.

It is impossible to subtract pointers of different types because subtracting pointers gives the number of elements between them - and which of the two types of elements can be taken as the base one? If you need a difference in bytes, you can always cast pointers to a pointer to char:

 cout << (reinterpret_cast<char*>(pda) - reinterpret_cast<char*>(pia)) << endl; 
  • What is my attempt to measure the size of an array by deducting UB pointers? - David
  • @David don't remember exactly. Maybe someone will fix it. - Pavel Mayorov
  • @David, to be precise, as long as you don’t try to use the pdb-pda result to access memory, it’s safe (just an arbitrary integer). And use is equivalent to accessing an arbitrary address, such as *(int*)(rand()) = 10; The compiler has the full right to completely rearrange your arrays for reasons of optimization. - andy.37