Using char[] as function argument is equivalent to have a char* variant

Discussion in 'C Programming' started by Xavier Roche, May 11, 2014.

  1. Xavier Roche

    Xavier Roche Guest

    Hi folks,

    I've never really used fixed-size char[] inside function argument list ;
    ie. as in:

    static void foo(char bar[32]) {
    strcpy(bar, "foo");
    }

    ... but I was expecting to get at least a warning when using it as in:

    char foo[32];
    foo(&foo[1]);

    (tested with GCC with -W -Wextra -pedantic)

    Aren't the types different ? Or did I obviously miss something
    ridiculously trivial ?

    My own explanation is that in this case "bar" is NOT of type char[32]
    but char* (probably because the array is not copied when calling the
    function, but its pointer is) [somehow confirmed by the fact that
    sizeof(bar) equals sizeof(char*), and not 32*sizeof(char) ...]

    Can a standard specialist confirm this (rather trivial) ?
     
    Xavier Roche, May 11, 2014
    #1
    1. Advertisements

  2. Am 11.05.2014 10:57, schrieb Xavier Roche:
    No. The type of &foo[1] is char *, and so is the function argument of
    foo(). Note that arrays decay into pointers when passed to functions,
    and formal array arguments of functions are also read as pointer
    arguments. IOWs, the above function declaration is identical to

    static void foo(char *bar)

    with no additional checks for the compiler involved. That you declare it
    as char[32] is just a matter of documentation, but has no further
    implications as far as the language is concerned.

    Yes.

    Greetings,
    Thomas
     
    Thomas Richter, May 11, 2014
    #2
    1. Advertisements

  3. Xavier Roche

    BartC Guest

    You can only pass pointers to functions not actual arrays. You can get the
    type-check you want using this:

    void strfn(char(*s)[32]) { ... }

    int main(void) {
    char t[33];

    strfn(&t);

    }

    This fails, because a strfn() expects a pointer to char[32], not the pointer
    to char[33] that is is passed. But:

    * strfn(t) would also fail, because char* is not a pointer to char[32]
    either

    * As written, you can't index s inside strfn as you'd normally expect (s,
    which expects s to be char*); you will have to use (*s), or copy (and
    cast) s to a normal char*. Nor can you pass it to standard string functions
    as it is.
     
    BartC, May 11, 2014
    #3
  4. Yes, bar's type is char *. The wording is in "6.7.6.3 Function
    declarators (including prototypes)" paragraph 7:

    "A declaration of a parameter as 'array of type' shall be adjusted to
    'qualified pointer to type', where the type qualifiers (if any) are
    those specified within the [ and ] of the array type derivation."

    The last bit is a little know feature. It enables you to declare a
    const pointer parameter using array notation (though I can't see why one
    would do that).
     
    Ben Bacarisse, May 11, 2014
    #4
  5. (snip)
    (snip)

    I believe you can pass arrays if they are inside a struct, and
    you pass the struct. K&R only allowed passing pointer to struct,
    but ANSI added struct by value.

    -- glen
     
    glen herrmannsfeldt, May 11, 2014
    #5
  6. Xavier Roche

    Xavier Roche Guest

    Le 11/05/2014 12:01, Ben Bacarisse a écrit :
    Ah, great, this is exactly what I was looking for, thank you! The C
    standard never fails to surprise me (even in very basic details such as
    this one)
     
    Xavier Roche, May 11, 2014
    #6
  7. IMHO in this case K&R makes more sense.
     
    Charles Richmond, May 11, 2014
    #7
  8. Really? Why?

    Modern C permits parameters of pointer-to-struct *and* struct types.
    K&R1 permitted only the former. How was that better?
     
    Keith Thompson, May 11, 2014
    #8
  9. I'm not a big fan of defining pointer paramaters using array notation,
    but adding "const" does make sense:

    void func(const int arr[]) {
    /* code that can't modify elements of the array */
    }

    ...

    const int arg[42];
    func(arg);
     
    Keith Thompson, May 11, 2014
    #9
  10. C is intended as a minimalist language. Is there anything you can do with
    passing structs that you can't do with pointer-to-struct? [*]

    From a practical point-of-view, one can always say that more features are
    better, because you are always free to not use the ones you don't like.
    But the mentality of C is not like that.

    [*] Equivalently, is there anything you could do (that you can't currently do)
    if you could pass arrays directly instead of just being able to pass
    pointers to them?

    (And, yes, I know how these two questions are connected, because you can
    now pass arrays [sort of] by wrapping them in a struct)

    And, finally, on a personal note, I don't like the passing structs-by-value
    thing, because:
    a) It's probably not efficient if the struct is large. And it is bad
    programming practice (IMHO) in any case.
    b) The elegance and beauty of passing structs around by pointer appeals
    to me and should not (Again, IMHO) be lost.

    --

    There are many self-professed Christians who seem to think that because
    they believe in Jesus' sacrifice they can reject Jesus' teachings about
    how we should treat others. In this country, they show that they reject
    Jesus' teachings by voting for Republicans.
     
    Kenny McCormack, May 11, 2014
    #10
  11. Xavier Roche

    David Brown Guest

    Passing a struct by value /may/ have optimisation benefits. Of course,
    if the struct is large you will need to do a lot more copying. But
    small structs can be passed inside registers - using a pointer forces
    the compiler to put the struct in memory, and use pointers and memory
    access. The other advantage is that when the struct is passed by value,
    the compiler knows that it has its own local copy that is private to the
    function in question (unless pointers to it are taken and passed out).
    If it only has a pointer to the struct, and your function calls other
    functions, then the compiler doesn't know whether or not the called
    function reads from or writes to the struct.
     
    David Brown, May 11, 2014
    #11
  12. Xavier Roche

    BartC Guest

    Some struct and array types that fit into 32 or 64 bits are passed neatly by
    value. It would be silly to use pointers to them, which when such values
    need to be returned (more likely with these small types), means managing
    memory.

    Also, arrays passed by value only really work for specific, fixed size
    arrays, which also means the length will be known, or can be deduced, by the
    called function.

    With larger arrays and structs, again it can reduce the problems of managing
    memory, when functions return such arrays and structs. If 'vector' is a
    struct (a typedef-ed one):

    vector a,b,c,d;

    a = addvec(b,addvec(c,d)); // a=b+c+d

    Here you don't need to worry about the intermediate result from addvec(c,d);
    there's no pointer to memory that needs deallocating.
     
    BartC, May 11, 2014
    #12
  13. Sure, but that's not what I commented on -- that's covered by the
    adjustment from 'array of type' to 'pointer to type' (the type is simply
    const qualified in both cases). I was talking about the rarely used
    permission to write

    void func(int arr[const]);

    This means

    void fund(int *const arr);

    because the qualifiers apply to the pointer, not to the pointed-to type.
    If you *really* want the parameter itself to be const (I never do), then
    the "natural" syntax is far less likely to confuse the reader. In fact,
    if I wanted a puzzle question about the obscure corners of C syntax,
    this would be my first choice.
     
    Ben Bacarisse, May 11, 2014
    #13
  14. It happened rather before that. K&R even contains the promise that some
    of the restrictions on structs will be removed in some future compiler
    version. In those days, what the Unix cc command is what C was, so C
    acquired passing structs in about 1979 (the same time C got "long").
     
    Ben Bacarisse, May 11, 2014
    #14
  15. Xavier Roche

    Kaz Kylheku Guest

    No, and in fact, compilers can implement all struct passing and returning
    using hidden arguments that are pointers.

    A function that returns a struct can use a calling convention whereby
    it receives an argument which indicates where to store it.
    This is true, but another aspect of C is that it is a combinative
    language, and the combinations of features that can be expressed should
    generally work. We don't want to banish "return x" when x happens to be a
    struct: we want the feature of value returning to combine with the struct type
    feature. (The situation with arrays is unfortunate.)

    This is minimalism of another form.

    A C language which allows structs to be treated like other values has a smaller
    definition than a C language which contains extra rules to prohibit that.
    (It just might have a smaller compiler.)

    Simiarly, a human being with an amputated leg isn't "more minimal" than one
    with two legs. Crippling something is subtly different from minimalism.
     
    Kaz Kylheku, May 12, 2014
    #15
  16. Xavier Roche

    Ian Collins Guest

    Not if it's already in registers...
     
    Ian Collins, May 12, 2014
    #16
  17. We also have rule that every line of C will compile to a handful of machine instructions
    which take essentially no time to execute. Allowing entire structures to be copied
    in an assignment statement breaks that rule.
     
    Malcolm McLean, May 12, 2014
    #17
  18. Xavier Roche

    BartC Guest

    How else would you copy structures?

    a=b is not going to take any more time than memcpy(&a,&b,sizeof(a)), but
    could well be easier to optimise (and is certainly easier to type).

    Or would an element-by-element copy be more in keeping with the rules?

    When you have a simple expression such as strcmp(p,q) that could potentially
    copy all of memory, then the handful of instructions rule goes out the
    window.

    More important is transparency.
     
    BartC, May 12, 2014
    #18
  19. Let's say a function is in a bottleneck. If the problem is that we've doing a lot of
    copying of very large structures, use of memcpy rather than = will make it
    somewhat easier to get to the root of the issue.
    memcpy should be a special function which the compiler always replaces with
    tailored inline code that takes account of alignment and so on. It's not always
    the case.
     
    Malcolm McLean, May 12, 2014
    #19
  20. Xavier Roche

    David Brown Guest

    Yes, you can tell if the struct has been put in a register - you look at
    the generated object code.

    You can also make a good guess in advance - if you are using a
    register-poor architecture like the x86, structs will mostly end up in
    memory along with everything else. If you are using a register-rich
    architecture like most RISC processors, the struct is small, and you
    don't take its address, then it will probably go in registers without
    having a copy in memory.
     
    David Brown, May 12, 2014
    #20
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.