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

M

Malcolm McLean

Who has that rule? Second guessing a compiler again? And you are aware
of instructions that bulk copy in "only a handful of instructions"?
Possibly you mean clock cycles?
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.
 
C

Charles Richmond

Keith Thompson said:
Really? Why?

Modern C permits parameters of pointer-to-struct *and* struct types.
K&R1 permitted only the former. How was that better?

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.
 
K

Keith Thompson

Charles Richmond said:
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.

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.
 
B

BartC

Charles Richmond said:
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.

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

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.
 
I

Ian Collins

Malcolm said:
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.

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 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.

There is a case for writing a = b if you want to write clear code and
allow the optimiser to do its job.
 
M

Malcolm McLean

Malcolm McLean wrote:

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?
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.
 
B

Bill Cunningham

Simiarly, a human being with an amputated leg isn't "more minimal" than
one
with two legs. Crippling something is subtly different from minimalism.

Who are you fooling? You don't believe that. Come on.
 
D

David Brown

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.

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.
 
D

David Brown

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.

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.
 
M

Malcolm McLean

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.
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.
 
D

David Brown

Not all the world is an Intel x86.

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.
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.

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.
 
M

Malcolm McLean

On 13/05/14 09:23, Malcolm McLean wrote:

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.
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.
 
M

Malcolm McLean

You are almost certainly not able to "micro optimise" in C better than
the compiler.
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.
 
D

David Brown

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.

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.
 
M

Malcolm McLean

On 13/05/14 14:44, Malcolm McLean wrote:

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.
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.
 

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

Ask a Question

Members online

Forum statistics

Threads
474,073
Messages
2,570,539
Members
47,197
Latest member
NDTShavonn

Latest Threads

Top