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