Good C programming style

S

Skarmander

But even so, it should only be used in a way that does not compromise
type safety, by defining the type-safe functions:

/* Unlike XGE_MKLINK_SLOT, the compiler will warn or stop if the
argument types do not match the formal parameter types. */
[return-type] xge_mklink_slot(xge_com* com, xge_cell from, xge_cell to,
tkey slotfrom, tkey slotto) {
return XGE_MKLINK_SLOT(com, from, to, slotfrom, slotto);
}
This can't possibly be right because xge_mklink_slot is defined in such
a way that it cannot evaluate to an expression. Should be

[return-type] xge_mklink_slot(xge_com* com, xge_cell from, xge_cell to,
tkey slotfrom, tkey slotto) {
XGE_MKLINK_SLOT(com, from, to, slotfrom, slotto);
}

It's likely that XGE_MKLINK_SLOT contains no return statement at all,
and [return-type] is just "void".

Left as an exercise to the reader: why could this otherwise produce
warnings of unreachable code, and how should we fix this? Just another
example of why macros should be used carefully. :)

S.
 
S

Sensei

What you have read is correct. Modern compilers have an optimiser to do
this for you.


Most welcome!
C does not specify a stack. All it specifies is that parameters are
passed by value (even pointers). On at least some systems parameters
will be passed in registers.


Quite understandable.
Type checking is a wonderful thing, it allows the compiler to complain
if you do something obviously stupid. Such as passing a pointer to a
function requiring an integer.


Yep. True.

We are quite used to that.

I strongly suggest you work through K&R2 and read the FAQ for this
group (which will explain what K&R2 is).


I'm already reading them :)
Also remember that high level languages were invented to make
programming easier, and this means you should forget about using a lot
of the little tricks you use as an assembler programmer.


Ok.
 
S

Sensei

Left as an exercise to the reader: why could this otherwise produce
warnings of unreachable code, and how should we fix this? Just another
example of why macros should be used carefully. :)


Thanks. I've read your post(s) and I find them very useful.

I have to learn :)
 
T

Thad Smith

Sensei said:
....
Ok, so defines are used to improve performances as inline functions are
not supported everywhere.

Yes, sometimes to a ridiculous degree. On the other hand, for embedded
systems simple function-like macros that have constant arguments can
generate very optimized code, due to the ability of the compiler to
evaluate the constant expressions at compile time.

Another use I make of function-like macros is for debugging. The
standard assert() macro is an example. Define NDEBUG and no code is
generated for the macro. I extend this concept for driving realtime
test points for embedded applications. In my case if the test pin macro
(separate from the function-like debug macro) is not defined, no code is
generated.

Thad
 
C

Chris Hills

Sensei <[email protected]> said:
Hi!

I'm thinking about a good programming style, pros and cons of some
topics. Of course, this has nothing to do with indentation... Students
are now java-dependent (too bad) and I need some serious motivations
for many issues... I hope you can help me :) I begin with the two major
for now, others will come for sure!

- function variables: they're used to java, so no pointers. Personally
I'd use always pointers, but why could be better to use:
void myfun(int &i) or (int *i) or (int i)
given that the function can and cannot (the last case of course) modify
a parameter. I see performance issues (stack related)...

- defines: why they use function and variable defines? why they shouldn't?
#define MY_PI 3.1415
#define fun(x) ((x) * (x))

Try MISRA-C

www.misra-c.com

It is the automotive (and now general embedded and safety critical)
coding guideline
 
M

Mark McIntyre

Gregory Pietsch said:


Why? Observe:

(calculation showing differences in radius of earth)

On the other hand, if you're trying to fit a piston into a cylinder,
such wild inaccuracies can matter. Either it will seize in use or will
be so slack as to be useless. This might be quite important if it were
say the pump for an artificial heart or the drive piston of a swing
bridge.
 
K

Keith Thompson

Mark McIntyre said:
(calculation showing differences in radius of earth)

On the other hand, if you're trying to fit a piston into a cylinder,
such wild inaccuracies can matter. Either it will seize in use or will
be so slack as to be useless. This might be quite important if it were
say the pump for an artificial heart or the drive piston of a swing
bridge.

