[net.lang.c] printf

sch (04/07/83)

Making printf a system call is the worst idea I have heard yet.
The Unix system was designed along the principle of having the kernel,
provide only those services which are necessary.  Why not make the shell
a system call, and how about all the other library routines!

Actually, there is something that can be done about this.  Why not make the
library routines available as common shareable text pages.  This would
reduce the disk/memory load of the routines without inviting the do everything
in the kernal method so popular with DEC operating systems.

		Not afraid to Defend Unix,
		Stephen Hemminger

leichter (04/07/83)

Stephen Hemminger proposes to make library routines "available as common
shareable text pages ... [not] inviting the do everything in the kernal
[sic] method so popular with DEC operating systems."

Hate to disabuse you of your prejudices, but most DEC operating systems do
exactly this.  In RSTS, the shared text goes under the names of RunTime System
or Shared Library; on RSX it is one of several kinds of shared library; and
in VMS, where this was built in from the start, essentially all the library
routines get used this way through "global sections".  In this area, at least,
it's Unix that ought to get on the ball and catch up with what everyone else
has known is "the right approach" for years...
							-- Jerry
						decvax!yale-comix!leichter
							leichter@yale
PS  If you are thinking about designing such a system, one nice thing to
design in is the use of a transfer vector to get to the library routines.
If you do this, you can actually run programs that were linked against an
old library using a new library with no changes at all.  VMS does this -
they've actually gotten more sophisticated about it over the years; it used
to be that you would have to re-link when the library grew enough to force
some vectors to move.  This is no longer a problem, because the program start-
up code does some last-minute "relocation" of vector addresses.  The older
PDP-11 OS's didn't, in general, have this, requiring you to re-link with
each new version of a library.  P/OS, on the other hand, is going to such a
scheme...					-- J

ka (04/12/83)

When Stehpen Hemminger talked about DEC putting everything into the kernel
he was probably talking about TOP-10 and TOPS-20.  The issue here seems
to be one of performance rather than functionality anyway, and the compar-
isons of UNIX vs. VMS performance that I have seen didn't consider the
issue important enough to discuss.  With shared libraries, you have tran-
fer vectors which make calling the routines slower, and the operating
system has to set up page table entries for routines which your program
doesn't ever call.  These problems may be enough to offset the performance
advantages of shared libraries.
				Kenneth Almquist

dave@lsuc.UUCP (David Sherman) (04/22/85)

I was fixing some year-old CAI code just now, and spotted
the following, which obviously resulted from a   s/,/+1,/
which I didn't check too carefully:

	printf("\n\nQUESTION %d of %d:\n"+1, sp-s85facts+1, maxquests);

Naturally, the C compiler didn't complain, since it's perfectly
legal C. Lint passes it too; and I'd never noticed that my
output was missing its first newline!

Dave Sherman
-- 
{utzoo pesnta nrcaero utcs hcr}!lsuc!dave
{allegra decvax ihnp4 linus}!utcsri!lsuc!dave

Purtill@MIT-MULTICS.ARPA (Mark Purtill) (08/07/85)

