Problem with va_ macros and arrays of arrays

Discussion in 'C Programming' started by rir3760, Mar 22, 2005.

  1. rir3760

    rir3760 Guest

    Since a few days ago I have been working with the program I post
    below (a school assignment). The purpose of the program is to work
    with the va_ macros (stdarg.h) and arrays of arrays, hopefully
    learning a little bit in the process.

    The objective of the fn_memset function is to set all the chars in
    the arrays passed (of type array N of array M of char) to a
    specific char, somewhat similar to what the standard function
    memset does:

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

    #define N_MAT 10
    #define T_NOM 40
    #define T_NIF 10
    #define T_POB 15

    char alu_nom[N_MAT][T_NOM];
    char alu_nif[N_MAT][T_NIF];
    char alu_pob[N_MAT][T_POB];

    void fn_memset(char car, int n_args, ...);

    int main(void)
    {
    int i;

    fn_memset('A', 6, T_NOM, alu_nom, T_NIF, alu_nif,
    T_POB, alu_pob);

    for (i = 0; i < N_MAT; i++){
    alu_nom[T_NOM - 1] = '\0';
    alu_nif[T_NIF - 1] = '\0';
    alu_pob[T_POB - 1] = '\0';
    }

    puts("Array alu_nom[][]:");
    for (i = 0; i < N_MAT; i++)
    printf("%s\n", alu_nom);
    printf("\n");

    puts("Array alu_nif[][]:");
    for (i = 0; i < N_MAT; i++)
    printf("%s\n", alu_nif);
    printf("\n");

    puts("Array alu_pob[][]:");
    for (i = 0; i < N_MAT; i++)
    printf("%s\n", alu_pob);
    printf("\n");

    return EXIT_SUCCESS;
    }

    void fn_memset(char car, int n_args, ...)
    {
    int i, j, tam;
    char *aux;
    va_list pa;

    va_start(pa, n_args);
    while(n_args > 0){

    tam = va_arg(pa, int);
    aux = va_arg(pa, char *);

    for (i = 0; i < N_MAT; i++)
    for (j = 0; j < tam; j++)
    *(aux + i * tam + j) = car;

    n_args -= 2;
    }
    va_end(pa);
    }

    The compiler (and splint) didn't emit any errors, and the program
    produced the expected output with no run-time errors but I think I
    just got 'lucky behaviour'.

    This because I discovered (with horror ;-) that the operation of
    'flattening an array' is illegal as the pointer to char moves from
    one array M of char to another one, or at least that's what I
    found in the book 'Pointers on C' by Kenneth A. Reek.

    As I'm still a beginner in C without a copy of the standard I
    don't want to fall into the scenario "OK let's try such and such
    and if it works in my system then ...". Instead what better than
    asking the pros of comp.lang.c a few questions? ;-)

    First, can the function fn_memset be implemented with standard C?

    Second, can a cast consist of values that are calculated at run-
    time? For example:

    void fn_memset(void *a, size_t n, size_t m)
    {
    size_t i, j;

    for (i = 0; i < n; i++)
    for (j = 0; j < m; j++)
    ((char (*)[m]) a)[j] = 'x';
    }

    Third (and related), can a pointer declaration consist of values
    that are calculated at run-time? In a similar vein:

    void fn_memset(void *a, size_t n, size_t m)
    {
    size_t i, j;
    char (*foo)[m] = a;

    for (i = 0; i < n; i++)
    for (j = 0; j < m; j++)
    foo[j] = 'x';
    }

    Last question, as you can see I have many holes in my knowledge of
    C so please can someone point me to didactic material (preferably
    online) about the correct use of declarations, casting and
    typedef's?

    Thanks for your time.
    rir3760, Mar 22, 2005
    #1
    1. Advertising

  2. On 22 Mar 2005 14:01:15 GMT, rir3760 <>
    wrote: (and no one else seems to have replied?)

    > Since a few days ago I have been working with the program I post
    > below (a school assignment). The purpose of the program is to work
    > with the va_ macros (stdarg.h) and arrays of arrays, hopefully
    > learning a little bit in the process.
    >
    > The objective of the fn_memset function is to set all the chars in
    > the arrays passed (of type array N of array M of char) to a
    > specific char, somewhat similar to what the standard function
    > memset does:
    >

    <snip>
    > char alu_nom[N_MAT][T_NOM];
    > char alu_nif[N_MAT][T_NIF];
    > char alu_pob[N_MAT][T_POB];
    >

    Aside: it appears here you want to have several (parallel) pieces of
    data or attributes about a series of entities. You can also achieve
    that with an array of struct, or possibly a pointer to a dynamic array
    of struct, and usually that's easier to code and maintain. For
    learning of course you should learn both (and in general all options),
    and if your teacher has decided to go this way first, or at this time,
    I realize that's not your responsibility.

    <snip>
    > fn_memset('A', 6, T_NOM, alu_nom, T_NIF, alu_nif,
    > T_POB, alu_pob);

    <snip>
    > void fn_memset(char car, int n_args, ...)
    > {
    > int i, j, tam;
    > char *aux;
    > va_list pa;
    >
    > va_start(pa, n_args);
    > while(n_args > 0){
    >
    > tam = va_arg(pa, int);
    > aux = va_arg(pa, char *);
    >
    > for (i = 0; i < N_MAT; i++)
    > for (j = 0; j < tam; j++)
    > *(aux + i * tam + j) = car;
    >

    I hope you know that's equivalent to aux[i*tam+j] = car;
    which is more usual and thus easier to read.

    > n_args -= 2;
    > }
    > va_end(pa);
    > }
    >
    > The compiler (and splint) didn't emit any errors, and the program
    > produced the expected output with no run-time errors but I think I
    > just got 'lucky behaviour'.
    >
    > This because I discovered (with horror ;-) that the operation of
    > 'flattening an array' is illegal as the pointer to char moves from
    > one array M of char to another one, or at least that's what I
    > found in the book 'Pointers on C' by Kenneth A. Reek.
    >

    For a non-char array say int x[3][6] using x[0][12] to access x[2][0]
    is technically illegal, but in practice it always works. Moreover,
    when accessing any object (area of memory) using a pointer to
    specifically character type (plain char, signed char, or unsigned
    char), you are guaranteed to be able to address all the bytes, and
    store to them (as you do); in C99 you are guaranteed to fetch them
    using pointer to unsigned char, which cannot have padding bits or trap
    representations, but maybe not signed char, and thus not plain char if
    it is signed, although in practice those work too. In C89 this was
    worded more loosely and it may be guaranteed that pointer to signed
    char also works; it certainly does in practice.

    You are actually doing something different: you are passing a
    char(*)[N] as a vararg thus without any conversion and picking it up
    as char*. This is not guaranteed to work; different kinds of pointers,
    that is to different types, can have different representations (and
    even sizes). In practice I don't know of and can't imagine an
    implementation where a pointer to char array would have any reason to
    be different from a pointer to char, but in theory it could happen. (I
    _do_ know rare cases where int *, or int(*)[N], or struct foo * or
    struct foo (*)[N], differs from char * and char (*)[N] and void *.)

    If you cast the pointer _in the call_ it would thus be totally safe:
    fn_memset ('A', 2, T_NOM, (char*)alu_nom);

    Alternatively, pointer to array-unbounded is required to be compatible
    with pointer to array of any fixed size, so you could pick them up as:
    typedef chararyptr char (*)[];
    chararyptr auxa = va_arg (pa, chararyptr);
    char * aux = *auxa /* or & * (*aux) to be explicit */
    /* or just use (*auxa)[i*m+j] or similar */
    (The typedef is needed because the typename in va_arg must be validly
    'pointerized' by just appending an asterisk.)

    That said, for real code you should just use memset(); that's what
    it's there for, it's (more) easily understood by anyone reading your
    code, it's one less thing to write and maintain and manage, and it's
    probably better optimized especially across platforms.

    > As I'm still a beginner in C without a copy of the standard I
    > don't want to fall into the scenario "OK let's try such and such
    > and if it works in my system then ...". Instead what better than
    > asking the pros of comp.lang.c a few questions? ;-)
    >

    Very good thinking. "What my implementation does" is not the right
    criteria _especially_ for C which deliberately leaves quite a bit of
    leeway -- some people feel too much -- for implementation variations.
    The standard isn't written as a tutorial and is difficult to read
    unless you already understand the concepts pretty well -- which it
    appears to me you do -- but if you want it you can purchase it in PDF
    form for individual use for USD 18 from ANSI, the US national body, at
    webstore.ansi.org, or as a deadtree book from Wiley with BSI the UK
    national body ISBN 0470845732 I believe about USD 40; or you can get
    the last publicly circulated draft in PDF, txt, and IIRC ps from the
    committee website std.dkuug.dk/JTC1/SC22/WG14/documents/n869/ .
    The draft is not exactly the standard, there were some small changes,
    but it is close enough to do a good deal of learning from. But, if you
    use it and need to post a citation here regarding some question, say
    it's from n869, so if you happen upon one of the few points that is
    different we will understand and not think you incompetent.

    There have actually been two "TCs" (Technical Corrigenda) to C99,
    which are both available free (last I looked) from ANSI, and I've been
    told the Wiley book incorporates TC1. Again these changes are minor.

    > First, can the function fn_memset be implemented with standard C?
    >

    See above.

    > Second, can a cast consist of values that are calculated at run-
    > time? For example:

    <snip>
    > ((char (*)[m]) a)[j] = 'x';
    > }
    >
    > Third (and related), can a pointer declaration consist of values
    > that are calculated at run-time? In a similar vein:

    <snip>
    > char (*foo)[m] = a;


    The bound(s) of a nonstatic nonmember array type (only) can be runtime
    values, including those two examples, in standard C99 (not yet widely
    implemented) or in GNU-C89 aka GCC as an extension (very widely
    available). Some other cases that would match your question as worded,
    like an array bound in a struct, a bitfield in a struct, or even a
    (stupidly placed) enum, cannot.

    > Last question, as you can see I have many holes in my knowledge of
    > C so please can someone point me to didactic material (preferably
    > online) about the correct use of declarations, casting and
    > typedef's?
    >

    I'd say you're doing pretty well to be asking questions at this level,
    and as far as my opinion matters (which isn't very far) you're welcome
    to continue. There's always the FAQ, at the usual places and a
    slightly old version at http://www.eskimo.com/~scs/C-faq/top.html .
    Beyond that I don't have any good, er, pointers.

    - David.Thompson1 at worldnet.att.net
    Dave Thompson, Apr 4, 2005
    #2
    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. Replies:
    80
    Views:
    2,416
    Stephen J. Bevan
    Nov 7, 2003
  2. Replies:
    1
    Views:
    438
    Marco Antoniotti
    Oct 7, 2003
  3. Replies:
    5
    Views:
    493
  4. Michael T. Babcock

    Re: Explanation of macros; Haskell macros

    Michael T. Babcock, Nov 3, 2003, in forum: Python
    Replies:
    0
    Views:
    515
    Michael T. Babcock
    Nov 3, 2003
  5. Andrew Arro

    macros-loop? calling macros X times?

    Andrew Arro, Jul 23, 2004, in forum: C Programming
    Replies:
    2
    Views:
    488
    S.Tobias
    Jul 24, 2004
Loading...

Share This Page