And if I have a definition of PI that uses the full precision of
whatever type you're going to use (in this case, you should probably
define it with an "L" suffix and use at least as many digits as long
double supports), I don't have to *think* about whether it's good
enough. 3.1415 (or 3.1416) might be good enough for some
applications. 3.141592653589793238462643383279502884197169399375106L
is good enough for anything you can do with long doubles (assuming
long double is no more than 50 or so digits). And you only have to
define it once.
 
C

Chris Torek

Ok, so [#]defines [of function-like macros] are used to improve
performances as inline functions are not supported everywhere.

Or, sometimes, inline macros and/or functions are used to make
performance *worse*.

As a case in point, the BSD code that went into NetBSD performs
all file-system operations through what look like remote procedure
call (RPC) mechanisms, even when doing local procedure calls [%]:

#define VOP_READ(vp, uio, ioflag, cred) \
(GCC-specific code, omitted)
/* and so on for all the many vnode ops */

An RPC requires "marshalling" parameters into a data structure,
so the inline macro/function did something equivalent to:

args.desc = &vnode_read_desc;
args.vp = vp;
args.uio = uio;
args.ioflag = ioflag;
args.cred = cred;
/* find the function to call */
funcptr = inline_table_lookup(vp->filesysinfo, vnode_read_op);
return (*funcptr)(&args);

On the VAX, where this work was originally done in the 1990s,
expanding all the RPC code in-line sped things up, in part because
every subroutine call on the VAX took the equivalent of over a
dozen "regular" instruction times.

As an experiment, the NetBSD team tried "un-inlining" the code, so
that all the marshalling was in actual functions, instead of being
expanded in-line. On at least some, and I think most, of the
supported architectures, the result was a significantly smaller
*and faster* kernel.

[% I argued against this, back when Kirk McKusick was integrating
John Heidemann's changes, but I lost that argument.]
 
C

cs

On 2005-10-07 21:35:26 +0200, Skarmander <[email protected]> said:

you can do something

fun(t++);

expanded in

(t++)*(t++);

??

#define xge_mklink_slot(com,from,to,slotfrom,slotto) \
{\
tkey _slotfrom=(slotfrom);\
tkey _slotto =(slotto );\

_name are reserved for compiler; i don't like thid kind of macro
So, I was wondering why and when it's a good idea to use defines.

for me it is not a good idea for debug puorpose and ... because it is
more easy to write errors

the only macros that i like are
#define P printf
#define F for
#defile W while
#define U unsigned
#define R return

but no one seems agree in this with me ...
 
M

Malcolm

Keith Thompson said:
And if I have a definition of PI that uses the full precision of
whatever type you're going to use (in this case, you should probably
define it with an "L" suffix and use at least as many digits as long
double supports), I don't have to *think* about whether it's good
enough. 3.1415 (or 3.1416) might be good enough for some
applications. 3.141592653589793238462643383279502884197169399375106L
is good enough for anything you can do with long doubles (assuming
long double is no more than 50 or so digits). And you only have to
define it once.
If you are working with graphics, it generally doesn't matter if the image
is a pixel out. However if one polygon is a pixel too low, and the adjacent
one a pixel too high, you get a hole, which does matter.
One way of checking whether your code is robust to this type of problem is
to deliberately degrade accuracy, for instance by using 3.142 for PI.
 
G

Gregory Pietsch

(Concerning the accuracy of pi)

With 42 digits of pi, you could find the circumference of the known
universe to within the width of a proton, but for practical matters, an
accuracy of one or two decimal places is ample.

LOL, Gregory Pietsch
 
F

Flash Gordon

Gregory said:
(Concerning the accuracy of pi)

With 42 digits of pi, you could find the circumference of the known
universe to within the width of a proton, but for practical matters, an
accuracy of one or two decimal places is ample.

It all depends on what you are using PI for. The 1% or 0.1% error may
only be one error factor within the calculation, and it may be scaled up
by other things. I have certainly used PI in calculations where I have
also used other approximations (ones which actually saved a significant
amount of processing time). So if you are trying to reduce the error the
place to start is with the accuracy of the constants, because you can
improve those up to what your implementation can cope with without
impacting on other aspects of performance. Of course, a far easier
approach is to never introduce error in constants for the sake of a
little typing.
 
S

Sensei

for me it is not a good idea for debug puorpose and ... because it is
more easy to write errors

the only macros that i like are
#define P printf
#define F for
#defile W while
#define U unsigned
#define R return

but no one seems agree in this with me ...


Ok. I see.

So why I see so often something like:

#define CEXCERPT do { \
some(); \
C_code(); \
here(); \
} while(0)

Is it just some person thinking he would achieve more speed?
 
C

Chris Dollin

Sensei said:
So why I see so often something like:

#define CEXCERPT do { \
some(); \
C_code(); \
here(); \
} while(0)

