Pointer arithmetic fails

Go To Last Post
23 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ignoring the utter uselessness of this program, follow the logic with me:

I have SomeData in memory, and set the bytes to some meaningless values. I have also built a struct and two pointers, lp and np, that are pointers to this struct.

See that the linker has placed SomeData at location 0x2004 in memory.

I set lp to the address of SomeData, and you see it is 0x2004. The data that lp points to, we can see lp->typ is 1 lp->ord is 1, and lp->siz is 5. Very nice.

Now, lets try some pointer math, and point np to the byte after the "e". I'll add the 5 to lp, then 4, and np should then be 0x200d, and np->typ should be 255.

Why is np 0x2928? Am I adding 9 to something pointed to by lp, and not lp itself?

Oops, you can't see the struct in the print screen. Here is the whole useless and incorrect program:

#include 

typedef struct ListItem
{
	uint8_t typ ;		// Type of item
	uint16_t ord ;	// Ordinal of item
	uint8_t siz ;		// Size of item
	uint8_t c[256];	// Contents of item
}ListItem;

uint8_t SomeData[1024];
ListItem * lp ;
ListItem * np ;

int main(void)
{
	SomeData[0] = 1 ;
	SomeData[1] = 1 ;
	SomeData[2] = 0 ;
	SomeData[3] = 5 ;
	SomeData[4] = 'a';
	SomeData[5] = 'b';
	SomeData[6] = 'c';
	SomeData[7] = 'd';
	SomeData[8] = 'e';
	SomeData[9] = 255 ;
	
	lp = (ListItem*) &SomeData ;
	np = lp + lp->siz + 4 ;
	
	PORTC.DIR = 255 ;
	PORTC.OUT = np->typ ;
	
	 
	
    while(1)
    {
        //TODO:: Please write your application code 
    }
}

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What is sizeof(ListItem)? I make it 260. So if you have:

ListItem * p;
p = p + 1;

how many bytes will p be incremented by?

If you said 1 you don't understand pointers ;-)

(a pointer increments by sizeof(*p))

If you add 9 to a pointer that is pointing to objects that are 327 bytes each then the address will increment by 9 * 327 and so on.

In fact a classic C programmer job interview question is:

int * p;
p += 3;

How many bytes does p move on by? Those who say "3" don't get the job. The true answer (as demonstrated by avr-gcc) is "depends on sizeof(int)".

EDIT: correct typo

Last Edited: Mon. Nov 4, 2013 - 05:53 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Torby wrote:

	np = lp + lp->siz + 4 ;


You need:

	np = (ListItem*)( ((unsigned char *)lp) + lp->siz + 4 );
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh! So that's why the compiler was including MUL instructions.

So if you add to a pointer, it multiplies what you add by the size of the thing being pointed to, assuming, rashly, that you have an array of these things.

Then, of course, Studio assumed, rashly, that I have a clue what I' doing.

"It's your language, I'm just trying to use it." -- Victor Borge.

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut. 

Last Edited: Mon. Nov 4, 2013 - 04:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

BTW this is a non-standard GCC extension but as long as you use -std=gnu this should be available to you:

http://gcc.gnu.org/onlinedocs/gc...

It does reduce the portability of the code though.

Quote:
So if you add to a pointer, it multiplies what you add by the size of the thing being pointed to, assuming, rashly, that you have an array of these things.

Is it really that "rash"? Think of your typical pointer usage:

int n[] = { 12345, 23456, 22222, 32145 };
int * p;

p = &n[0];
p += 3;
dest = *p;

What would you hope "dest" to contain? I rather think you might like it to hold 32145. It could only do this if each increment of p was by 2 bytes (ie sizeof(int)).

