Invalid lvalue in assignment when trying to advance a void-pointer (struct iovec)

A

A. Farber

Hello,

I call readv() and writev() in several spots of a program which I run
under Linux, OpenBSD and Cygwin. Since it always the same way
(check the return value; then check errno and retry if EAGAIN/EINTR),
I've written a wrapper function (full source code on the bottom)
to call those functions and just pass the function pointer to it:

do {
...
} while ((n = transmit_iov(writev, cfg->fd, iov, iovcnt)) <= 0);

-OR-

if (transmit_iov(writev, pkid->fd, iov, 2) <= 0) {
...
}

It has always worked until I switched to FC5 Linux which uses:

$ gcc -v
Using built-in specs.
Target: i386-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man
--infodir=/usr/share/info --enable-shared --enable-threads=posix
--enable-checking=release --with-system-zlib --enable-__cxa_atexit
--disable-libunwind-exceptions --enable-libgcj-multifile
--enable-languages=c,c++,objc,obj-c++,java,fortran,ada
--enable-java-awt=gtk --disable-dssi
--with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre
--with-cpu=generic --host=i386-redhat-linux
Thread model: posix
gcc version 4.1.1 20060525 (Red Hat 4.1.1-1)

Now my compilation fails with:

$ gcc -Wall -I common -ggdb -O0 -DQUEUE_MACRO_DEBUG -D_GNU_SOURCE
-I openbsd-compat -I /usr/include -c common/common.c -o build/common.o
common/common.c: In function 'transmit_iov':
common/common.c:58: error: invalid lvalue in assignment

My problem is that I have an array struct iovec iov[], which is:

struct iovec {
void *iov_base; /* Starting address */
size_t iov_len; /* Number of bytes */
};

In my code I'm trying to advance the iov_base by the number of
bytes I have successfully transmitted in the previous readv/writev
call:

/* only part of iov.iov_base transmitted */
if (n < iov.iov_len) {
/* the troublesome line 58 */
(char *)iov.iov_base += n;
iov.iov_len -= n;
break;
}

And that's where gcc 4.x (or my code) is failing.

Does anybody please have a hint or solution for this probably
frequent problem: How to advance a void-pointer by few bytes?

Thank you
Alex

PS: and here is my wraper function for readv/writev():


/* The fp is either readv or writev; the iov array is not const */
int
transmit_iov(ssize_t (*fp)(int, const struct iovec*, int),
int fd, struct iovec iov[], int iovcnt)
{
int n;
unsigned i = 0;

while (i < iovcnt) {
/* skip eventual elements with iov_len=0
at the end of iov */
if (0 == iov.iov_len) {
i++;
continue;
}
/* keep retrying if interrupted by a signal
or would block */
do
n = (*fp)(fd, &iov, iovcnt - i);
while (-1 == n && (EINTR == errno || EAGAIN == errno));
/* give up on real failure or terminated connection */
if (n <= 0)
return n;
/* n bytes successfully transmitted, adjust iov[] */
while (n > 0) {
/* only part of iov.iov_base transmitted */
if (n < iov.iov_len) {
(char *)iov.iov_base += n; /* line 58 */
iov.iov_len -= n;
break;
/* one array element transmitted completely */
} else {
n -= iov.iov_len;
i++;
}
}
}
return i;
}
 
C

Cong Wang

Hello,

I call readv() and writev() in several spots of a program which I run
under Linux, OpenBSD and Cygwin. Since it always the same way
(check the return value; then check errno and retry if EAGAIN/EINTR),
I've written a wrapper function (full source code on the bottom)
to call those functions and just pass the function pointer to it:

do {
...
} while ((n = transmit_iov(writev, cfg->fd, iov, iovcnt)) <= 0);

-OR-

if (transmit_iov(writev, pkid->fd, iov, 2) <= 0) {
...
}

It has always worked until I switched to FC5 Linux which uses:

$ gcc -v
Using built-in specs.
Target: i386-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man
--infodir=/usr/share/info --enable-shared --enable-threads=posix
--enable-checking=release --with-system-zlib --enable-__cxa_atexit
--disable-libunwind-exceptions --enable-libgcj-multifile
--enable-languages=c,c++,objc,obj-c++,java,fortran,ada
--enable-java-awt=gtk --disable-dssi
--with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre
--with-cpu=generic --host=i386-redhat-linux
Thread model: posix
gcc version 4.1.1 20060525 (Red Hat 4.1.1-1)

Now my compilation fails with:

$ gcc -Wall -I common -ggdb -O0 -DQUEUE_MACRO_DEBUG -D_GNU_SOURCE
-I openbsd-compat -I /usr/include -c common/common.c -o build/common.o
common/common.c: In function 'transmit_iov':
common/common.c:58: error: invalid lvalue in assignment

My problem is that I have an array struct iovec iov[], which is:

struct iovec {
void *iov_base; /* Starting address */
size_t iov_len; /* Number of bytes */
};

In my code I'm trying to advance the iov_base by the number of
bytes I have successfully transmitted in the previous readv/writev
call:

/* only part of iov.iov_base transmitted */
if (n < iov.iov_len) {
/* the troublesome line 58 */
(char *)iov.iov_base += n;
iov.iov_len -= n;
break;
}

And that's where gcc 4.x (or my code) is failing.

Does anybody please have a hint or solution for this probably
frequent problem: How to advance a void-pointer by few bytes?

Thank you
Alex

PS: and here is my wraper function for readv/writev():