<Fnord>
>[Regarding a space between a function and the paren, as in foo () ;
>ANSI don't say it okay,...
Wrong.  I quote (from the April 30 draft, section C.1):

          Space characters,... - collectively called *white space* - are
    ignored except as they seperate tokens.
          
And of course "printf" and "(" are both tokens (identifier and operator
resp.)

       Mark
^.-.^  Purtill at MIT-MULTICS.ARPA    **Insert favorite disclaimer here**
(("))  2-032 MIT Cambrige MA 02139

mwm@eris.berkeley.edu (07/08/86)

In article <1385@well.UUCP> ewhac@well.UUCP (Leo 'Bols Ewhac' Schwab) writes:
>	It never fails to amaze me just how many people do this:
>		if (foo == NULL) { ... }
>	or:
>		if (foo == 0) { ... }
[instead of...]
>		if (!foo) { ... }

Well, I wrote it the way you think people ought to for 8 years before
changing to coding the test explicitly. I changed because the (!foo)
notation is less readable than the (foo == 0) notation.

Also, if foo is a pointer type, the test (!foo) is wrong. NULL doesn't
have to be zero.

>	The reason the compiler is exhibiting the behavior you describe is
>because you told it to.  You are asking the compiler to compare an unsigned
>byte to the literal value of NULL.

Uh, what he got was:

>>causes var to be first loaded, extended to a word, extended to a long, then
>>compared against 0, EVEN when var is declared as a UBYTE...

What's the second extend doing in there? Anything claiming to be a
reasonable compiler should generate ONE extend. Anything claiming to
be not bad (much less good) should realize what's going on, and
generate the CMP.B instruction. Of course, a good compiler will check
to see if the Z condition code is set correctly already (from a move
to/from var, or the evaluation into var), and just generate the
branch. If it's slightly intelligent, it'll be willing to re-arrange
code to make that pre-condition happen.

>	If you *really* care about optimized code, compile to an assembly
>source file and bang on it yourself.  That's something Lettuce *definitely*
>won't let you do.

Well, I'd rather get the source to the compiler and bang on that so it
generates good code (that way I only have to do it once). But I expect
the compiler to at least generate non-abysmal code.

>>Oh, well... I guess someone will make a good 'C' compiler someday...

DEC has one for VMS. Convex has one for their box (though it may not
be released yet). Anybody know of any others?

	<mike

rgenter@BBN-LABS-B.ARPA (Rick Genter) (07/09/86)

     One point about your posting:

     You mention that the compiler should generate only one extend
instruction.  The MC68000 does not have an instruction to extend from
byte to long - you have to extend byte to word, then extend word to
long.  Of course, it's much better if it just generates TST.B (if
comparing against zero), or, as you pointed out, uses the condition
codes set from the MOV.B instruction which loaded the value in the
first place.
--------
Rick Genter 				BBN Laboratories Inc.
(617) 497-3848				10 Moulton St.  6/512
rgenter@labs-b.bbn.COM  (Internet new)	Cambridge, MA   02238
rgenter@bbn-labs-b.ARPA (Internet old)	linus!rgenter%BBN-LABS-B.ARPA (UUCP)

chris@umcp-cs.UUCP (Chris Torek) (07/09/86)

In article <951@jade.BERKELEY.EDU> mwm@eris.UUCP () writes:
>Also, if foo is a pointer type, the test (!foo) is wrong. NULL doesn't
>have to be zero.

(Old ground again. . . .)

NULL *does* have to be zero, and the test is not wrong.

Any null pointer must compare equal to the integer constant zero.
Any null pointer may contain a non-zero bit pattern.  Do you
understand the difference?  (If so, stop reading now.)

The difference is that the integer constant zero is only a zero
before compilation.  There is no conceptual reason that it cannot
mutate in the process of code generation.  For example, consider
a Vax compiler that uses the bit pattern 0xc0000000 as the null
pointer (0xc0000000 is an illegal address on a Vax).  The compiler
might turn this:

	register char *p = 0;

	...
	if (!p) ...

into this:

	movl	$0xc0000000,r11		# p = 0
	...
	cmpl	$0xc0000000,r11		# if (!p)
	jneq	L70			# branch if p == 0
	...

The same compiler would also generate different code for

	long t, time(); t = time(0);

and

	long t, time(); t = time((long *)0);

The former might generate the following:

	pushl	$0			# 0
	calls	$1,_time
	movl	r0,-4(fp)

and the latter:

	pushl	$0xc0000000		# (long *)0
	calls	$1,_time
	movl	r0,-4(fp)

Now do you see the difference?
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 1516)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@mimsy.umd.edu

guy@sun.uucp (Guy Harris) (07/09/86)

> >	It never fails to amaze me just how many people do this:
> >		if (foo == NULL) { ... }
> >	or:
> >		if (foo == 0) { ... }
> [instead of...]
> >		if (!foo) { ... }
> 
> Well, I wrote it the way you think people ought to for 8 years before
> changing to coding the test explicitly. I changed because the (!foo)
> notation is less readable than the (foo == 0) notation.

Amen.  The three constructs given have exactly the same meaning in C (yes,
wait for the explanation, it's true) and any good compiler will produce
exactly the same code for them.  However, "!foo" is suggestive of testing
for the falsity of a Boolean (yes, I know C doesn't have a formal Boolean
type, but that's the proper way to think of something with values meaning
"true" and "false"), while "foo == NULL" is suggestive of testing whether a
pointer is null or not, and "foo == 0" is suggestive of testing whether an
integral value is zero or not.

> Also, if foo is a pointer type, the test (!foo) is wrong. NULL doesn't
> have to be zero.

Oh, yes it does.  A null pointer need not have a bit pattern consisting of
all zeroes.  However, the value of a null pointer is constructed in
C by converting the integral constant 0 to a pointer type.  In the construct
"foo == 0", the LHS is a pointer, so the compiler knows enough to convert
the "0" on the RHS to a pointer of the same type, hence a null pointer of
the appropriate type.  (No, I won't cite the K&R/ANSI C draft which says
this; seek and ye shall find.)

As such, NULL should be #defined as 0 - NOT "(char *)0", because that
causes the compiler to get annoyed when you compare an "int *" against NULL,
since you're comparing two different kinds of pointers.

> >	The reason the compiler is exhibiting the behavior you describe is
> >because you told it to.  You are asking the compiler to compare an unsigned
> >byte to the literal value of NULL.

Which should always be 0.  As such, in the example given, 

	if (var == NULL) ...

is identical to

	if (var == 0) ...

and should generate the same code; as the author of the response to the
above comment points out, there is NO reason to extend a 1-byte variable
"var" to 4 bytes when doing this.
-- 
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com (or guy@sun.arpa)

pete@valid.UUCP (Pete Zakel) (07/10/86)

> >>causes var to be first loaded, extended to a word, extended to a long, then
> >>compared against 0, EVEN when var is declared as a UBYTE...
> 
> What's the second extend doing in there? Anything claiming to be a
> reasonable compiler should generate ONE extend.

Not if the processer is a 68000 or 68010.  There is NO instruction to extend
byte to long.  Fortunately that was fixed in the 68020, but that doesn't help
unless you have a compiler that understands 68020 AND you know all your
target machines have 68020s.
-- 
-Pete Zakel (..!{hplabs,amd,pyramid,ihnp4}!pesnta!valid!pete)

gwyn@BRL.ARPA (07/11/86)

In agreement with Guy and others, I do not equate readability of C
code with use of the minimum number of tokens.  Readability pertains
to the human mind, perceptions, cognitive psychology, etc. which
makes it hard to reduce C code readability to a simple set of rules
for construction.  However, there are several known good principles
(data abstraction, modularity, good mnemonics, and so forth).  The
key point is that readability depends on conceptual clarity.  This is
why, as Guy points out, it is proper to maintain a conceptual
distinction between Boolean data and pointers, even though C itself
permits blurring of such distinctions.  The use of NULL to mean
"invalid pointer, no data can be accessed via this pointer" is one
important tool for writing intelligible code.  I probably once
thought that it was neat that C (like B, which I never used) folded
together a lot of apparently separate things; fortunately if I did
think that I have since come to my senses.  Simply combining
entities is a far cry from integrating them into a cohesive
concept.  [loose use of terms here so I can be understood by non-
epistemologists]  The only "concept" served by ignoring data types is
that of the machine storage cell (just like B), which is the wrong way
to go, since leverage is obtained by extending one's focus to higher-
level abstractions, not lower.  Only when there is a real need to
be concerned with low-level details (e.g. when writing device
drivers) should one stoop to working at that level of abstraction.

Incidentally, ANSI X3J11 seems to be urging the general adoption of
	(void *)0
as the definition of NULL instead of the
	0
that Guy and I agree is the best definition.  I don't know what
their rationale for that is.  It may be meant to help compensate
for lack of care commonly seen in code such as:
	func( arg1, arg2, NULL, arg4 );
since on lucky machines where all pointers happen to be the same
size, that would work despite the fact that it was coded wrong.
I think that's a bogus reason, if it is indeed the reason.

mykes@3comvax.UUCP (07/11/86)

In article <4907@sun.uucp> guy@sun.uucp (Guy Harris) writes:
>
>Which should always be 0.  As such, in the example given, 
>
>	if (var == NULL) ...
>
>is identical to
>
>	if (var == 0) ...
>
>and should generate the same code; as the author of the response to the
>above comment points out, there is NO reason to extend a 1-byte variable
>"var" to 4 bytes when doing this.
>-- 
>	Guy Harris
>	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
>	guy@sun.com (or guy@sun.arpa)

As author of the original article, I would just like to point out that
the NULL in he case I described was NOT 0L (I removed all the Ls form the
include files - i use +l all the time so I dont need them).  I had hoped
that removing the Ls, this would make the "problem" go away but no luck.
Simple peephole optimization hould catch most of the stuff I see wrong
wit MANX.  I also acknowledge that the +l option was an "afterthought",
having been added maybe days before 3.20A shipped.  I still would rather
stick with their product for the next few years, because they do have the
better compiler, a head start on everyone else (at making a good Amiga
library), excellent support and a bright future.  I will probably pay for
updates forever...

This discussion has brought out a few interesting points: if you consider
the compiler a tool to generate the program you want (which  I do), then
you have to play some tricks and use a style that makes the compiler generate
better code.  On one hand, future compilers (versions of compilers) may not
generate optimal code for this style, but what you get today is closest to
the binary program that you want (i.e. fast, compact, etc.) as doable with
the current version of the compiler.  On the other hand, if you code for
portability, there are less of these tricks (there probably are a few though)
that work across all compilers and all machines.  Matt Dillon's comments 
about longs and shorts and ints are valid and his approach is valid, when
you consider his intentions (purism aside).  For now, I do use:
	if (!pointer) ...
instead of
	if (pointer == NULL) ...
because no matter how much RAM I have, I seem to always want more.  Coding
this way saves me RAM all the time.

ricmtodd@uok.UUCP.UUCP (07/16/86)

/* Written  2:26 pm  Jul  8, 1986 by mwm@eris.berkeley.edu in uok.UUCP:net.lang.c */
>>		if (foo == NULL) { ... }
>>	or:
>>		if (foo == 0) { ... }
>[...]
>>>Oh, well... I guess someone will make a good 'C' compiler someday...
>
>DEC has one for VMS. Convex has one for their box (though it may not
>be released yet). Anybody know of any others?
>	<mike
/* End of text from uok.UUCP:net.lang.c */
Well the Aztec C compiler on my PC does the above-mentioned test in one
instruction (a compare with zero). Note that this isn't the fancy version
with optimization or long pointers -- the code is probably worse for long
pointers.  In the test, foo was just a simple (small mem model) char pointer.
It also seems to generate reasonably good (i.e. not totally atrocious) code
for other constructs; remember this isn't even the OPTIMIZING compiler.
___________________________________________________________________________
Vila: "I am entitled to my own opinion."
Avon:  "Yes, but it's your constant assumption that everyone else is
        also that's so annoying."
___________________________________________________________________________
Richard Todd
USSnail:820 Annie Court,Norman OK 73069
UUCP: {allegra!cbosgd|ihnp4}!okstate!uokvax!uok!ricmtodd