By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
449,220 Members | 1,588 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 449,220 IT Pros & Developers. It's quick & easy.

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

P: n/a
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[i].iov_base transmitted */
if (n < iov[i].iov_len) {
/* the troublesome line 58 */
(char *)iov[i].iov_base += n;
iov[i].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[i].iov_len) {
i++;
continue;
}
/* keep retrying if interrupted by a signal
or would block */
do
n = (*fp)(fd, &iov[i], 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[i].iov_base transmitted */
if (n < iov[i].iov_len) {
(char *)iov[i].iov_base += n; /* line 58 */
iov[i].iov_len -= n;
break;
/* one array element transmitted completely */
} else {
n -= iov[i].iov_len;
i++;
}
}
}
return i;
}
--
http://preferans.de

Oct 6 '06 #1
Share this Question
Share on Google+
5 Replies


P: n/a


On Oct 6, 10:29 pm, "A. Farber" <Alexander.Far...@gmail.comwrote:
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[i].iov_base transmitted */
if (n < iov[i].iov_len) {
/* the troublesome line 58 */
(char *)iov[i].iov_base += n;
iov[i].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[i].iov_len) {
i++;
continue;
}
/* keep retrying if interrupted by a signal
or would block */
do
n = (*fp)(fd, &iov[i], 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[i].iov_base transmitted */
if (n < iov[i].iov_len) {
(char *)iov[i].iov_base += n; /* line 58 */
iov[i].iov_len -= n;
break;
/* one array element transmitted completely */
} else {
n -= iov[i].iov_len;
i++;
}
}
}
return i;

}--http://preferans.de
iov[i].iov_base = (char *)iov[i].iov_base + n; /* line 58 */

Oct 6 '06 #2

P: n/a
In comp.unix.programmer A. Farber <Al**************@gmail.comwrote:
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[i].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[i].iov_base = (char *) iov[i].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
--
\ Jens Thoms Toerring ___ jt@toerring.de
\__________________________ http://toerring.de
Oct 6 '06 #3

P: n/a

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[i].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[i].iov_base) += n;

Not so ugly option, almost certainly equivalent in every way:
iov[i].iov_base = (char *)iov[i].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[i].iov_base);

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

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

iov[i].iov_base = (char *)iov[i].iov_base + n;
printf("%p\n", iov[i].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
--
Martin Golding DoD #0236 | fo*****@comcast.net
Always code as if the person who ends up maintaining your code will be a
violent psychopath who knows where you live.

Oct 6 '06 #4

P: n/a
Martin Golding wrote:
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[i].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[i].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

Oct 6 '06 #5

P: n/a
Thank you all

Oct 6 '06 #6

This discussion thread is closed

Replies have been disabled for this discussion.