Does pointer arithmetic apply here or should I calculate an offset?

M

Mark Hobley

I have two structures:

struct buffertablestru {
struct bufstru *addr;
int numbufs;
};

struct bufstru {
char *addr;
int sz;
int ptr;
int tail;
};

The variable buffertable is of type buffertablestru and holds the address of a
set of buffer tables of type bufstru allocated in dynamic memory:

/* allocate the buffertable */
buffertable.addr = malloc(BUFTABINIT * sizeof(struct bufstru));
buffertable.numbufs = BUFTABINIT

(In this case BUFTABINIT is the number of tables to be allocated).

I have a function that I want to use to wipe an entry from the dyamic table
on request:

int wipebuffer(int bufnum) {
struct bufstru *bufstruptr = buffertable.addr +
(bufnum * sizeof(struct bufstru)); <---
bufstruptr -> addr = NULL; |
bufstruptr -> sz = 0; |
bufstruptr -> ptr = 0; |
bufstruptr -> tail = 0; |
} |
|
My question is, should I be calculating an offset here, or does pointer
arithmetic apply, requiring me to only provide the buffer number as below?

struct bufstru *bufstruptr = buffertable.addr + bufnum;

I have the start of the buffer tables at buffertable.addr and a buffer number.
I guess because I have a physical address, I am adding the offset, not the
buffer number. Does that sound right?

Now, suppose that i did this instead:

struct bufstru *bufstruptr = buffertable.addr;

I now want to set *bufstruptr to the address of the third buffer within the
dynamic table:

bufnum = 2; /* I want the third entry in the dynamic table */
bufstruptr += bufnum;

Am I right in thinking that pointer arithmetic applies here, so I don't need
to calculate the offset, because bufstruptr will always go up and down by the
size of bufstru?

Mark.
 
I

Ike Naar

I have two structures:

struct buffertablestru {
struct bufstru *addr;
int numbufs;
};

struct bufstru {
char *addr;
int sz;
int ptr;
int tail;
};

The variable buffertable is of type buffertablestru and holds the address of a
set of buffer tables of type bufstru allocated in dynamic memory:

/* allocate the buffertable */
buffertable.addr = malloc(BUFTABINIT * sizeof(struct bufstru));
buffertable.numbufs = BUFTABINIT

(In this case BUFTABINIT is the number of tables to be allocated).

I have a function that I want to use to wipe an entry from the dyamic table
on request:

int wipebuffer(int bufnum) {
struct bufstru *bufstruptr = buffertable.addr +
(bufnum * sizeof(struct bufstru)); <---
bufstruptr -> addr = NULL; |
bufstruptr -> sz = 0; |
bufstruptr -> ptr = 0; |
bufstruptr -> tail = 0; |
} |
|
My question is, should I be calculating an offset here, or does pointer
arithmetic apply, requiring me to only provide the buffer number as below?

struct bufstru *bufstruptr = buffertable.addr + bufnum;

I have the start of the buffer tables at buffertable.addr and a buffer number.
I guess because I have a physical address, I am adding the offset, not the
buffer number. Does that sound right?

No. When you add an integer i to a pointer p (regardless whether you
call i "offset" or "buffer number"), the result points i * sizeof *p
bytes beyond p; if you want the address of buffertable.addr[bufnum],
use buffertable.addr + bufnum or &buffertable.addr[bufnum] .

Another way to write your function is

int wipebuffer(int bufnum) {
buffertable.addr[bufnum].addr = NULL;
buffertable.addr[bufnum].sz = 0;
buffertable.addr[bufnum].ptr = 0;
buffertable.addr[bufnum].tail = 0;
}
Now, suppose that i did this instead:

struct bufstru *bufstruptr = buffertable.addr;

I now want to set *bufstruptr to the address of the third buffer within the
dynamic table:

bufnum = 2; /* I want the third entry in the dynamic table */
bufstruptr += bufnum;

Am I right in thinking that pointer arithmetic applies here, so I don't need
to calculate the offset, because bufstruptr will always go up and down by the
size of bufstru?

Correct.
 
D

Dr Malcolm McLean