Is it just some person thinking he would achieve more speed?

No, it's some person arranging that their macro can be expanded
as
CEXCERPT;

without confusion. (I would have thought, even more likely
with a parameterised macro.)
 
R

Richard Tobin

#define CEXCERPT do { \
some(); \
C_code(); \
here(); \
} while(0)
[/QUOTE]
No, it's some person arranging that their macro can be expanded
as
CEXCERPT;

without confusion.

In particular, as the body of an if. If the macro were defined as just
the bracketed part, then

if(test)
CEXCERPT;
else
...

would be an error, and

if(test1)
if(test2)
CEXCERPT;
else
...

would not behave as suggested by the indentation.

-- Richard
 
C

cs

Ok. I see.

So why I see so often something like:

#define CEXCERPT do { \
some(); \
C_code(); \
here(); \
} while(0)

Is it just some person thinking he would achieve more speed?

it is a trick
because some people want a more than one instruction macro and
they want save a call (in some loop i think)
a=1;
CEXCERPT;
b=1;

is expanded in
a=1;
{ some(); C_code(); here(); } while(0);
b=1;

so in this way you can use ";" for end the instruction (CEXCERPT;)

note that in this way
if(a==rand()%9)
CEXCERPT;
else b=1;
is also ok
is expanded in

if(a==rand()%9)
{ some(); C_code(); here(); } while(0);
else b=1;

the same seem ok
if(a==rand()%9)
{ CEXCERPT; }
while(a==rand()%9)
CEXCERPT;
while(a==rand()%9)
{ CEXCERPT; }
etc
 
C

cs

it is a trick
because some people want a more than one instruction macro and
they want save a call (in some loop i think)
a=1;
CEXCERPT;
b=1;

i would like to say this
is expanded in
a=1;
do { some(); C_code(); here(); } while(0);
b=1;

so in this way you can use ";" for end the instruction (CEXCERPT;)

note that in this way
if(a==rand()%9)
CEXCERPT;
else b=1;
is also ok
is expanded in

if(a==rand()%9)
do { some(); C_code(); here(); } while(0);
 
S

Sensei

No, it's some person arranging that their macro can be expanded
as CEXCERPT;

without confusion. (I would have thought, even more likely
with a parameterised macro.)


I understood why he does not use a ; at the end, but why someone would
have such a big macro... is beyond my knowledge, something like the
xge_whatever stuff I found... :)
 
K

Keith Thompson

Sensei said:
I understood why he does not use a ; at the end, but why someone would
have such a big macro... is beyond my knowledge, something like the
xge_whatever stuff I found... :)

One reason to do this is to save the overhead of a function call.
That's rarely worth the effort, though it can be if it's likely to be
invoked repeatedly in a loop. For example, putc() in <stdio.h> is
commonly implemented as a complex macro; likewise for the is*() and
to*() functions/macros in <ctype.h>.

Another reason is to do something that you can't do with a function,
even an inline one. For example, a function argument can only be an
expression; a macro argument can be a type name. The offsetof() macro
is an example that can't be implemented as a function.
 
C

Chris Dollin

Sensei said:
I understood why he does not use a ; at the end, but why someone would
have such a big macro... is beyond my knowledge, something like the
xge_whatever stuff I found... :)

How big is "big"?

#define NEEDS(mill, n) \
do { \
if (vm->myHeap->available < (Size) (n)) \
(FREEZE(), millRegenerate( mill ), MELT()); \
} while (0)
 

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

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top