C++ pointer question

I considered myself fairly adept at C++, but I’m reading someone else’s C code and it’s hurting my brain.

  T Z[part_count];
  for(int p=0; p<part_count; p++)
    {
      Z[p]=0;
      T *Z_cp = Z+p;
    }

What is going on in the code above? T is just a #define for double. He creates an array (pointer), then, as part of the loop, sets the current array member to 0. Then, for reasons I can’t figure out, he creates a pointer Z_cp which is Z+ (the loop index). Isn’t this functionally equivalent to Z[p]?

I chopped a lot of the loop out. The only other place where Z or Z_cp are referenced within the loop is

(*Z_cp)=blah blah value

. Why not just use Z[p] for that? I can’t figure out if this guy’s a dick or I’m just missing something obvious.

Later in the same function, there’s this statement (within 3 nested loops i, j, and k):

T *out_cp = result_p[i+k]+(j+high_bound1);

out_cp is a pointer to a double array- check.
result_p’s [] operator returns a pointer to a double (a row in a matrix)- check.
Forgive my failing memory, but what’s the above statement do? Set each value of the out_cp array to the equivalent value in the result_p array PLUS the constant represented by the sum (j+high_bound1)? Or is the value (j+high_bound1) added to the pointer result_p, and what does that do?

This is driving me nuts.

REDACTED: I was incorrect, pay no attention.

So generally speaking you add integers to pointers to create new pointers that are offset by that value. For exaimple if I have:

int xs[10];
int *x5 = xs+5;

I have a pointer to the 5th element of xs. Another way to do that would be:

int *x5 = &xs[5];

The actual offset is dependent on the type information at hand. For an array of integers, adding 5 means advancing the pointer by the size of 5 integers (on most systems that would be 20 bytes). If you had an array of chars then adding 5 to a pointer to that array would advance the pointer by the size of 5 chars (or 5 bytes). This is important if you, for example, used a void* or cast a pointer from one type to another. For your purposes it probably doesn’t matter. You can just mentally rearrange pointer arithmetic like I did above to be taking the address of an element of an array.

This is equivalent to &(Z[p]). He’s basically caching the value of the pointer. This can be useful if you want to avoid recalculating memory addresses or if you want to get a pointer value to, for example, use as an argument to another function. If he were to do, for example:

z[p] = z[p] + 1;

It would at least in theory have to calculate &z[p] twice (adding p to the memory location of z). In practice the compiler would probably figure out whatever optimization it is he’s trying to do here and his work is redundant/needlessly confusing.

If he only references Z_cp once then no reason for this. Maybe he just didn’t clean up his code after a refactor.

T *out_cp = result_p[i+k]+(j+high_bound1);

He’s just creating a pointer to result_p[i+k][j+high_bound1].

The first block reads like the author is trying to win a C++ obfuscation contest. Z[p] and *(Z+p) are indeed equivalent, there’s no good reason to use both in the same loop.

The last statement simply declares a pointer. It’s a variable declaration, it doesn’t set anything in the array.

Not in C (and I don’t think it changed in C++). Addition to a pointer is aligned to the size of the pointer type, if I’m not mistaken.

Wrong, the two are equivalent. C and C++ pointer arithmetic is the same as array arithmetic. You must cast to byte (well, char in these obnoxious languages) to get byte increments.

Jeez guys, that’s harsh, two posts criticizing me five minutes after I redacted my original post? I mean, just because I was completely and utterly incorrect?

Remember in C and C++, A[i] is exactly the same as *(A+i) is therefore exactly the same as i[A]. The array braces notation is actually a super-sekrit macro for the pointer arithmetic expression, which is why i[A] is actually allowed in code despite breaking every conceivable rule of typing.

(Actually, i[A] is so egregious I don’t guarantee it works in all compilers, but it certainly works in some compilers, anyway).

If it doesn’t work, it’s a pretty horrible bug in the compiler and almost certainly indicative that lots of other pointer and array math is going to go wrong. That’s why this oddball construct is a very standard part of most test suites.

It’s also a good indication that the compiler front-end author never actually read the standard, as this exact construct is explicitly spelled out in a footnote.

Huh, while the result follows from the commutativity of addition, I had no idea it was an actual standards footnote.

If i is 32 bits and A is 64 bits, wouldn’t A[i] mean *(A + (64 * i)), and i[A] mean *(i + (32 * A)), meaning that they’re not reversible at all?

No, i is a counter that multiples the sizeof A. This is because the compiler knows that i is an int but A is a pointer, so it treats them appropriately regardless of the order of operands.

If A is an array of structs or something like that with 128 bytes per object, then *(A+2) dereferences the memory location of the base of A plus 256, as does *(2+A), and also casts to the type of A’s array elements.

Shouldn’t, literally,


int q=2[A]

throw an error? 2 is a concrete type and is not dimensional, how can you index it?

I get the equivalence through the commutivity of addition, but there’s some really screwy syntax wankery going on if the above works.

Huh, I didn’t think C was typesafe enough to know whether something was a pointer or an int. I.e., I thought you could do like:

int *z = whatever();
int x = z;

But apparently not!

Yay for not having to remember esoteric C/C++ trivia for upwards of 15 years now, I suppose.

You may or may not get a cast warning depending on how size_t converts to int, but yeah. It’s fun!

If you cast a pointer to an int and try to dereference it, despite the fact the data and the memory hasn’t changed at all, you will get a compiler error, not a segmentation fault. C isn’t strongly typed, but it does have types, and that’s something, anyway :)

If you cast a random integer back to a pointer and dereference that, then you get the runtime core dump.

Yeah, I’m running on ancient memory, too. I think 15 years is about the time frame since I last used C or C++. Obviously C-like descendant languages that don’t even have pointers don’t have this weird commutativity thing going on with their array syntax.

The operator is defined such that a[b] is equivalent to *(a + b).

The 2[A] trick is an artifact of that definition.

This works for arrays because when an array object reference (lvalue) is converted to an rvalue (operands of addition are both rvalues), it’s implicitly converted to a pointer the first element of the array. So in our expression “a + b”, the type of the variable “b” can be “array of 10 integers”, but the type of the expression on the right hand side of the ‘+’ operator is “pointer to int”.

Also, Peter van der Linden’s Expert C Programming (Prentice Hall (SunSoft Press) 1994, ISBN 0-13-177429-8) has a great explanation of C/C++ pointer syntax and other idiosyncracies of the C language.

Apropos of nothing, this is C++ code, not C code. You can’t declare a new variable (Z_cp) in the middle of a code block in C; it has to come at the top. Which makes it bad C++ code, in addition to/in place of being bad C code.

Later in the same function, there’s this statement (within 3 nested loops i, j, and k):

T *out_cp = result_p[i+k]+(j+high_bound1);

out_cp is a pointer to a double array- check.
result_p’s operator returns a pointer to a double (a row in a matrix)- check.
Forgive my failing memory, but what’s the above statement do? Set each value of the out_cp array to the equivalent value in the result_p array PLUS the constant represented by the sum (j+high_bound1)? Or is the value (j+high_bound1) added to the pointer result_p, and what does that do?

Let me guess: result_p is of type pointer-to-pointer-to-X? That’s basically how you would store a matrix of user-defined size if you don’t have C99’s dynamically sized arrays.

In that case, what it’s doing is storing some index into the matrix that varies based on i and j, the row and column indices (or possibly column and row, depending on how result_p is defined). high_bound1 is probably the total no. of rows/columns.