/* The fp is either readv or writev; the iov array is not const */
int
transmit_iov(ssize_t (*fp)(int, const struct iovec*, int),
int fd, struct iovec iov[], int iovcnt)
{
int n;
unsigned i = 0;

while (i < iovcnt) {
/* skip eventual elements with iov_len=0
at the end of iov */
if (0 == iov.iov_len) {
i++;
continue;
}
/* keep retrying if interrupted by a signal
or would block */
do
n = (*fp)(fd, &iov, iovcnt - i);
while (-1 == n && (EINTR == errno || EAGAIN == errno));
/* give up on real failure or terminated connection */
if (n <= 0)
return n;
/* n bytes successfully transmitted, adjust iov[] */
while (n > 0) {
/* only part of iov.iov_base transmitted */
if (n < iov.iov_len) {
(char *)iov.iov_base += n; /* line 58 */
iov.iov_len -= n;
break;
/* one array element transmitted completely */
} else {
n -= iov.iov_len;
i++;
}
}
}
return i;

}--http://preferans.de


iov.iov_base = (char *)iov.iov_base + n; /* line 58 */
 
J

Jens Thoms Toerring

In comp.unix.programmer A. Farber said:
It has always worked until I switched to FC5 Linux which uses:
$ gcc -Wall -I common -ggdb -O0 -DQUEUE_MACRO_DEBUG -D_GNU_SOURCE
-I openbsd-compat -I /usr/include -c common/common.c -o build/common.o
common/common.c: In function 'transmit_iov':
common/common.c:58: error: invalid lvalue in assignment
/* the troublesome line 58 */
(char *)iov.iov_base += n;


And that can't really work. By using the cast you calculate a new
value. But a value is not a variable, so you can't assign another
value to it (that would require a so-called "lvalue"), you just
can use it in further calculations but not on the left hand side
of an assignment. The simple fix is, obviously, to use

iov.iov_base = (char *) iov.iov_base + n;

instead (conversion of the result to void * is done automatically
by the compiler, so no further cast is required). That it seemed
to work with other compilers doesn't mean that it's correct, just
that the GCC writers got further in writing a more standard com-
pliant C compiler;-)
Regards, Jens
 
M

Martin Golding

On Fri, 06 Oct 2006 07:29:02 -0700, A. Farber wrote:

[much detail, relevant to the question but not relevant to the answer]
common/common.c: In function 'transmit_iov':
common/common.c:58: error: invalid lvalue in assignment
My problem is that I have an array struct iovec iov[], which is:

struct iovec {
void *iov_base; /* Starting address */
size_t iov_len; /* Number of bytes */
};

In my code I'm trying to advance the iov_base by the number of
bytes I have successfully transmitted in the previous readv/writev
call:
/* the troublesome line 58 */
(char *)iov.iov_base += n;

Does anybody please have a hint or solution for this probably
frequent problem: How to advance a void-pointer by few bytes?

Ugly option (though casting the address of a variable to a pointer
of a different type is, WHEN CLEARLY UNDERSTOOD, a useful idiom):
*(char **)&(iov.iov_base) += n;

Not so ugly option, almost certainly equivalent in every way:
iov.iov_base = (char *)iov.iov_base + n;


Code:

#include <stdio.h>

struct iovec {
void *iov_base; /* Starting address */
size_t iov_len; /* Number of bytes */
};

int
main(int argc, char *argv[])
{
struct iovec iov[2] = {{0}};
int i = 1, n = 2;

printf("%p\n", iov.iov_base);

(char *)iov.iov_base += n; /* OP's error, my warning */
printf("%p\n", iov.iov_base);

*(char **)&(iov.iov_base) += n;
printf("%p\n", iov.iov_base);

iov.iov_base = (char *)iov.iov_base + n;
printf("%p\n", iov.iov_base);

return 0;
}


cc xmpl.c -o xmpl
xmpl.c: In function `main':
xmpl.c:16: warning: use of cast expressions as lvalues is deprecated

./xmpl
(nil)
0x2
0x4
0x6


Martin
 
G

Guest

Martin said:
On Fri, 06 Oct 2006 07:29:02 -0700, A. Farber wrote:

[much detail, relevant to the question but not relevant to the answer]
common/common.c: In function 'transmit_iov':
common/common.c:58: error: invalid lvalue in assignment
My problem is that I have an array struct iovec iov[], which is:

struct iovec {
void *iov_base; /* Starting address */
size_t iov_len; /* Number of bytes */
};

In my code I'm trying to advance the iov_base by the number of
bytes I have successfully transmitted in the previous readv/writev
call:
/* the troublesome line 58 */
(char *)iov.iov_base += n;

Does anybody please have a hint or solution for this probably
frequent problem: How to advance a void-pointer by few bytes?

Ugly option (though casting the address of a variable to a pointer
of a different type is, WHEN CLEARLY UNDERSTOOD, a useful idiom):
*(char **)&(iov.iov_base) += n;


This isn't allowed in standard C, and GCC (which the OP was using) will
not make this code do what you expect it to, if certain optimisations
are enabled.

$ cat >test.c
#include <stdio.h>
int main(void) {
char a[] = { 1, 2, 3 };
void *p = a;
*(char **) &p += 2;
printf("%d\n", *(char *) p);
}
$ gcc -std=c99 -pedantic -Wall test.c -o test && ./test
3
$ gcc -std=c99 -pedantic -Wall test.c -o test -O2 && ./test
test.c: In function ‘main’:
test.c:5: warning: dereferencing type-punned pointer will break
strict-aliasing rules
1
 

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,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top