endof

M

Michael B Allen

Is there a macro for determining the address of the end of an object? For
example, if f is a pointer to an instance of the below struct, endof(f->a)
would be equal to the address of member b.

struct foo {
int a[N];
int b;
};

If not, is the following sufficient in all cases?

#define endof(o) ((char *)(&(o)) + sizeof(o))

Mike
 
S

Suman

Michael said:
Is there a macro for determining the address of the end of an object? For
example, if f is a pointer to an instance of the below struct, endof(f->a)
would be equal to the address of member b.

struct foo {
int a[N];
int b;
};

If not, is the following sufficient in all cases?

#define endof(o) ((char *)(&(o)) + sizeof(o))
We have just had a pretty long discussion on offsetof() macro, that
should
serve you well.Look at the FAQ & search the group, please.
 
M

Michael Mair

Michael said:
Is there a macro for determining the address of the end of an object? For
example, if f is a pointer to an instance of the below struct, endof(f->a)
would be equal to the address of member b.

You mean the first address _after_ the end of an object.
struct foo {
int a[N];
int b;
};

Note that there may be padding between structure members
even in this case.
The "relative address" of b within a struct foo can be found
by using the offsetof macro from said:
If not, is the following sufficient in all cases?

#define endof(o) ((char *)(&(o)) + sizeof(o))

Nope. Think of arrays passed (via pointer to their first element)
to a function.
It should work for scalar types and struct and union types.
Depending on the intended use, I would consider casting the
calculated address to void *.


Cheers
Michael
 
C

Christian Kandeler

Michael said:
Is there a macro for determining the address of the end of an object? For
example, if f is a pointer to an instance of the below struct, endof(f->a)
would be equal to the address of member b.

I hope you are aware that the "end" of a and the beginning of b are not
necessarily the same.
And no, there is no macro for that in the standard library. And if there
were, I'd wonder why.
struct foo {
int a[N];
int b;
};

If not, is the following sufficient in all cases?

#define endof(o) ((char *)(&(o)) + sizeof(o))

If that is really what you want, then it's correct ;)
What do you intend to use it for?


Christian
 
L

Lawrence Kirby

On Mon, 27 Jun 2005 09:07:21 +0200, Michael Mair wrote:

....
Nope. Think of arrays passed (via pointer to their first element)
to a function.

The macro still works, but it gives you (one past) the end of the object
you specify i.e. a pointer in that case.
It should work for scalar types and struct and union types.

It works for arrays too, as long as you use on the actual array and not a
pointer. For example with:
struct foo {
int a[N];
int b;
};

struct foo s;

then

endof(s.a);

will give you the address of the character just after the end of s.a.

Lawrence
 
M

Michael Mair

Lawrence said:
On Mon, 27 Jun 2005 09:07:21 +0200, Michael Mair wrote:

...



The macro still works, but it gives you (one past) the end of the object
you specify i.e. a pointer in that case.

Yep. I just wanted to warn about it.

It should work for scalar types and struct and union types.

It works for arrays too, as long as you use on the actual array and not a
pointer. For example with:
struct foo {
int a[N];
int b;
};

struct foo s;

then

endof(s.a);

will give you the address of the character just after the end of s.a.

Thanks, this was too sloppy on my part.

Cheers
Michael
 
M

Michael B Allen

struct foo {
int a[N];
int b;
};

If not, is the following sufficient in all cases?

#define endof(o) ((char *)(&(o)) + sizeof(o))

If that is really what you want, then it's correct ;)
What do you intend to use it for?

I'll give you two examples.

1) I have a habit of using limit pointers where someone might normally
pass a size. For example instead of using strncpy I prefer the following
function instead:

int
str_copy(const unsigned char *src,
const unsigned char *slim,
unsigned char *dst,
unsigned char *dlim,
int n);

The idea is that it's a little less error prone to assert 'src < slim'
as opposed to 'n > 0' and then also have to adjust n by some appropriate
amount. Now if all of your string processing functions are designed
like this, they tend to cascade together resulting in overall smaller
code. This is safer because slim never changes.

So sometimes I find myself computing where the end of an object is. For
example:

struct foo {
int i;
char name[NAME_MAX];
};

str_copy(src, slim, f->name, endof(f->name));

This case is a little too simple to illustrate its usefulness but I do
a lot of complex decoding and encoding of formats and find techniques
like this important.

2) Another case is when I embed a bitset at the end of a struct to allow
the number of bits to be variable like:

struct fancy_array {
...
void *blim; /* endof bitset */
char bitset[1]; /* incomplete */
}
struct foo {
struct fancy_array bar;
char bar_bitset[MAX_BARS / 8];
...

So bar_bitset just reserves space for the fancy_array's bitset. But to
initialize this I need to pass the end of bar_bitset so we can set blim:

fancy_array_init(&f->bar, endof(f->bar_bitset));

Mike
 
C

Christian Kandeler

Michael said:
I'll give you two examples.

1) I have a habit of using limit pointers where someone might normally
pass a size.

That's okay, then. As you may have noticed, some of us were afraid you'd use
it to compute the offset of struct members.
For example instead of using strncpy I prefer the following
function instead:

int
str_copy(const unsigned char *src,
const unsigned char *slim,
unsigned char *dst,
unsigned char *dlim,
int n);

The idea is that it's a little less error prone to assert 'src < slim'
as opposed to 'n > 0' and then also have to adjust n by some appropriate
amount. Now if all of your string processing functions are designed
like this, they tend to cascade together resulting in overall smaller
code. This is safer because slim never changes.

I don't see how that makes things safer. On the contrary, you are
introducing redundancy (and a source for errors) by adding the additional
constraint of (dlim - dst == slim - src). And on top of all that, you still
pass n. I would stick with the canonical way.
So sometimes I find myself computing where the end of an object is. For
example:

struct foo {
int i;
char name[NAME_MAX];
};

str_copy(src, slim, f->name, endof(f->name));

This case is a little too simple to illustrate its usefulness but I do
a lot of complex decoding and encoding of formats and find techniques
like this important.

The technique of passing the same information with a higher number of
arguments? ;)
2) Another case is when I embed a bitset at the end of a struct to allow
the number of bits to be variable like:

struct fancy_array {
...
void *blim; /* endof bitset */
char bitset[1]; /* incomplete */

Huh? Why is blim the end of bitset?
}
struct foo {
struct fancy_array bar;
char bar_bitset[MAX_BARS / 8];
...

So bar_bitset just reserves space for the fancy_array's bitset. But to
initialize this I need to pass the end of bar_bitset so we can set blim:

fancy_array_init(&f->bar, endof(f->bar_bitset));

You completely lost me here... Either this makes no sense or I am stupid.


Christian
 
M

Michael Mair

Christian said:
Michael B Allen wrote:



That's okay, then. As you may have noticed, some of us were afraid you'd use
it to compute the offset of struct members.


I don't see how that makes things safer. On the contrary, you are
introducing redundancy (and a source for errors) by adding the additional
constraint of (dlim - dst == slim - src). And on top of all that, you still
pass n. I would stick with the canonical way.

Yep, but n can now be easily larger than slim-src, or slim-src and/or
n can be larger than dlim-dst and you can easily implement the whole
thing safely.

So sometimes I find myself computing where the end of an object is. For
example:

struct foo {
int i;
char name[NAME_MAX];
};

str_copy(src, slim, f->name, endof(f->name));

This case is a little too simple to illustrate its usefulness but I do
a lot of complex decoding and encoding of formats and find techniques
like this important.

The technique of passing the same information with a higher number of
arguments? ;)

Nope. Think of copying something other than strings -- there you
need some way of finding out about maximum number of bytes to be
copied and maximum number of bytes the destination can hold.
At least with the above way of going about it, of course.

2) Another case is when I embed a bitset at the end of a struct to allow
the number of bits to be variable like:

struct fancy_array {
...
void *blim; /* endof bitset */
char bitset[1]; /* incomplete */

Huh? Why is blim the end of bitset?

It is not. But it is intended to _hold_ the end (see below)
}
struct foo {
struct fancy_array bar;
char bar_bitset[MAX_BARS / 8];
...

So bar_bitset just reserves space for the fancy_array's bitset. But to
initialize this I need to pass the end of bar_bitset so we can set blim:

fancy_array_init(&f->bar, endof(f->bar_bitset));

You completely lost me here... Either this makes no sense or I am stupid.

Neither. Think of it as a variant of the struct hack. Instead of
allocating the excess memory, you use automatic (or static)
struct variables. You can access everything by a pointer to struct
fancy_array, so fancy_array only has to know its "real" size or
its ends (stored in blim or passed explicitly to functions from
the outside.

Maybe the OP can clarify this further if I did not get all of it
(or are completely off the track).


Cheers
Michael
 
M

Michael B Allen

Perhaps it is best if I just provide the function in question:

int
str_copy(const unsigned char *src,
const unsigned char *slim,
unsigned char *dst,
unsigned char *dlim,
int n)
{
unsigned char *start = dst;

if (dst == NULL || dst >= dlim) {
return 0;
}
if (src == NULL || src >= slim) {
*dst = '\0';
return 0;
}
while (n-- && *src) {
*dst++ = *src++;
if (src == slim || dst == dlim) {
dst = start;
break;
}
}
*dst = '\0';

return dst - start;
}
Yep, but n can now be easily larger than slim-src, or slim-src and/or
n can be larger than dlim-dst and you can easily implement the whole
thing safely.

Right. The lim pointers are just "fence post"s. When you decode or
encode something you usually know where the end of your buffer is and
that never changes (the limit pointer). Regardless of what hula-hoops
your code goes through the lim pointers should never change. This makes
the src < slim check very consistent and less prone to error because
you're not recomputing the sentinel all the time.

Admittedly that function might not be ideal to illustrate my point because
it does add the additional constaint of 'n'. Perhaps my alternative to
strlen is better:

int
str_length(const unsigned char *src, const unsigned char *slim)
{
const unsigned char *start = src;

if (src == NULL || src >= slim) {
return 0;
}
while (*src) {
src++;
if (src == slim) {
return 0;
}
}

return src - start;
}
Neither. Think of it as a variant of the struct hack. Instead of
allocating the excess memory, you use automatic (or static)
struct variables. You can access everything by a pointer to struct
fancy_array, so fancy_array only has to know its "real" size or
its ends (stored in blim or passed explicitly to functions from
the outside.

Maybe the OP can clarify this further if I did not get all of it
(or are completely off the track).

That's exactly right. I didn't describe it very well but you get it.

Mike
 

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

Forum statistics

Threads
474,166
Messages
2,570,901
Members
47,442
Latest member
KevinLocki

Latest Threads

Top