-->

C++ pointer arithmetic weirdness

2019-02-28 10:53发布

问题:

I found my bug (after a few hours) and isolated it in the following program. The problem is with the way in which the pst2 variable's value is calculated when using pointers to a struct. When using pointers to char, all works fine. Why is this?
(Using gcc/g++ version: (Debian 4.4.5-8) 4.4.5)
(For those who are wondering: I'm accessing a file-buffer containing data-groupings at regular offsets.)

#include <iostream>
#include "testpa.h"

#pragma pack(push)
#pragma pack(1)
//---------------------------
struct st_one
{
    int i;
    char c;
};
//---------------------------
struct st_two
{
    long l;
    int i;
};
#pragma pack(pop)

//===========================
int main()
{
    int n=1024, np1=sizeof(st_one); //, np2=sizeof(st_two);
    st_one *pst1, *pst1a;
    st_two *pst2, *pst2a;
    char *pc1, *pc2, *pc1a, *pc2a, *pb;

    pb = new char[n];

    pst1 = (st_one*)(pb);
    pst2 = (st_two*)(pst1 + np1); //using pst1
    pc1 = (char*)(pb);
    pc2 = (char*)(pc1 + np1); //using pc1

    pst1a = (st_one*)(pb);
    pst2a = (st_two*)(pb + np1); //using pb
    pc1a = (char*)(pb);
    pc2a = (char*)(pb + np1); //using pb

    std::cout << "\npb = " << (long)pb;
    std::cout << "\n-----";
    std::cout << "\npst1 = " << (long)pst1 << "\tpst2 = " << (long)pst2;
    std::cout << "\npc1  = " << (long)pc1 << "\tpc2  = " << (long)pc2;
    std::cout << "\n-----";
    std::cout << "\npst1a = " << (long)pst1a << "\tpst2a = " << (long)pst2a;
    std::cout << "\npc1a  = " << (long)pc1a << "\tpc2a  = " << (long)pc2a;
    std::cout << "\n-----\n";

    return 0;
}

Output:

pb = 19546128

pst1 = 19546128         pst2 = 19546153  <--- WRONG!
pc1  = 19546128         pc2  = 19546133

pst1a = 19546128        pst2a = 19546133
pc1a  = 19546128        pc2a  = 19546133

回答1:

That looks fine to me. The line:

 (pst1 + np1)

adds np1 instances of st_one to what pst1 points at, which means that pst1s value is incremented by np1 * sizeof (st_one) bytes, which is 25 (sizeof = 5), which corresponds to the values you've outputted. Instead of the above, I think you wanted:

 (pst1 + 1)

The pc1 value works because that is a char pointer, so the line:

(pc1 + np1)

adds np1 * sizeof (char) bytes to pc1, which is 5 bytes.

Incrementing a pointer makes the pointer point to the next element in memory, not the next byte.



回答2:

You shouldn't add sizeof(x), because that is done automatically. When you increment a pointer, like ++p, the address is incremented by the size of the object, so that it points to the next one.

Adding 1 to a pointer is the same as ++p. Adding sizeof(x) scales the increment twice.

Your calculations work fine for char, because sizeof(char) is 1.



回答3:

C++ automatically multiplies the integer you're adding by the size of the element the pointer points to. It assumes you want to advance the pointer by whole elements, not bytes.



回答4:

Pointer arithmetic in C and C++ is done times the sizeof the pointed to type of the pointer. That is, int *abc = /* ... */; int *def = abc + 1 results in def having a result an int ahead of abc, not a char.

As for your casting of the pointers into longs, that's implementation defined behavior, so you might get strange results on different machines from doing that.

(For that matter, so is your casting between pointer types. C++ says that's implementation defined too)



回答5:

It looks like you got everything wrong. For example, for get pst2 from pst1, you have to increment it by one, as pointer pst1 is of type st_one *, so you have to write like this:

pst2 = (st_two*)(pst1 + 1);

.. but you have:

pst2 = (st_two*)(pst1 + np1);

... where np1 is a sizeof of st_one, so it will skip as many st_one structures as that structure has bytes...

Read some docs on pointer arithmetic, like this one.