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

Discussion in 'C Programming' started by A. Farber, Oct 6, 2006.

  1. A. Farber

    A. Farber Guest

    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
     
    A. Farber, Oct 6, 2006
    #1
    1. Advertising

  2. A. Farber

    Cong Wang Guest

    On Oct 6, 10:29 pm, "A. Farber" <> wrote:
    > 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 */
     
    Cong Wang, Oct 6, 2006
    #2
    1. Advertising

  3. In comp.unix.programmer A. Farber <> wrote:
    > 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
    --
    \ Jens Thoms Toerring ___
    \__________________________ http://toerring.de
     
    Jens Thoms Toerring, Oct 6, 2006
    #3
  4. 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
    --
    Martin Golding DoD #0236 |
    Always code as if the person who ends up maintaining your code will be a
    violent psychopath who knows where you live.
     
    Martin Golding, Oct 6, 2006
    #4
  5. 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.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
     
    =?utf-8?B?SGFyYWxkIHZhbiBExLNr?=, Oct 6, 2006
    #5
  6. A. Farber

    A. Farber Guest

    Thank you all
     
    A. Farber, Oct 6, 2006
    #6
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Lucas Machado

    pointer-to-pointer (invalid lvalue in unary `&)

    Lucas Machado, Apr 3, 2004, in forum: C Programming
    Replies:
    19
    Views:
    11,065
    Irrwahn Grausewitz
    Apr 15, 2004
  2. mdh

    invalid lvalue in assignment

    mdh, Oct 28, 2007, in forum: C Programming
    Replies:
    4
    Views:
    372
  3. Replies:
    11
    Views:
    715
    James Kuyper
    Sep 22, 2008
  4. Chad

    invalid lvalue in assignment

    Chad, Jan 9, 2009, in forum: C Programming
    Replies:
    16
    Views:
    1,011
    Default User
    Jan 10, 2009
  5. Replies:
    2
    Views:
    1,915
Loading...

Share This Page