I was looking at one of the first compilers of c, written by dmr himself. It was written in the early 1970's, so obviously the syntax is very different. In this file, what does ossiz mean?
ossiz 250;
By the way, is there any helpful article or manual on this sort of thing (older c syntax)?
Just like in B, it's a global variable definition with initialization. In modern C it would be:
int ossiz = 250;
Also: https://github.com/mortdeus/legacy-cc/blob/2b4aaa34d3229616c65114bb1c9d6efdc2a6898e/last1120c/c10.c#L462
Related
I was playing around with an online PDP11 emulator (link) and was looking at the programming section of its FAQ.
It says this about programming in C on the emulator:
You need to write pre-K&R C which is quite a bit different from modern C
I believe this is referring to the version of C in use before the publication of The C Programming Language. I have tried to understand this version by reading the sparse C files I can find in the emulator's filesystem, but even simple things like declaring argc and argv have eluded me. I also can't find anything about it online.
Is there any documentation, written at the time or after the fact, on "pre-K&R" C?
For this sort of question, my go-to source is the archived web page of the late Dennis Ritchie:
https://www.bell-labs.com/usr/dmr/www/
From there it's one click to this early reference manual for C, written by Ritchie himself:
https://www.bell-labs.com/usr/dmr/www/cman.pdf
This is indeed "pre-K&R C", featuring anachronisms such as =+ instead of +=.
This is the same reference manual that appeared, in updated form, as Appendix A in the K&R book.
On the same page are links to several other versions of that reference manual, as well as notes about and even the source code of two early versions of Ritchie's compiler. These are pretty fun to look at, although as the page notes, "You won't be able to compile them with today's compilers".
There's a whole Stack Exchange site dedicated to questions like these: https://retrocomputing.stackexchange.com/.
Steve Summit answered about where to get the documentation. So this post is intended to summarize noticeable differences from modern C
Types for function arguments were quite different. They were not specified within the parenthesis
void foo(a, b)
int a;
float b;
{
// Function body
}
No row comments, like //. Only /* */
No logical or and and. The operators && and || did not exist. The bitwise operators & and | were used instead.
=- meant the same as -=, which lead to ambiguity with for instance x=-1.
There was no void pointers. char pointers were used instead.
One could not declare variables in the for header, so one had to write:
int i=0;
for(i=0; i<N; ++i)
= were only used for assignments and not initialization. Those were done like this: int x 3;
Implicit int. This is still valid in modern C. The difference is that no (sane) programmer is using it anymore. This:
foo 3;
is actually equivalent to
int foo = 3;
There was no const qualifier
So I'm completely new to programming. I currently study computer science and have just read the first 200 pages of my programming book, but there's one thing I cannot seem to see the difference between and which havn't been clearly specified in the book and that's reserved words vs. standard identifiers - how can I see from code if it's one or the other.
I know the reserved words are some that cannot be changed, while the standard indentifiers can (though not recommended according to my book). The problem is while my book says reserved words are always in pure lowercase like,
(int, void, double, return)
it kinda seems to be the very same for standard indentifier like,
(printf, scanf)
so how do I know when it is what, or do I have to learn all the reserved words from the ANSI C, which is the current language we are trying to learn, (or whatever future language I might work with) to know when it is when?
First off, you'll have to learn the rules for each language you learn as it is one of the areas that varies between languages. There's no universal rule about what's what.
Second, in C, you need to know the list of keywords; that seems to be what you're referring to as 'reserved words'. Those are important; they're immutable; they can't be abused because the compiler won't let you. You can't use int as a variable name; it is always a type.
Third, the C preprocessor can be abused to hijack anything; if you compile with #define double int in effect, you get what you deserve, but there's nothing much to stop you doing that.
Fourth, the only predefined variable name is __func__, the name of the current function.
Fifth, names such as printf() are defined by the standard library, but the standard library has to be implemented by someone using a C compiler; ask the maintainers of the GNU C library. For a discussion of many of the ideas behind the treaty between the standard and the compiler writers, and between the compiler writers and the programmers using a compiler, see the excellent book The Standard C Library by P J Plauger from 1992. Yes, it is old and the modern standard C library is somewhat bigger than the one from C90, but the background information is still valid and very helpful.
Reserved words are part of the language's syntax. C without int is not C, but something else. They are built into the language and are not and cannot be defined anywhere in terms of this particular language.
For example, if is a reserved keyword. You can't redefine it and even if you could, how would you do this in terms of the C language? You could do that in assembly, though.
The standard library functions you're talking about are ordinary functions that have been included into the standard library, nothing more. They are defined in terms of the language's syntax. Also, you can redefine these functions, although it's not advised to do so as this may lead to all sorts of bugs and unexpected behavior. Yet it's perfectly valid to write:
int puts(const char *msg) {
printf("This has been monkey-patched!\n");
return -1;
}
You'd get a warning that'd complain about the redefinition of a standard library function, but this code is valid anyway.
Now, imagine reimplementing return:
unknown_type return(unknown_type stuff) {
// what to do here???
}
I generated a hash function with gperf couple of days ago. What I saw for the hash function was alien to me. It was something like this (I don't remember the exact syntax) :
unsigned int
hash(str, size)
register char* str;
register unsigned int size;
{
//Definition
}
Now, when I tried to compile with a C++ compiler (g++) it threw errors at me for not having str and size declared. But this compiled on the C compiler (gcc). So, questions:
I thought C++ was a superset of C. If its so, this should compile with a C++ compiler as well right?
How does the C compiler understand the definition? str and size are undeclared when they first appear.
What is the purpose of declaring str and size after function signature but before function body rather than following the normal approach of doing it in either of the two places?
How do I get this function to compile on g++ so I can use it in my C++ code? Or should I try generating C++ code from gperf? Is that possible?
1. C++ is not a superset, although this is not standard C either.
2/3. This is a K&R function declaration. See What are the major differences between ANSI C and K&R C?
.
4. gperf does in fact have an option, -L, to specify the language. You can just use -L C++ to use C++.
The Old C syntax for the declaration of a function's formal arguments is still supported by some compilers.
For example
int func (x)
int x
{
}
is old style (K&R style) syntax for defining a function.
I thought C++ was a superset of C. If its so, this should compile with a C++ compiler as well right?
Nopes! C++ is not a superset of C. This style(syntax) of function declaration/definition was once a part of C but has never been a part of C++. So it shouldn't compile with a C++ compiler.
This appears to be "old-school" C code. Declaring the types of the parameters outside of the parentheses but before the open curl-brace of the code block is a relic of the early days of C programming (I'm not sure why but I guess it has something to do with variable management on the stack and/or compiler design).
To answer your questions:
Calling C++ a "superset" of C is somewhat a misnomer. While they share basic syntax features, and you can even make all sorts of C library calls from C++, they have striking differences with respect to type safety, warnings vs. errors (C is more permissible), and compiler/preprocessor options.
Most contemporary C compilers understand legacy code (such as this appears to be). The C compiler holds the function parameter names sort of like "placeholders" until their type can be declared immediately following the function header name.
No real "purpose" other than again, this appears to be ancient code, and the style back in the day was like this. The "normal" approach is IMO the better, more intuitive way.
My suggestion:
unsigned int hash(register char *str, register unsigned int size)
{
// Definition
}
A word of advice: Consider abandoning the register keyword - this was used in old C programs as a way of specifying that the variable would be stored in a memory register (for speed/efficiency), but nowadays compilers are better at optimizing away this need. I believe that modern compilers ignore it. Also, you cannot use the & (address of) operator in C/C++ on a register variable.
I came across this code in Stephen G Kochan's book, Programming in c. Is this possible?
float absolute_value(x)
float x;
{
-----
-----
}
So, as you can see, the argument x is declared after it's used it the method arguments. This throws an obvious compilation error in G++.
So, which C compiler supports this?
That's the old style K&R format. It's not actually declaring the argument x, rather defining its type. By default, things were int unless otherwise specified.
Back when C was a much simpler language, not that far removed from my beloved BCPL, this was how you gave function arguments their types. None of these prototype stuff that you young whippersnappers take for granted.
Oh yeah, and get off my lawn :-)
This is the original way of declaring the types of function parameters in C. Any properly functioning C compiler is required to accept it. It is not allowed in C++, however, so every properly functioning C++ compiler must reject it (though in both cases, note that some specific combination of compiler flags might be required to achieve proper function). Once upon a time, C compilers only accepted this style, and would reject code like: float absolute_value(float x) {}. This was added (along with function prototypes) while C was being standardized.
Here is the question, How did C (K&R C) look like? The question is about the first ten or twenty years of C's life?
I know, well I heard them from a prof in my uni, that C didn't have the standard libraries that we get with ANSI C today. They used to write IO routines in wrapped assembly! The second thing is that K&R book, is one the best books ever for a programmer to read, This is what my prof told us :)
I would like to know more about good ol' C. For example, what major difference you know about it compared to ANSI C, or how did C change programmers mind about programming?
Just for record, I am asking this question after reading mainly these two papers:
Evolving a language in and for the real world: C++ 1991-2006
A History of C++: 1979-1991
They are about C++, I know! thats why I wanna know more about C, because these two papers are about how C++ was born out of C. I am now asking about how it looked before that. Thanks Lazarus for pointing out to 1st edition of K&R, but I am still keen to know more about C from SO gurus ;)
Well, for a start, there was none of that function prototype rubbish. main() was declared thus:
/* int */ main(c,v)
int c;
char *v[];
{
/* Do something here. */
}
And there was none of that fancy double-slash comments either. Nor enumerations. Real men used #define.
Aah, brings a tear to my eyes, remembering the good old days :-)
Have a look at the 'home page' for the K&R book at Bell Labs, in particular the heading "The history of the language is traced in ``The Development of the C Language'', from HOPL II, 1993"
Speaking from personal experience, my first two C compilers/dev environments were DeSmet C (16-bit MS-DOS command line) and Lattice C (also 16-bit MS-DOS command line). DeSmet C came with its own text editor (see.exe) and libraries -- non-standard functions like scr_rowcol() positioned the cursor. Even then, however, there were certain functions that were standard, such as printf(), fopen() fread(), fwrite() and fclose().
One of the interesting peculiarities of the time was that you had a choice between four basic memory models -- S, P, D and L. Other variations came and went over the years, but these were the most significant. S was the "small" model, 16-bit addressing for both code and data, limiting you to 64K for each. L used 24-bit addressing, which was a 16-bit segment register and a 16-bit offset register to compute addresses, limiting you to 1024K of address space. Of course, in a 16-bit DOS world, you were confined to a physical limitation of 640K. P and D were compromises between the two modes, where P allowed for 24-bit (640K) code and 64K data, and D allowed for 64K code and 640K data addressing.
Wikipedia has some information on this topic.
Here is one example of the code that changed with ANSI C for the better:
double GetSomeInfo(x)
int x;
{
return (double)x / 2.0;
}
int PerformFabulousTrick(x, y, z)
int x, int y;
double z;
{
/* here we go */
z = GetSomeInfo(x, y); /* argument matching? what's that? */
return (int)z;
}
I first started working with C on VAX/VMS in 1986. Here are the differences I remember:
No prototypes -- function definitions and delcarations were written as
int main() /* no void to specify empty parameter list */
{
void foo(); /* no parameter list in declaration */
...
}
...
void foo(x,y)
int x;
double y;
{
...
}
No generic (void) pointer type; all of the *alloc() functions returned char * instead (which is part of why some people still cast the return value of malloc(); with pre-ANSI compilers, you had to);
Variadic functions were handled differently; there was no requirement for any fixed arguments, and the header file was named differently (varargs.h instead of stdarg.h);
A lot of stuff has been added to math.h over the years, especially in the C99 standard; '80s-vintage C was not the greatest tool for numerical work;
The libraries weren't standardized; almost all implementations had a version of stdio, math, string, ctype, etc., but the contents were not necessarily the same across implementations.
Look at the code for the Version 6 Unix kernel - that was what C looked like!
See Lion's Commentary on Unix 6th Edition (Amazon).
Also, it would be easier if you told us your age - your profile says you're 22, so you're asking about code prior to 1987.
Also consider: The Unix Programming Environment from 1984.
While for obvious reasons the core language came before the library, if you get hold of a first edition copy of K & R published in 1978 you will find the library very familiar. Also C was originally used for Unix development, and the library hooked into the I/O services of the OS. So I think your prof's assertion is probably apocryphal.
The most obvious difference is the way functions were defined:
VOID* copy( dest, src, len )
VOID* dest ;
VOID* src ;
int len ;
{
...
}
instead of:
void* copy( void* dest, void* src, int len )
{
...
}
for example. Note the use of VOID; K&R C did not have a void type, and typically VOID was a macro defined as int*. Needless to say, to allow this to work, the type checking in early compilers was permissive. From a practical point of view, the ability of C to validate code was poor (largely through lack of function prototypes and weak type checking), and hence the popularity of tools such a lint.
In 1978 the definition of the language was the K&R book. In 1989 it was standardised by ANSI and later by ISO, the 2nd edition is no longer regarded as the language definition, and was based on ANSI C. It is still the best book on C IMO, and a good programming book in general.
There is a brief description on Wikipedia which may help. Your best bet is to get a first edition copy of K&R, however, I would not use it to learn C, get a 2nd ed. for that.
I started using C in the early 1980's. The key difference I've seen between now and then was that early C did not have function prototypes, as someone noted. The earliest C I ever used had pretty much the same standard library as today. If there was a time when C didn't have printf or fwrite, that was before even my time! I learned C from the original K&R book. It is indeed a classic, and proof that technically sophisticated people can also be excellent writers. I'm sure you can find it on Amazon.
You might glance at the obfuscated C contest entries from the time period you are looking for.
16 bit integers were quite common in the ol' days.