On 3 Apr, 16:10, (e-mail address removed) (Mark Hobley)
wrote:
[ buffertable.addr is address of an array of bufstrs and a bufstr *]
int wipebuffer(int bufnum) {
  struct bufstru *bufstruptr = buffertable.addr +
    (bufnum * sizeof(struct bufstru));  <---
This is your error. Pointer arithmetic is scaled by the size of the
object pointed to. So the * sizeof() is superfluous.

However
buffertable.addr[bufnum]

is a shorter method of getting the same element, and is as a general
rule preferable.
 
S

Seebs

struct buffertablestru {
struct bufstru *addr;
int numbufs;
};

struct bufstru {
char *addr;
int sz;
int ptr;
int tail;
};
int wipebuffer(int bufnum) {
struct bufstru *bufstruptr = buffertable.addr +
(bufnum * sizeof(struct bufstru)); <---
bufstruptr -> addr = NULL; |
bufstruptr -> sz = 0; |
bufstruptr -> ptr = 0; |
bufstruptr -> tail = 0; |
} |
|
My question is, should I be calculating an offset here, or does pointer
arithmetic apply, requiring me to only provide the buffer number as below?

Of course pointer arithmetic applies.

You are adding an integer value to a pointer. Therefore, you are doing
pointer arithmetic.
I have the start of the buffer tables at buffertable.addr and a buffer number.
I guess because I have a physical address, I am adding the offset, not the
buffer number. Does that sound right?

No.

I have no idea what you mean by "a physical address". C has no such
distinction, it just has addresses. When you add an integer to a pointer,
no matter WHAT the pointer is, no matter how you got it, it increments
by sizeof(*pointer).
Am I right in thinking that pointer arithmetic applies here, so I don't need
to calculate the offset, because bufstruptr will always go up and down by the
size of bufstru?

Yes. But it's not because bufstruptr will "go up and down", but because
"adding an integer to a poniter always goes by the size of the thing pointed
to".

There is no difference between:
bufstruptr += 2;
and
otherptr = bufstruptr + 2;
in terms of how the arithmetic works. It's the *addition* that is pointer
arithmetic, not the fact that you're modifying a particular pointer.

Also, I strongly disreccomend the naming convention you're using. The
suffixes are not particularly informative, and "bufstruptr" tells us
virtually nothing. Give things names that reflect their function, not
their implementation.

-s
 
B

Barry Schwarz

On Sat, 3 Apr 2010 16:10:42 +0100,
I have two structures:

struct buffertablestru {
struct bufstru *addr;
int numbufs;
};

struct bufstru {
char *addr;
int sz;
int ptr;
int tail;
};

The variable buffertable is of type buffertablestru and holds the address of a
set of buffer tables of type bufstru allocated in dynamic memory:

/* allocate the buffertable */
buffertable.addr = malloc(BUFTABINIT * sizeof(struct bufstru));
buffertable.numbufs = BUFTABINIT

(In this case BUFTABINIT is the number of tables to be allocated).

I have a function that I want to use to wipe an entry from the dyamic table
on request:

int wipebuffer(int bufnum) {
struct bufstru *bufstruptr = buffertable.addr +
(bufnum * sizeof(struct bufstru)); <---
bufstruptr -> addr = NULL; |
bufstruptr -> sz = 0; |
bufstruptr -> ptr = 0; |
bufstruptr -> tail = 0; |
} |
|
My question is, should I be calculating an offset here, or does pointer
arithmetic apply, requiring me to only provide the buffer number as below?

struct bufstru *bufstruptr = buffertable.addr + bufnum;

I have the start of the buffer tables at buffertable.addr and a buffer number.
I guess because I have a physical address, I am adding the offset, not the
buffer number. Does that sound right?

Now, suppose that i did this instead:

struct bufstru *bufstruptr = buffertable.addr;

I now want to set *bufstruptr to the address of the third buffer within the
dynamic table:

bufnum = 2; /* I want the third entry in the dynamic table */
bufstruptr += bufnum;

Am I right in thinking that pointer arithmetic applies here, so I don't need
to calculate the offset, because bufstruptr will always go up and down by the
size of bufstru?

Don't let the fact that you allocated the memory confuse the issue.

Start with a normal array like
int x[5];
Under most conditions, the array expression x evaluates to the address
of the first element and is exactly equivalent to &x[0]. Obviously,
x[0] is the object at that address. x+1 will evaluate to the address
of the second element, &x[1]. And so on. This is independent of the
size of the elements of the array

You allocated space for a number of objects. You can treat that space
as if it were an array of such objects. The pointer expression
buffertable.addr evaluates to the address of the first element of this
"array" and buffertable.addr[0] is the object at that address.
buffertable.addr+1 will evaluate to the address of the second object.
And so on. This too is independent of the size of the "elements" in
the dynamically allocated "array".

To access the member sz in the i-th element (which is easier to
pronounce than the bufnum-th element), you can use the address of the
element and the -> dereference operator, as in
(buffertable.addr+i)->sz
or you can use the element itself and the . member specification
operator, as in
buffertable.addr.sz

And imbedded in the first example above is the answer to your question
about how to compute the address of the i-th element. Pointer
arithmetic is always "scaled" by the size of the object type being
pointed to.
 
M

Mark Hobley

Ike Naar said:
I have the start of the buffer tables at buffertable.addr and a buffer number.
I guess because I have a physical address, I am adding the offset, not the
buffer number. Does that sound right?

No. When you add an integer i to a pointer p (regardless whether you
call i "offset" or "buffer number"), the result points i * sizeof *p
bytes beyond p; if you want the address of buffertable.addr[bufnum],
use buffertable.addr + bufnum or &buffertable.addr[bufnum] .

Ok. Suppose now that I had some prefix data before the actual tables begins
and the address held in buffertable.addr is actually the address of the prefix,
so the actual table begins 3 bytes layer say.

So I want now (buffertable.addr + 3) + bufnum
^
|
Is there a way to make this operator not use pointer arithmetic?

I guess that buffertable.addr would be cast as void in this scenario, or
as a pointer to a super structure which consists of the prefix, plus the
bufferstru.

Presumably the modified syntax would be as follows, but with a cast for
bufstru somewhere.


int wipebuffer(int bufnum) {
buffertable.addr+3[bufnum].addr = NULL;
/* etc etc */
}

Where would I put the cast in this scenario?
Would it be as follows?

struct bufstru *(buffertable.addr+3)[bufnum].addr = NULL;

Or would that cause pointer arithmetic for the +3?

I could use a separate variable tmpptr, cast buffertable.addr as void,
set tmpptr as buffertable.addr, increment tmpptr by 3, and then cast a
separate pointer (donkeyptr) as struct bufstru *donkeyptr to do the actual
work. Do I need those extra variables?

This is purely academic by the way, I am not using a prefix in this case. I
did initially cast buffertable.addr as void though.

Mark.
 
S

Seebs

Ok. Suppose now that I had some prefix data before the actual tables begins
and the address held in buffertable.addr is actually the address of the prefix,
so the actual table begins 3 bytes layer say.
So I want now (buffertable.addr + 3) + bufnum
^
|
Is there a way to make this operator not use pointer arithmetic?

No.

And the solution is, DON'T DO THAT.

Because it may be misaligned for the data.

In short: Don't use a pointer to your table to hold a pointer that
isn't actually to your table.
I guess that buffertable.addr would be cast as void in this scenario, or
as a pointer to a super structure which consists of the prefix, plus the
bufferstru.
Right.

int wipebuffer(int bufnum) {
buffertable.addr+3[bufnum].addr = NULL;
/* etc etc */
}
Where would I put the cast in this scenario?

You shouldn't, because you should NEVER do anything like this to begin
with.

If you have a pointer to a given type, it should point to things of that
type, not to a buffer which contains some other stuff of other types followed
by the things it's supposed to actually point to.
I could use a separate variable tmpptr, cast buffertable.addr as void,
set tmpptr as buffertable.addr, increment tmpptr by 3, and then cast a
separate pointer (donkeyptr) as struct bufstru *donkeyptr to do the actual
work. Do I need those extra variables?

No, you need to not do this kind of thing ever.

You have addr point to the stuff it actually points to. You do not point
it somewhere else and try to calculate the right offset into it. If you want
a larger buffer that contains your table and some other stuff, fine, store
that value somewhere else, and have addr be a pointer to the part that
contains the table, but be aware of alignment -- you can't just arbitrarily
make any old pointer into a pointer to a given kind of object.
This is purely academic by the way, I am not using a prefix in this case. I
did initially cast buffertable.addr as void though.

No, you didn't. You may have cast it as "(void *)". Formally, it's not
defined to try to perform arithmetic AT ALL on a "(void *)". Some compilers
treat it the same way they'd treat arithmetic on a "(char *)".

-s
 
M

Mark Hobley

Seebs said:
Also, I strongly disreccomend the naming convention you're using. The
suffixes are not particularly informative, and "bufstruptr" tells us
virtually nothing. Give things names that reflect their function, not
their implementation.

bufstru is a structure for buffer related data
bufstruptr is a pointer to bufstru structures

What names would you have used?

The actual buffers will be allocated dynamically, and the structures will be
stored in a table which will keep track of each buffer in use. The table
itself is dynamic, and can be resized in the event that more buffers are
allocated than what the table can hold.

Mark.
 
S

Seebs

bufstru is a structure for buffer related data
bufstruptr is a pointer to bufstru structures
What names would you have used?

struct buffer;
struct buffer *<name>;

where "<name>" has to do with the actual purpose for which the
set of buffers is being used.

I would NEVER label a tag "stru" or "struc" or anything. That it's
a structure is either obvious or irrelevant.

-s
 
M

Mark Hobley

Seebs said:
struct buffer;

I would tend to use buffer as the variable name.

struct bufstru buffer;

So I would then refer to buffer.parm for one of its parameters.

I presume here that structure names and variables cannot be the same here.
I can't have struct buffer buffer can I?
struct buffer *<name>;

where "<name>" has to do with the actual purpose for which the
set of buffers is being used.

This is going to be part of a library of dynamic buffer allocation routines.
These are general purpose as far as this project is concerned.

It turns out that I will be writing a string handler for manipulating
pathnames that will utilize this library, but that is a separate project.
I would NEVER label a tag "stru" or "struc" or anything. That it's
a structure is either obvious or irrelevant.

What would you have called the pointer by the way?
Is is also obvious or irrelevant that it is a pointer?

Mark.
 
M

Mark Hobley

Seebs said:
No.

And the solution is, DON'T DO THAT.

Because it may be misaligned for the data.

In short: Don't use a pointer to your table to hold a pointer that
isn't actually to your table.

Ok. I have noted your comments and will look at this again, when a situation
for this exists. Thanks.

Mark.
 
S

Seebs

I presume here that structure names and variables cannot be the same here.
I can't have struct buffer buffer can I?

Sure. Why not? Tags and types are in their own namespace. The reason
you can't have "int int;" is that int is a magical token, rather than
a defined name; it's not that you can't in general have objects whose
names are the same as the name of a type.

Especially in the absence of a typedef, the mere fact that you're
writing "struct <structname>" means that you never need to point out
that the type is a structure. If you do something like
typedef struct buffer buffer_t;
then it is possible for someone not to know it's a structure, but
usually it doesn't matter -- either you're writing the buffer management
code, and you are obviously aware of what you're working on, or you're
writing code which uses the API, and you don't need to know what a
buffer_t is, only what it does.
What would you have called the pointer by the way?
Is is also obvious or irrelevant that it is a pointer?

Generally, I give them names which make it clear what they point to, and
yes, it's obvious that it's a pointer. You're using -> on it, so of
course it's a pointer.

So, for instance:

void free_buf(struct buffer *b) {
free(b->data);
free(b);
}

struct buffer *new_buf(char *data, size_t len) {
struct buffer *newbuf;
newbuf = malloc(sizeof(*newbuf));
if (newbuf) {
newbuf->data = malloc(len);
if (!newbuf->data) {
free(newbuf);
return 0;
}
memcpy(newbuf->data, data, len);
}
return newbuf;
}

int
read_input(void) {
struct buffer *line = readbuf(stdin);
...
}

As long as the name tells you the purpose of the variable, the mechanical
type is usually either irrelevant or obvious. Not always true, but
more often than you might think.

-s
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,015
Latest member
AmbrosePal

Latest Threads

Top