The same would be true however complex the pointed objects were (maybe not just int's but compound structures say).

BTW this is general C not avr-gcc issue (apart from the GNU thing I mentioned) so before Bob throws all the toys out of the pram I'll move it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh. Where to for general C questions? Bob once threw me out of the pram I tend to have LOTS of general C questions.

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One of the failings of this site is that there isn't really a place for general C questions.

Some get asked in Off Topic, some in General Electronics but neither of those seem appropriate places either. In theory AVR Forum isn't even the right place as it's not about the use of AVRs as such but I think it can usually be taken as read that most C questions here are about using it on AVR so I guess the rules can be bent a little? ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
So if you add to a pointer, it multiplies what you add by the size of the thing being pointed to, assuming, rashly, that you have an array of these things.
There is no assumption here at all. Pointer arithmetic is based on the size of the object being pointed to. This is a fully logical and sensible thing. If you tell someone to move forward 5, you are not likely to get what you want. You have to tell them what unit to use. You told the compiler that the struct takes up 260 bytes. If you create an instance of that struct, it will take up 260 bytes whether or not you actually assign a value to all 260 bytes.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

simpler yet... pointer bytes vs. pointer to ints

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What tripped me up was the only examples I found of pointer arithmetic were picking characters out of strings, so the size of the element was 1. Code working happily now.

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Generally, pointer arithmetic is bad form, rarely done. Mostly it's p++ or --p or some such. Or *p[n].
p+42 is crummy code.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

p+42 is crummy code.

Umm why? How is it really any different to:

p[42]

or is the use of arrays also "crummy code"? Here's what I consider to be some perfectly valid ptr+offset code:

        uint32 pixelOffset = m_imWidth * y + x;

        //Create pointers to relevant pixels at start of each row
        pCentre = m_blurIm.data() + pixelOffset;
        pMag    = m_magIm.data()  + pixelOffset;
        pBin    = m_binIm.data()  + pixelOffset;

        pLeft   = pCentre - 1;
        pRight  = pCentre + 1;
        pUp     = pCentre - m_imWidth;
        pDown   = pCentre + m_imWidth;

I suppose there is the issue of whether +42 is within the bounds of what p is pointing to? But you can face the same with indices out of bounds too.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

p+42 and p[42] yield the same machine code. maybe. Based on byte/word alignment done by the compiler.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

stevech wrote:
p+42 and p[42] yield the same machine code. maybe. Based on byte/word alignment done by the compiler.

*(p+42) (don't forget the indirection) and p[42] yield the same behavior, if not the same machine code. p+42 is the same as &p[42].

Incidentally, p[42] is also the same as 42[p]. Array and pointer indexing in C works in either order.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Incidentally, p[42] is also the same as 42[p].

Yup, I just love that as the odd fact to try to put aspiring C programmers into confusion. No never would rely on that in real code. I've posted here before that array notation is just syntactic sugar and that it holds that

a[i] <--> *(a+i) <--> *(i+a) <--> i[a]

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

i[a], that's a good one!

    int super = 2;
    printf("%c\n", super["Freak!"]);

Welcome back, Johan! :-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Incidentally, p[42] is also the same as 42[p].

I would never have thought of putting 42[p].

Let's see, since p is really just an address, assuming its type is a byte size, it would take 42 and add p*1 to get to the same place? Ok, let's just say I'd NEVER use this

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Let's see, since p is really just an address, assuming its type is a byte size, it would take 42 and add p*1 to get to the same place?

No. You misssed the point(s):

1) Array notation is just syntactic sugar for pointer arithmetics. You can define array notation exactly by saying that

a[i]

will be treated by the compiler as

*(a+i)

2) It is a fundamental math axiom (and the C compiler adheres to it) that (x + y) <==> (y + x)
Thus it is equally true to state the last expression under 1) as

*(i+a)

3) Applying 1) in reverse we can write

i[a]

So, its not

Quote:
it would take 42 and add p*1 to get to the same place

The compiler still knows that i is some integer type and a is a pointer. It will still take a and add sizeof(*a)*i, regardless of if you do

a[i]

or

i[a]

"to get to" the right place.

----------

One should aways be careful with definitions that one comes up with oneself, but it seems to me that it is formally correct to describe pointer arithmetics like so:

For

someType * p;
int i;

The expression

p + i

is equivalent to taking the value of the pointer p, and add to it the value of sizeof(*p)*i.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

Last Edited: Thu. Nov 7, 2013 - 03:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I've posted here before that array notation is just syntactic sugar and that it holds that

Code:
a[i] <--> *(a+i) <--> *(i+a) <--> i[a]

Aah--the old "An array is a pointer and a pointer is an array" maxim.

From long ago, I thought there was an asterisk to that, with certain exceptions. Digging...
Perhaps a good summary here: http://c-faq.com/aryptr/aryptreq...

Quote:
Q: So what is meant by the ``equivalence of pointers and arrays'' in C?

A: Much of the confusion surrounding arrays and pointers in C can be traced to a misunderstanding of this statement. Saying that arrays and pointers are ``equivalent'' means neither that they are identical nor even interchangeable. What it means is that array and pointer arithmetic is defined such that a pointer can be conveniently used to access an array or to simulate an array. In other words, as Wayne Throop has put it, it's ``pointer arithmetic and array indexing [that] are equivalent in C, pointers and arrays are different.'') ...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Aah--the old "An array is a pointer and a pointer is an array" maxim.

No, the old array access with "array/index/bracket notation is syntactic sygar for pointer arithmetic" maxim. My
Quote:
a[i] <--> *(a+i) <--> *(i+a) <--> i[a]

is perfectly in line with
Quote:
pointer arithmetic and array indexing [that] are equivalent in C, pointers and arrays are different

EDIT: No, I won't fix it - I just like to spell sygar that way.. :wink:

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sygar must be Swedish for something. I've been told that "Häagen-Dazs" is Swedish for "Heart Disease."

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Häagen-Dazs

Though as you may know it was simply a name made up by marketing from a New York based ice cream company to sound "Danish" as a tribute to Denmark's treatment of the jews during the war.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

(I know that. It was a joke.)

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut.