473,697 Members | 1,823 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Incrementing a void pointer. Legal C99?

Hi all,

The GNU C compiler allows a void pointer to be incremented and
the behaviour is equivalent to incrementing a char pointer.

Is this legal C99 or is this a GNU C extention?

Thanks in advance.

Erik
--
+-----------------------------------------------------------+
Erik de Castro Lopo
+-----------------------------------------------------------+
"C++ is a language strongly optimized for liars and people who
go by guesswork and ignorance." -- Erik Naggum
Apr 13 '06 #1
27 8954
Erik de Castro Lopo <no****@mega-nerd.com> writes:
The GNU C compiler allows a void pointer to be incremented and
the behaviour is equivalent to incrementing a char pointer.

Is this legal C99 or is this a GNU C extention?


gcc extension

check info gcc -> C Extensions

--
burton samograd kruhft .at. gmail
kruhft.blogspot .com www.myspace.com/kruhft metashell.blogs pot.com
Apr 13 '06 #2
Erik de Castro Lopo wrote:
The GNU C compiler allows a void pointer to be incremented and
the behaviour is equivalent to incrementing a char pointer.

Is this legal C99 or is this a GNU C extention?


Even if you specify -Wall -Wextra -Werror -std=c99 -pedantic ?
Apr 13 '06 #3
Erik de Castro Lopo wrote:
The GNU C compiler allows a void pointer to be incremented and
the behaviour is equivalent to incrementing a char pointer.

Is this legal C99 or is this a GNU C extention?


C99 (6.5.6) defines the addition of an integer to a pointer in terms of
a pointer pointing to an element of an array: after the addition of N
the pointer will point N elements forward (subject to array size
restrictions). C99 (6.2.5) also says that void is an incomplete type,
which means that arrays of that type can not be constructed. My
understanding from these two facts is that C99 doesn't define pointer
arithmetic with a void pointer.

Note that gcc -ansi -pedantic will issue a warning:

warning: wrong type argument to increment

--
Diomidis Spinellis
Code Quality: The Open Source Perspective (Addison-Wesley 2006)
http://www.spinellis.gr/codequality
Apr 13 '06 #4
Spoon wrote:

Erik de Castro Lopo wrote:
The GNU C compiler allows a void pointer to be incremented and
the behaviour is equivalent to incrementing a char pointer.

Is this legal C99 or is this a GNU C extention?


Even if you specify -Wall -Wextra -Werror -std=c99 -pedantic ?


Unfortunately I have to use -std=gnu99 to get access to
64 bit file offsets for the POSIX read/write/lseek
family of functions (I'm targeting POSIX rather than
real standard C).

Erik
--
+-----------------------------------------------------------+
Erik de Castro Lopo
+-----------------------------------------------------------+
"life is too long to know C++ well" -- Erik Naggum
Apr 13 '06 #5
> The GNU C compiler allows a void pointer to be incremented and
the behaviour is equivalent to incrementing a char pointer.
Is this legal C99 or is this a GNU C extention?


The pointer can be regarded as "memory address". The value of the
pointer can be incremented whatever the pointer type is. But you
cannot know the memory boundary of the structure to which the void
pointer refers.

In your case, the behavior is legal for both C99 and GNU C. But you
need to cast the void pointer into a known data type when you want to
access the content at the pointer.

Apr 14 '06 #6
da********@gmai l.com writes:
The GNU C compiler allows a void pointer to be incremented and
the behaviour is equivalent to incrementing a char pointer.
Is this legal C99 or is this a GNU C extention?

Please don't snip attribution lines. The above was written by Erik de
Castro Lopo.
The pointer can be regarded as "memory address". The value of the
pointer can be incremented whatever the pointer type is. But you
cannot know the memory boundary of the structure to which the void
pointer refers.
Right.
In your case, the behavior is legal for both C99 and GNU C. But you
need to cast the void pointer into a known data type when you want to
access the content at the pointer.


No, standard C does not allow arithmetic on void*; it's a gcc
extension.

--
Keith Thompson (The_Other_Keit h) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Apr 14 '06 #7
Burton Samograd wrote:
Erik de Castro Lopo <no****@mega-nerd.com> writes:
The GNU C compiler allows a void pointer to be incremented and
the behaviour is equivalent to incrementing a char pointer.

Is this legal C99 or is this a GNU C extention?


gcc extension

check info gcc -> C Extensions


Note that:
void *a; ..... a++; leads to a warning from gcc, but
void **a; .....a++; does not.
Probably you are probably trying to do the latter anyway.
Are the following identical semantically?
1)
void *a;
a = &foo;
a = (void **)a+1;

2)
void **a;
a = &foo;
a++;

I feel a little uneasy declaring things as void **, (or void ***), but
it seems to work just fine. I'm not sure why I feel uncomfortable with
it. Is it safe and valid?, or should the cast be used?

Apr 14 '06 #8
>> Erik de Castro Lopo <no****@mega-nerd.com> writes:
> The GNU C compiler allows a void pointer to be incremented and
> the behaviour is equivalent to incrementing a char pointer.
>
> Is this legal C99 or is this a GNU C extention?
Burton Samograd wrote:
gcc extension

check info gcc -> C Extensions

(Burton Samograd is correct. Note that when GCC is requested
to compile either Standard C89 or Standard C99, it does complain
about attempts to do pointer arithmetic of any sort on "void *".)

In article <11************ **********@u72g 2000cwu.googleg roups.com>
Bill Pursell <bi**********@g mail.com> wrote:Note that:
void *a; ..... a++; leads to a warning from gcc, but
void **a; .....a++; does not.
That is because the C Standards forbid the former (arithmetic on
"void *"), but allow the latter (arithmetic on a pointer that points
to "void *", i.e., values of type "void **").
Are the following identical semantically?
1)
void *a;
a = &foo;
a = (void **)a+1;

2)
void **a;
a = &foo;
a++;
Let me rewrite these as differently-named variables, so that
we can talk about "a" and "a" and not be confused as to whether
"a" means "a", or means instead "a". :-) I will rename the
first one "v1" and the second one "v2".

Consider a hypothetic machine that has 128-byte "void *"s, but
4 or 8 byte "int *", "double *", "struct S *", and so on. That
is, almost every pointer is just 4 or 8 bytes, as they are on
most machines with which most programmers are familiar -- but
for some reason (perhaps random stubborn-ness), the C compiler
writer chose to make "void *" *much* bigger.

In this case, "void *v1" declares a 128-byte pointer, and
sizeof(v1) is 128. Those 128 bytes hold some value(s); as
required by Standard C, after:

void *v1 = &foo;

the 128 bytes hold enough information to locate the variable
"foo", no matter what (data) type "foo" has (we can assume
"foo" is not a function name here).

On the other hand, "void **v2" declares an ordinary 4 or 8 byte
pointer, so that sizeof(v2) is 4 or 8. If we are lucky -- it is
not clear whether this is "good luck" or "bad luck" -- and we
write:

void **v2 = &foo;

and it compiles at all and runs, "v2" will also hold enough
information to locate the variable "foo". The C standards do
not *guarantee* this unless "foo" has type "void *" in the
first place, though, because whatever type "foo" has, &foo
produces a value of type "pointer to ____" (fill in the blank
with foo's type). The variable v2 has type "void **" and thus
can only point to "void *"s.

Let us get a little more concrete about our target machine, and
declare that, on our target machine, "void *" is 128 bytes, and
"int *" is 8 bytes, but "double *" is just 4 bytes. The machine
does this because it has a maximum of 32 gigabytes of RAM. Its
"int"s are 4 bytes long and always aligned on a 4-byte boundary,
but this means that they could be at any of 8,589,934,592 possible
locations, so we need 33 bits to address them. Its "double"s are
8 bytes long and always aligned on an 8-byte boundary, so they
can only be at any of 4,294,967,296 possible locations -- 32
bits suffices to address those.

"void *" remains 128 bytes because of the compiler-writer's whim,
apparently. But, because these are always aligned on a 128-byte
boundary, "void **" only needs to represent 268,435,456 distinct
values (28 bits), so "void **" is just 4 bytes, like "double *".

Now let us suppose that "foo" has type "int":

int foo;
void *v1 = &foo;
void **v2 = (void **)&foo;

The cast is needed here to force the compiler to accept the
conversion: &foo has type "int *" -- which is 8 bytes long -- and
we use the cast to squeeze it through a knothole, whacking off 4
of the 8 bytes, to put it into a "void **".

Having done all of this, "v1" definitely points to "foo" --
128 bytes of "void *" have plenty of room for 8 bytes' worth
of pointer value -- but "v2" might well not point to "foo" at
all, having lost some important bit(s).

Now, if we do:

v1 = ((void **)v1) + 1;

this takes the 128-byte long value in v1 -- which contains the
exact 8-byte address of the variable "foo", along with a bunch more
bytes that are not very interesting -- and scrapes it through the
knothole, producing a 4-byte value of type "void **" and discarding
the remaining 124 bytes. 120 of those 124 were probably useless,
but 4 of them might have held important data. Those data are gone!

Next, this adds 1 "void *"'s worth to the value computed so far.
Since sizeof(void *) is 128, this effectively adds 128 to the
poointer produced by scraping 124 bytes off the value in v1. Then
this result is converted back to "void *" -- adding back 124
bytes, but probably not restoring the lost data -- and that
result is stored back into "v1". Of course, this being all one
big expression, and the effect of "scraping off" value bits not
being defined by the C Standards, it is possible this *does*
restore the lost data, so that v1 points to "128 bytes past
the variable foo".

If we do:

v2++;

then we take the (not well defined) value in v2 -- the result of
squeezing &foo through that same knothole -- and increment it,
pointing to the next "void *" to which v2 points. Since v2 does
not point to "void *"s, the effect continues to be undefined,
but this probably effectively adds 128 to v2.

In other words, the two have similar effects -- but "v1" was
at least guaranteed to start out as valid, while v2 had no
guarantees at all.

Note that if we made "foo" have type "void *", the picture
changes. In this case, &foo has type "void **" -- pointer to
pointer to void -- which fits in v2 just fine, because v2
has type "pointer to pointer to void" and can thus point to
any "pointer to void". In this case, "v2++" is well-defined
-- it makes v2 point "one past the end of the array", where
"the array" is the "implied" array of size 1 that the Standards
guarantee for ordinary variables. Since v2++ is well-defined,
(v1 = (void **)v1 + 1) is also well-defined and then *does*
do the same thing.
I feel a little uneasy declaring things as void **, (or void ***), but
it seems to work just fine. I'm not sure why I feel uncomfortable with
it. Is it safe and valid?, or should the cast be used?


It may, I think, help to think of "void *" as a special type
(because it *is* in fact a *very* special type). It may even
help a bit further to use a typedef for it:

typedef void *Generic_Ptr;

Now we have an alias named Generic_Ptr that can be used to declare
variables (and structure members and so on):

Generic_Ptr p1;
Generic_Ptr p2;
Generic_Ptr arr[100];

Of course, given any object (loosely, "variable") of some type T,
we can always, in C, construct a value of type "pointer to T", and
declare variables suitable to hold such values. So if p1, p2,
and arr[i] are "Generic_Pt r"s, we can point to any of them:

Generic_Ptr *q;
...
q = &p1;
...
q = &p2;
...
q = &arr[i]; /* for some valid index "i" */

As with any pointer in C, "q" can be NULL, or it can point to a
single instance of a Generic_Ptr (like p1 and p2), or it can point
into an array (like &arr[i]). If it points to the first element
of an array:

q = arr;

then q[i] "means" the same thing as arr[i]. This is just like
any other pointer in C:

int iarr[100];
int *ip = iarr;
/* now ip[i] is just like iarr[i] */

char carr[100];
char *cp = carr;
/* now cp[i] is just like carr[i] */

And just as we can do things with "*ip++" and "*cp++" to step
through iarr[] and carr[], we can do things with "*q++" to step
through arr[], our array of "Generic_Pt r"s. All we have to
do is set elements of that array as appropriate:

arr[0] = &this_var;
arr[1] = &that_var;
arr[2] = &the_other_v ar;
arr[3] = NULL; /* marks the end */

for (q = arr; *q != NULL; q++) {
... do something with *q ...
}

Of course, typedefs do not actually define new types, so "q" has
type "void **" -- q has type "pointer to Generic_Ptr", but "Generic_Pt r"
is just another name for "void *".

Note that "void **" is *not* generic; it is an ordinary, non-special
pointer. It just happens to point *to* a special kind of pointer.

[pause here, if you like :-) ]

The "void ***" type in C is just like any other triple-pointer: it
can be NULL, or can point to a single "void **", or can point into
an array of "void **"s. If "q" has type "void **", then we can
define a "pq" thus:

void ***pq = &q;

Now *pq is just another way to name "q", and if we do:

q = arr;

then q[i] names arr[i] as before.

Most often, triple pointers in C like "qp" come about when we need
to write a function that sets a variable like "q".

For instance, suppose we start with a function that creates an
array of "char *"s:

char **make_arr(size _t n) {
char **space;
size_t i;

space = malloc(n * sizeof *space);
if (space == NULL) ... do something here ...
for (i = 0; i < n; i++)
space[i] = NULL;
}

This function is reasonably straightforward and might be used to
build an "argv" array. Of course, each argv[i] also has to be
set to some useful value, not just NULL -- only the last one is
NULL -- so we might augment it a bit further:

for (i = 0; i < n - 1; i++)
space[i] = malloc( ?? );
space[i] = NULL;

We still need to figure out how much space to allocate, i.e.,
to fill in the "??" part. We also need to handle the case where
we run out of memory (at least one of the malloc()s fails).

(Those paying particularly close attention should have noticed
by now that, with argc and argv, we have argv[argc]==NULL, not
argv[argc-1]==NULL. So make_arr() is not *quite* the same as
working with argv -- we would have to pass in argc+1.)

As the code progresses, we might eventually find ourselves
wanting to write a sub-function that takes "&space" and does
the malloc()-ing. Well, "space" has type "char **" here, so
&space has type "char ***".

If instead of an argv[]-like array, we want an arr[]-like array of
"void *"s, then we would have a "void **space", and if we decided
to put the allocation in a sub-function that takes &space, the
sub-function would have anargument of type "void ***".

Note, again, that it is ONLY the type "void *" that is special:
"void **", "void ***", and even "void ****" or "void *****" is just
ordinary pointer types, and obey all the normal rules for pointers
in C. The "void *" type is the ONLY "special" one, and its
"special-ness" is limited to ordinary assignments and those things
that emulate them (argument passing with prototypes, and -- because
casts are just very forceful assignments -- casts).

The moral, as it were, of the all this is that "void *" can point
to any data type, but "void **" can only point to "void *"; and
"void ***" can only point to "void **"; and so on. You can let
yourself think of "void *" as a special case, because it is; but
do not allow this to lead you into thinking that other types are
special too.
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
Apr 23 '06 #9
Chris Torek wrote:
Bill Pursell <bi**********@g mail.com> wrote:
Are the following identical semantically?
1)
void *a;
a = &foo;
a = (void **)a+1;

2)
void **a;
a = &foo;
a++;

<long and excellent description which shortens to "no" snipped>
In other words, the two have similar effects -- but "v1" was
at least guaranteed to start out as valid, while v2 had no
guarantees at all.


Thank you, that was very informative. I've been using a function to
create arrays, and I beleive my function is valid, but my method for
dereferencing the values is slightly incorrect. Based on your
description, I believe the following is correct. The first method,
commented as invalid, is how I've been using this. I believe I should
simply change to the second method. Comments welcome.

#include <stdlib.h>
#include <stdio.h>

void *
xmalloc(size_t size)
{
void *ret;
/*
* Using the comma is a stylistically horrible way of avoiding
* excessive {}'s. Is there a non-stylistic reason to avoid it?
*/
if ( ( ret = malloc(size)) == NULL)
perror("malloc" ), exit(-1);

return ret;
}

/*
* Recursively allocate a
* dimensions[0] x max_dim array of void *.
* If size > 0, malloc space for objects of that size.
*/
void *
make_array(unsi gned int *dimensions, unsigned int max_dim, unsigned int
size)
{
void **ret; /* declared as void ** to allow "ret[idx]". Is this
safe?*/
unsigned int idx;

if (max_dim == 0) {
if (size > 0)
return xmalloc(size);
else
return NULL;
}

ret = xmalloc(dimensi ons[0] * sizeof ret);

for ( idx = 0; idx < dimensions[0]; idx++) {
ret[idx] = make_array(dime nsions + 1, max_dim-1, size);
}

return ret;
}

int
main(void)
{
void *foo;
int i,j,k,l;
unsigned int dimensions[] = {5,2,4,8};

foo = make_array( dimensions, 4, sizeof foo);
/* Assign to the array in an invalid way. */
for (i=0; i<5; i++) {
for (j=0; j<2; j++) {
for (k=0; k<4; k++) {
for (l=0; l<8; l++) {
((int ****)foo)[i][j][k][l] = 1000*i + 100*j + 10*k
+ l;
printf("foo [%d][%d][%d][%d] = %d\n", i,j,k,l,
((int ****)foo)[i][j][k][l]);
}
}
}
}
printf("\n\n*** *************** **********\n\n" );

/* Assign to the array in a valid way. */
for (i=0; i<5; i++) {
for (j=0; j<2; j++) {
for (k=0; k<4; k++) {
for (l=0; l<8; l++) {
((int *)( ((void ****)foo)[i][j][k]))[l] =
1000*i + 100*j + 10*k + l;
printf("foo [%d][%d][%d][%d] = %d\n", i,j,k,l,
((int *)( ((void ****)foo)[i][j][k]))[l] );
}
}
}
}

return 0;
}

Apr 23 '06 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

6
8241
by: bob_jenkins | last post by:
{ const void *p; (void)memset((void *)p, ' ', (size_t)10); } Should this call to memset() be legal? Memset is of type void *memset(void *, unsigned char, size_t) Also, (void *) is the generic pointer type. My real question is, is (void *) such a generic pointer type that it
188
17346
by: infobahn | last post by:
printf("%p\n", (void *)0); /* UB, or not? Please explain your answer. */
56
3326
by: maadhuu | last post by:
hello, this is a piece of code ,which is giving an error. #include<stdio.h> int main() { int a =10; void *p = &a; printf("%d ", *p ); //error....why should it //be an error ?can't the compiler make out because //of %d that the pointer is supposed to refer to an integer ?? or is explicit type casting required ??
49
2785
by: elmar | last post by:
Hi Clers, If I look at my ~200000 lines of C code programmed over the past 15 years, there is one annoying thing in this smart language, which somehow reduces the 'beauty' of the source code ;-): char *cp; void *vp; void **vpp;
53
4800
by: subramanian100in | last post by:
I saw this question from www.brainbench.com void *ptr; myStruct myArray; ptr = myArray; Which of the following is the correct way to increment the variable "ptr"? Choice 1 ptr = ptr + sizeof(ptr);
9
3367
by: subramanian100in | last post by:
The following portion is from c-faq.com - comp.lang.c FAQ list · Question 6.13 int a1 = {0, 1, 2}; int a2 = {{3, 4, 5}, {6, 7, 8}}; int *ip; /* pointer to int */ int (*ap); /* pointer to array of int */\ ap = &a1; printf("%d\n", **ap);
0
8667
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
8597
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
9148
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
7708
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
0
5857
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
4357
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
1
3034
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
2319
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
1992
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.