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

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

  1. One of the advantages of C is that you often can second-guess the compiler.

    If you know the instruction set, or even if you don't know the instruction set
    but you know what type of processor you're compiling for, you can often make
    a very good guess what machine instructions you'll generate. So you try to
    write functions with four or fewer parameters on architectures where the
    calling convention is that the first four go in registers, for example.

    There is a case for writing a = b rather than memcpy(&a, &b, sizeof(struct t))
    when the processor has a block copy, if the programmer is mentally translating
    machine instructions to C and back. One way of generating assembly is to write
    a function in C, then gradually replace the C with C instructions that match exactly
    one assembly instruction, then mechanically translate from C to assembly.

    But generally you're more interested in cycles.
    Malcolm McLean, May 12, 2014
    1. Advertisements

  2. K&R makes more sense because arrays *and* structs could only be past by
    reference, a pointer to the actual memory. Now if you can pass an actual
    struct, as it was pointed out, you can put an array *inside* the struct and
    thus pass an array. But you still can *not* pass an array by value by
    itself. Inconsistent.

    I think consistency is better.
    Charles Richmond, May 12, 2014
    1. Advertisements

  3. Ok, then I disagree. Consistency is better than inconsistency *all else
    being equal*, but all else is not equal in this case. Being able to
    pass structs by value is *useful*. I wouldn't want to lose that useful
    feature for the sake of a consistency that adds no expressive power.

    Being able to pass arrays by value could be even more useful *and*, if
    done right, could make the language more consistent. But given the way
    C has evolved, there's no good way to add that to the language without
    breaking existing code. The issues that make it difficult to define
    passing arrays by value don't apply to structs.
    Keith Thompson, May 12, 2014
  4. Xavier Roche

    BartC Guest

    Consistent would have been to pass arrays by value as well as structs and

    Not passing structs by value would have a different inconsistency: you could
    pass a standalone int by value, but not a struct consisting of one int, nor
    a one-element array of int.
    BartC, May 12, 2014
  5. Xavier Roche

    Ian Collins Guest

    Hold on a minute, something smells ratty here. On the "strange warning"
    thread you are advocating dynamic sizing as a (valid, I agree) sound
    design technique for writing more generic code while here you are
    suggesting the ultimate in machine specific micro-optimisation?

    Write code that expresses the intent and let the compiler take care of
    the optimisation, which may well be the equivalent of a call to memcpy().
    There is a case for writing a = b if you want to write clear code and
    allow the optimiser to do its job.
    Ian Collins, May 12, 2014
  6. Sometimes you do have to micro-optimise. It's not something I do much
    of these days. But sometimes you have to do it. You don't always have
    control over the hardware the program has to run on. Buying a faster
    processor is often a lot cheaper than developing a faster program,
    but sometimes it's simply not an option.

    A good approach is to write the function in C, get it right, debugged,
    tested, handling all the corner cases. Then keep a correct C version,
    and slowly convert a second C version to assembly, using the original
    known correct function to test it against. Particularly if you don't
    do much assembly programming and aren't all that familiar with the
    instruction set.
    Malcolm McLean, May 12, 2014
  7. Who are you fooling? You don't believe that. Come on.
    Bill Cunningham, May 13, 2014
  8. Xavier Roche

    David Brown Guest

    This is particularly true of something like copying structs - when you
    write "a = b", the compiler can choose to insert a call to memcpy().
    And when you write memcpy(), the compiler can choose to inline block
    copies, or copy loops, or whatever works best on that target.
    David Brown, May 13, 2014
  9. Xavier Roche

    David Brown Guest

    There are occasions when one still needs to write assembler - but these
    are rare now, and mainly niche areas. (I think it is important to be
    able to read and understand assembly, especially for embedded systems,
    but writing good assembly is seldom necessary.)

    Write /good/ C code - with an emphasis on making it clear, maintainable,
    correct (and clearly correct), and using appropriately efficient
    algorithms and methods. Let the compiler do the job of generating
    efficient object code from it.

    There are times when you want to spend significant time getting a
    particular function to be as small and/or fast as possible. To do that,
    you must work /with/ your compiler - not against it, as you seem to want
    to do. You need to look at the generated code, take real-life
    measurements, and experiment with different compiler options and code
    structures. What you do /not/ want to do is try to write assembly code
    and then force the compiler to generate that assembly from your C code.
    That would lose all flexibility and maintainability of the C code, and
    almost certainly result in worse code.

    Remember, the compiler is better at generating assembly than /you/ are -
    it knows more about the timings of the processor, the pipelines, and the
    weird instructions. It can generate more convoluted sequences while
    keeping the details clear in its "head". If you think you can out-do
    your compiler while writing maintainable assembler (written in assembler
    or in "C") in many real-world situations, then you have /far/ too much
    time on your hands - and you would make better use of that time in other
    parts of your code, or by sending patches to gcc/llvm to improve their
    code generators.
    David Brown, May 13, 2014
  10. Not all the world is an Intel x86.
    Whilst reasonably efficient C compiler are available for practically all
    processors, often there hasn't been much development effort spent on
    them. Just a quick retarget of an existing compiler.
    Malcolm McLean, May 13, 2014
  11. Xavier Roche

    David Brown Guest

    As I have noted many times in my posts, I worked with embedded systems -
    mostly small embedded systems. I do almost no C work on x86 systems,
    and I can't comprehend how you could possibly have read my posts and
    then imagined that I have only considered x86 systems.
    I have done C programming for at least 10 different processor
    architectures, and assembly programming on close to 20 architectures -
    including those that I program in C. I have not written any C
    compilers, but I have helped those that do. And I have done some study
    of processor design and architecture at university and as a hobby.

    So I am well aware of how C compilers work, how they are written or
    ported, which compilers are good or bad on different architectures,
    which architectures are good or bad for C development, and also which
    architectures have issues with pipelines, instruction timings, etc., for
    which target architectures there are different binary compatible
    implementations with significantly different optimisation issues, and
    which architectures are easy or hard for efficient assembly programming.

    I don't know what your background is, but your posts suggest that your
    world consists of x86/amd64 targets (or perhaps another "big"
    architecture). I am sure you know much more about that area than I do,
    but I would be surprised if you have a broader experience of multiple
    targets and different C compilers than I do.

    I also happen to know that in the world of small embedded systems,
    efficient code generation means smaller and cheaper chips - which means
    a lot of money on large production runs. People sometimes pay very
    large sums of money for embedded development tools - and embedded
    development tool vendors spend a great deal of time and money making
    their tools more efficient than their competitors'. In many cases, the
    x86 toolchain authors copy the innovations and developments of embedded
    toolchain developers - not the other way round.
    David Brown, May 13, 2014
  12. Mainly I program for large systems, as you suggest.

    But I do also occasionally do work for smaller systems. It's not always
    true that the C compiler is better at generating code than a human
    programmer. For example one I used recently wouldn't replace a constant
    function pointer parameter with a direct call. You can't always trust it
    like that.
    Malcolm McLean, May 13, 2014
  13. It depends what type of code you write.
    A frequency transform, for example, is quite difficult for a compiler to
    produce optimal machine code for, especially if the programmer has
    coded it for clarity rather than to minimise the number of variables.

    It also depends on the quality of the compiler, of course.
    Malcolm McLean, May 13, 2014
  14. Xavier Roche

    David Brown Guest

    No compiler is perfect, and there are sometimes things that you know
    about that are hard to communicate to the compiler in order to let it
    generate tighter code.

    But the /general/ rule is that for a decent compiler on a decent target,
    and for maintainable code, the compiler will do this sort of thing
    better than hand-programming in assembly. Maintainability is key here -
    with C, you can write your code in a convenient way using a function
    pointer, and let the compiler optimise it. With assembly, you can be
    sure of getting the optimisation - but if you change your code slightly
    (perhaps making the function pointer non-constant), you have to re-do
    parts of the code.
    David Brown, May 13, 2014
  15. Exactly.
    I see assembly as very much last resort. First you use the algorithm with the
    lowest big O run time, then you strip out as much of the gift-wrapping and
    duplicate calculation as possible. Then finally you start counting machine
    cycles. That's the point at which assembly might come in.
    Malcolm McLean, May 13, 2014
    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.