|
Softpanorama
(slightly skeptical)
Open Source Software Educational Society |
May the
source be with you,
but remember the KISS principle ;-)
|
Softpanorama C Language Webliography
Dr. Nikolai Bezroukov
Notes:
- This is a Spartan WHYFF (We Help
You For Free) site written by people for whom English
is not a native language.
Some amount of grammar and spelling errors should be
expected.
- The site contain some broken links
as it develops like a living tree...
Please try to use Google, Open directory,
etc. to find a replacement link (see
HOWTO search the WEB for details). We would appreciate
if you can
mail us a correct link.
|
|
|
|
V IDE
V IDE works with GNU g++, Borland C++ 5.5 and Java and runs
on Windows and Linux. It includes a syntax highlighting editor
for C/C++, Java, Perl, Fortran, TeX and HTML. It has a built-in
code beautifier, macro support, ctags support, project manager,
integrated support for the V applications generator and icon
editor, integrated support for the GNU gdb and Sun's jdb (for
Java), etc.
Slashdot Optimizations - Programmer vs. Compiler
Re:Clear Code (Score:5, Insightful)
by Rei (128717) on Friday
February 25, @04:56PM (#11782241)
(http://www.cursor.org/)
|
An important lesson that I wish I had learned when I was younger
;) It is crazy to start optimizing before you know where your
bottlenecks are. Don't guess - run a profiler. It's not hard, and you'll
likely get some big surprises.
Another thing to remember is this: the compiler isn't stupid; don't
pretend that it is. I had senior developers at an earlier job mad at me
because I wasn't creating temporary variables for the limits of my loop
indices (on unprofiled code, nonetheless!). It took actually digging up
an article on the net to show that all modern compilers automatically
dereference any const references (be they arrays, linked lists, const
object functions, etc) before starting the loop.
Another example: function calls. I've heard some people be insistant
that the way to speed up an inner loop is to remove the code from
function calls so that you don't have function call overhead. No! Again,
compilers will do this for you. As compilers were evolving, they added
the "inline" keyword, which does this for you. Eventually, the compilers
got smart enough that they started inlining code on their own when not
specified and not inlining it when coders told it to be inline if it
would be inefficient. Due to coder pressure, at least one compiler that
I read about had an "inlinedamnit" (or something to that effect) keyword
to force inlining when you're positive that you know better than the
compiler ;)
Once again, the compiler isn't stupid. If an optimization seems
"obvious" to you, odds are pretty good that the compiler will take care
of it. Go for the non-obvious optimizations. Can you remove a loop from
a nested set of loops by changing how you're representing your data? Can
you replace a hack that you made with standard library code (which tends
to be optimized like crazy)? Etc. Don't start dereferencing variables,
removing the code from function calls, or things like this. The compiler
will do this for you.
If possible, work with the compiler to help it. Use "restrict". Use
"const". Give it whatever clues you can. |
Write C for C programmers (Score:5, Insightful)
by swillden (191260)
* on
Friday February 25, @03:55PM (#11781306)
|
| With regard to your example, I can't imagine any modern compiler
wouldn't treat the two as equivalent.
However, in your example, I actually prefer "if (!ptr)" to "if (ptr
== NULL)", for two reasons. First the latter is more error-prone,
because you can accidentally end up with "if (ptr = NULL)". One common
solution to avoid that problem is to write "if (NULL == ptr)", but that
just doesn't read well to me. Another is to turn on warnings, and let
your compiler point out code like that -- but that assumes a decent
compiler.
The second, and more important, reason is that to anyone who's been
writing C for a while, the compact representation is actually clearer
because it's an instantly-recognizable idiom. To me, parsing the "ptr ==
NULL" format requires a few microseconds of thought to figure out what
you're doing. "!ptr" requires none. There are a number of common idioms
in C that are strange-looking at first, but soon become just another
part of your programming vocabulary. IMO, if you're writing code in a
given language, you should write it in the style that is most
comfortable to other programmers in that language. I think proper use of
idiomatic expressions *enhances* maintainability. Don't try to write
Pascal in C, or Java in C++, or COBOL in, well, anything, but that's a
separate issue :-)
Oh, and my answer to your more general question about whether or not
you should try to write code that is easy for the compiler... no. Don't
do that. Write code that is clear and readable to programmers and let
the compiler do what it does. If profiling shows that a particular piece
of code is too slow, then figure out how to optimize it, whether by
tailoring the code, dropping down to assembler, or whatever. But not
before. |
Check out the LLVM demo page (Score:5, Interesting)
by sabre (79070) on Friday
February 25, @03:58PM (#11781354)
(http://www.nondot.org/~sabre/)
|
LLVM is an aggressive compiler that is able to do many cool things.
Best yet, it has a demo page here: http://llvm.org/demo [llvm.org], where you can
try two different things and see how they compile.
One of the nice things about this is that the code is printed in a
simple abstract assembly language that is easy to read and understand.
The compiler itself is very cool too btw, check it out.
:) |
If you're not willing to TIME it... (Score:4, Insightful)
by dpbsmith (263124) on
Friday February 25, @04:30PM (#11781872)
(http://world.std.com/~dpbsmith)
|
...then the code isn't important enough to optimize. Plain and
simple.
Never try to optimize anything unless you have measured the speed of the
code before optimizing and have measured it again after optimizing.
Optimized code is almost always harder to understand, contains more
possible code paths, and more likely to contain bugs than the most
straightforward code. It's only worth it if it's really faster...
And you simply cannot tell whether it's faster unless you actually time
it. It's absolutely mindboggling how often a change you are certain will
speed up the code has no effect, or a truly negligible effect, or slows
it down.
This has always been true. In these days of heavily optimized compilers
and complex CPUs that are doing branch prediction and God knows what
all, it is truer than ever. You cannot tell whether code is fast just by
glancing at it. Well, maybe there are processor gurus who can accurately
visualize the exact flow of all the bits through the pipeline, but I'm
certainly not one of them.
A corollary is that since the optimized code is almost always trickier,
harder to understand, and often contains more logic paths than the most
straightforward code, you shouldn't optimize unless you are committed to
spending the time to write a careful unit-test fixture that exercises
everything tricky you've done, and write good comments in the code. |
Premature Optimization (Score:5, Insightful)
by fizban (58094) <fizban@umich.edu> on Friday February 25,
@05:06PM (#11782376)
(http://www.sophicstudios.com/)
|
Premature Optimization is the DEVIL! I repeat, it is the gosh darn
DEVIL! Don't do it. Write clear code so that I don't have to spend days
trying to figure out what you are trying to do.
The biggest mistake I see in my professional (and unprofessional) life
is programmers who try to optimize their code is all sorts of "733+"
ways, trying to "trick" the compiler into removing 1 or 2 lines of
assembly, yet completely disregard that they are using a map instead of
a hash_map, or doing a linear search when they could do a binary search,
or doing the same lookup multiple times, when they could do it just
once. It's just silly, and goes to show that lots of programmers don't
know how to optimize effectively.
Compilers are good. They optimize code well. Don't try to help them out
unless you know your code has a definite bottleneck in a tight loop that
needs hand tuning. Focus on using correct algorithms and designing your
code from a high level to process data efficiently. Write your code in a
clear and easy to read manner, so that you or some other programmer can
easily figure out what's going on a few months down the line when you
need to add fixes or new functionality. These are the ways to build
efficient and maintainable systems, not by writing stuff that you could
enter in an obfuscated code contest. |
valgrind (Score:4, Informative)
by cyco/mico (178541) on
Friday February 25, @05:12PM (#11782431)
|
If in doubt, use valgrind and kcachegrind [sourceforge.net].
One run with callgrind gives you all the information you want:
- How often are functions called (and branches taken)
- Which functions take most of the time
- See the assembler code for each line with a mouse click (no need
to guess anymore)
callgrind/kcachegrind is by far the easiest profiling solution I ever
tried, and it seems answer more or less all of your questions. |
Rules for writing fast code (aka optimization) (Score:4,
Insightful)
by MSBob (307239) on Friday
February 25, @05:56PM (#11782860)
|
First: Avoid doing what you don't have to do. Sounds obvious
but I rarely see code that does the absolute minimum it needs to. Most
of the code I've seen to date seems to precalculate too much stuff, read
too much data from external storage, redraw too much stuff on screen
etc...
Second: Do it later. There are thousands of situations where you
can postpone the actual computations. Imagine writing a Matrix class
with the invert() method. You can actually postpone calculating the
inverse of the matrix until there is a call to access on of the fields
in the matrix. Also you can calculate only the field being accessed. Or
at some sensible threshold you may assume that the user code will read
the entire inverted matrix and you can just calculate the remaining
inverted fields... the options are endless.
Most string class implementations already make good use of this rule by
only copying their buffers only when the "copied" buffer changes.
Third: Apply minimum algorithmic complexity. If you can use a
hashmap instead of a treemap use the hash version it's O(1) vs Olog(n).
Use quicksort for just about any kind of sorting you need to do.
Fourth: Cache your data. Download or buy a good caching class
or use some facilities your language provides (eg. Java SoftReference
class) for basic caching. There are some enormous performance gains that
can be realized with smart caching strategies.
Fifth: Optimize using your language constructs. User the
register keyword, use language idioms that you know compile into faster
code etc... Scratch this rule! If you're applying rules one to
four you can forget about this one and still have fast AND readable
code. |
The never overused example that I have (Score:4, Informative)
by roman_mir (125474) on
Friday February 25, @05:56PM (#11782867)
(http://slashdot.org/
| Last Journal: Monday
December 08, @11:44AM) |
I got this job as a contractor 4 years ago now where the project was
developed by over 30 junior developers and one crazy overpaid lady (hey,
Julia,) who wouldn't let people touch her code so fragile it was (and it
was the main action executor,) she would rather fight you for hours than
make one change in the code (she left 2 months before the project
release.) Now, I have never witnessed such monstrocity of a code base
before - the business rules were redefined about once every 2 weeks dor
1.5 years straight. You can imagine.
So, the client decided not to pay the last million of dollars because
the performance was total shit. On a weblogic cluster of 2 Sun E45s they
could only achieve 12 concurrent transactions per second. So the client
decided they really did not want to pay and asked us to make it at least
200 concurrent transactions per second on the same hardware. If I may
make a wild guess, I would say the client really did not want to pay the
last million, no matter what, so they upped the numbers a bit from what
they needed. But anyway.
Myself and another developer (hi, Paul) spent 1.5 months - removing
unnecessary db calls (the app was incremental, every page would ask you
more questions that needed to be stored, but the app would store all
questions from all pages every time,) cached XML DOM trees instead of
reparsing them on every request, removed most of the session object,
reduced it from 1Mb to about 8Kb, removed some totally unnecessary and
bizarre code (the app still worked,) desynchronized some of the calls
with a message queue etc.
At the end the app was doing 320 and over concurrent transactions per
second. The company got their last million.
The lesson? Build software that is really unoptimized first and then
save everyone's ass by optimizing this piece of shit and earn total
admiration of the management - you are a miracle worker now.
The reality? Don't bother trying to optimize code when the business
requirements are constantly changing, the management has no idea how to
manage an IT dep't, the coders are so nube - there is a scent of
freshness in the air and there is a crazy deadline right in front of
you. Don't optimize, if the performance becomes an issue, optimize then.
|
Highlight UnMatched Brackets - Capture those unmatched brackets while u r still
in insert-mode vim online
Its really irksome when your compiler
complains for any unmatched "{" or "(" or "[".
With this plugin you can highlight all
those unmatched "{" or "(" or "[" as you type.
This helps you to keep track of where
the exact closing bracket should come.
This plugin also warns you of any extra "}" or ")" or "]" you typed.
Customization:
- Specifying Additional Bracket-pairs.
User can specify additional matching pairs in
the global option 'matchpairs', see :help 'matchpairs'
For eg: set mps+=<:> (to include <> pair)
put the above setting in .vimrc file and restart
vim.
- To get rid of highlighting when you quit insert
mode, add this mapping in your vimrc
noremap! <Esc> <Esc>:match NONE<CR>
To test how this plugin works type something like
{
( ) [ ]
( ( ( ) ) )
}
Happy vimming.
ShowFunc.vim - Creates a list of all tags - functions from a window, all windows
or buffers. vim online
This script creates a hyperlink list of all the tags (i.e.
functions, subroutines, classes, macros or procedures) from a single
buffer, all open windows or buffers and displays them in a dynamically
sized cwindow.
Supported File types with Exuberant Ctags version 5.5.4 (and newer): Asm,
Asp, Awk, Beta, C, C++, c#, Cobol, Eiffel, Erlang, Fortran, Java,
Javascript, Lisp, Lua, Make, Pascal, Perl, PHP, Python, PL/SQL, REXX,
Ruby, Scheme, Shell, SLang, SML, SQL, Tcl, Vera, Verilog, Vim, YACC......and
any user defined (i.e. --regex-lang=) types.
Default Key Mappings:
<F1> Run scan and open cwindow.
To reassign add the following to your .vimrc:
map NewKey <Plug>ShowFunc
map! NewKey <Plug>ShowFunc
For example to change the <F1> mapping to <F7>
map <F7> <Plug>ShowFunc
map! <F7> <Plug>ShowFunc
ShowFunc Window commands:
c Close cwindow.
h Display help dialog.
r Refresh.
s Change file sort, results will appear in either alphabetical or
file order. (Default: file order)
t Change scan type, results will be from either the current file,
all open windows or all open buffers. (Default: all open buffers) |
| |
| install details |
Put this file in the vim plugins directory (~/.vim/plugin/) to load
it automatically, or load it with :so ShowFunc.vim.
You need Exuberant CTags installed for this function to work.
Website:
http://ctags.sourceforge.net/
Source:
http://prdownloads.sourceforge.net/ctags/ctags-5.5.4.tar.gz
Redhat/Fedora RPM: http://prdownloads.sourceforge.net/ctags/ctags-5.5.4-1.i386.rpm
Debian: apt-get install exuberant-ctags |
vimcommander - totalcommander-like two-panel tree file explorer for vim vim
online
This is an adaptation of opsplorer (vimscript
#362), intended to be more like the Total Comander (http://www.ghisler.com) file
explorer.
This opens two panels of file explorers on the top half of the vim
screen.
Targets for moving and copying defaults to the other panel, like
totalcmd. TAB switches between panels.
Vimcommander keys are mostly totalcommander's:
F3 - view
F4 - edit
F5 - copy
F6 - move
F7 - create dir
F8 - del
Others: C-U, C-Left/C-Right, C-R, BS, DEL, C-H, etc.
Selection of files/dirs also works: INS, +, -. Then copy/move/del
selected files.
Suggested binding is
noremap <silent> <F11> :cal VimCommanderToggle()<CR>
|
| |
| install details |
Drop vimcommander.vim in ~/.vim/plugin
Put in you .vimrc a map to VimCommanderToggle():
noremap <silent> <F11> :cal VimCommanderToggle()<CR> |
c.vim -
Write C-C++ programs by inserting statements, idioms and comments. vim online
| script type |
| utility |
| |
| description |
** Statement oriented editing of C / C++ programs
** Speed up writing new code considerably.
** Write code und comments with a professional appearance from the
beginning.
** Use code snippets
- insertion of various types of comments (file prologue, function
descriptions, file section headers
keyword comments, date, time, ... )
- insertion of empty control statements (if-else, while, do-while,
switch, ... )
- insertion of various preprocessor directives
- insertion of C-idioms (enum+typedef, loops, complete main, empty
function, file open dialogs, ... )
- insertion of C++ -idioms ( frames for simple classes and template
classes, try-catch blocks,
file open dialogs, output manipulators, ios flags, ... )
- compile / link / run support for one-file projects (without a
makefile)
- personalization of comments (name, email, ... )
- menus can be switched on and off (Tools menu) |
[Jan 2, 2005]
CRefVim -
a C-reference manual especially designed for Vim vim online
The intention of this project is to provide a C-reference manual that can
be viewed with Vim and accessed from within Vim. The C-reference is a
reference, it is NOT a tutorial or a guide on how
to write C-programs. It is a quick reference to the functions and syntax
of the standard C language.
[Jan 2, 2005] C-fold
- Automates folding and unfolding C & C++ comments and code blocks. vim online
Automatically folds all blocks (i.e. { } ) in C and C++ and defines
a function that performs a command e.g. zo on all folds beginning with
text that matches a given pattern.
This allows for the following mappings defined by the plugin:
z[ - Opens all doxygen-style comments
z] - Closes all doxygen-style comments
z{ - Opens all code blocks (i.e. { })
z} - Closes all code blocks
| |
| install details |
Extract the archive from your home directory. This will extract the
following files: .vim/plugins/cfold.vim
.vim/after/syntax/c.vim
Also requires the folding of Doxygen-style comments. This requires
vimscript #5. This can be done easily by adding the 'fold' keyword
to the end of the 'doxygenComment' region in the 'doxygen.vim' syntax
file:
syn region doxygenComment start= ... keepend fold
Additional languages can be supported as appropriate (e.g. Java) by
copying 'c.vim' and renaming to the syntax file for the language (e.g.
java.vim).
Linux Online
- The Linux Tips HOWTO Short Tips
I do a lot of C programming in my spare time, and I've taken
the time to rig vi to be C friendly. Here's my .exrc:
set autoindent
set shiftwidth=4
set backspace=2
set ruler
What does this do? autoindent causes vi to automatically
indent each line following the first one indented, shiftwidth
sets the distance of ^T to 4 spaces, backspace sets the
backspace mode, and ruler makes it display the line number.
Remember, to go to a specific line number, say 20, use:
vi +20 myfile.c
Most hackers already have ctags on their computers, but don't
use it. It can be very handy for editing specific functions.
Suppose you have a function, in one of many source files in a
directory for a program you're writing, and you want to edit
this function for updates. We'll call this function foo(). You
don't where it is in the source file, either. This is where
ctags comes in handy. When run, ctags produces a file named tags
in the current dir, which is a listing of all the functions,
which files they're in and where they are in said files. The
tags file looks like this:
ActiveIconManager iconmgr.c /^void ActiveIconManager(active)$/
AddDefaultBindings add_window.c /^AddDefaultBindings ()$/
AddEndResize resize.c /^AddEndResize(tmp_win)$/
AddFuncButton menus.c /^Bool AddFuncButton (num, cont, mods, func, menu, item)$/
AddFuncKey menus.c /^Bool AddFuncKey (name, cont, mods, func, menu, win_name, action)$/
AddIconManager iconmgr.c /^WList *AddIconManager(tmp_win)$/
AddIconRegion icons.c /^AddIconRegion(geom, grav1, grav2, stepx, stepy)$/
AddStartResize resize.c /^AddStartResize(tmp_win, x, y, w, h)$/
AddToClientsList workmgr.c /^void AddToClientsList (workspace, client)$/
AddToList list.c /^AddToList(list_head, name, ptr)$/
To edit, say AddEndResize() in vim, run:
vim -t AddEndResize
This will bring the appropriate file up in the editor, with the
cursor located at the beginning of the function. |
[Jan 2, 2005]
C-editing-with-VIM-HOWTO. See also Ctags
code browsing framework
3.1. ctags
A Tag is a sort of placeholder. Tags are very useful in understanding and
editing C. Tags are a set of book-marks to each function in a C file. Tags
are very useful in jumping to the definition of a function from where it is
called and then jumping back.
Take the following example.
Figure 6. Tags Example
[tags]
Lets say that you are editing the function foo() and you come across the
function bar(). Now, to see what bar() does, one makes uses of Tags. One can
jump to the definition of bar() and then jump back later. If need be, one
can
jump to another function called within bar() and back.
To use Tags one must first run the program ctags on all the source files.
This creates a file called tags. This file contains pointers to all the
function definitions and is used by VIM to take you to the function
definition.
The actual keystrokes for jumping to and fro are CTRL-] and CTRL-T. By
hitting CTRL-] in foo() at the place where bar() is called, takes the cursor
to the beginning of bar(). One can jump back from bar() to foo() by just
hitting CTRL-T.
ctags are called by
$ ctags
options file(s)
To make a tags file from all the *.c files in the current directory all one
needs to say is
$ ctags *.c
In case of a source tree which contains C files in different sub
directories,
one can call ctags in the root directory of the source tree with the -R
option and a tags file containing Tags to all functions in the source tree
will be created. For Example.
$ ctags -R
*.c
There are many other options to use with ctags. These options are explained
in the man file for ctags.
-----------------------------------------------------------------------------
3.2. marks
Marks are place-holders like Tags. However, marks can be set at any point in
a file and is not limited to only functions, enums etc.. Plus marks have be
set manually by the user.
By setting a mark there is no visible indication of the same. A mark is just
a position in a file which is remembered by VIM. Consider the following code
Figure 7. The marks example
[marks]
Suppose you are editing the line x++; and you want to come back to that line
after editing some other line. You can set a mark on that line with the
keystroke m' and come back to the same line later by hitting ''.
VIM allows you to set more than one mark. These marks are stored in
registers
a-z, A-Z and 1-0. To set a mark and store the same in a register say j, all
one has to hit is mj. To go back to the mark one has to hit 'j.
Multiple marks are really useful in going back and fro within a piece of
code. Taking the same example, one might want one mark at x++; and another
at
y=x; and jump between them or to any other place and then jump back.
Marks can span across files. To use such marks one has to use upper-case
registers i.e. A-Z. Lower-case registers are used only within files and do
not span files. That's to say, if you were to set a mark in a file foo.c in
register "a" and then move to another file and hit 'a, the cursor will not
jump back to the previous location. If you want a mark which will take you
to
a different file then you will need to use an upper-case register. For
example, use mA instead of ma. I'll talk about editing multiple files in a
later section.
-----------------------------------------------------------------------------
3.3. gd keystroke
Consider the following piece of code.
Figure 8. The third example
[gd]
For some reason you've forgotten what y and z are and want to go to their
declaration double quick. One way of doing this is by searching backwards
for
y or z. VIM offers a simpler and quicker solution. The gd keystroke stands
for Goto Declaration. With the cursor on "y" if you hit gd the cursor will
take you to the declaration :- struct Y y;.
A similar keystroke is gD. This takes you to the global declaration of the
variable under the cursor. So if one want to go to the declaration of x,
then
all one needs to do is hit gD and the cursor will move to the declaration of
x.
[Dec 26, 2004]
Writing a C-based Client-Server
Consider for a moment having
the massive power of different computers all
simultaneously trying to compute a problem for you—and
still being legal! With the commonplace
interconnectivity that a network brings, you can do just
that. All you need is a login and a compiler—and a few
system calls.
Network programming extends the ability to solve
computational problems. Nearly all network programs use
sockets to provide connection points between the source
and destination. You can compare sockets with an in/out
box for messages between tasks on different computers.
All you need is a host address and port, and you can
send and receive messages from anywhere on your network.
See also
[Nov 12, 2004] C
Coroutines
co_create, co_call, co_resume, co_delete,
co_exit_to, co_exit - C coroutine management
The coro library implements the low level functionality for
coroutines. For a definition of the term coroutine see The
Art of Computer Programming by Donald E. Knuth. In
short, you may think of coroutines as a very simple cooperative multitasking
environment where the switch from one task to another is done explicitly by
a function call. And, coroutines are fast. Switching from one
coroutine to another takes only a couple of assembler
instructions more than a normal function call.
This document defines an API for the low level handling of coroutines i.e.
creating and deleting coroutines and switching between them. Higher
level functionality (scheduler, etc.) is not covered.
[Oct 28, 2004]
Open source compiler demos speed, compactness with boottime compile trick
A project to build a C compiler small enough to enable C to be used
as a scripting language has released an impressive technology demonstration:
a bootloader that uses the Tiny C Compiler (TCC) to compile a Linux kernel
from source at boot-time in as little as 15 seconds.
TCCBoot is framed on creator Fabrice Bellard's website as a demonstration
application for Bellard's Tiny C Compiler (TCC), described as "a tiny but
complete ISOC99 C compiler which enables you to use C as scripting language."
According to Bellard, TCC is several times faster
than gcc, produces optimized code, and is small enough "(about 100KB
for x86 TCC executable, including C preprocessor, C compiler, assembler and
linker)" to be used on rescue disks, among other features.
TCCBoot is distributed as an ISO image, as well as source code. The ISO image
can be burned onto a CD and used to boot x86 PCs. The CD first loads an
initrd filesystem on which kernel C and assembly code have been stashed in a
gzipped ROMFS. It then reads a TCC config file before compiling a binary
kernel image. the image is then booted, leaving the user in a rudimentary
Linux environment featuring a very basic shell with benchmarking software.
We couldn't resist trying it. The whole boot process took about 45 seconds,
including 33 seconds of compile time, on a 1.6GHz Pentium M.
Project creator Fabrice Bellard writes, "It is sure that there are still many
bugs in the kernel generated by TinyCC/TCCBOOT, but at least it can boot and
launch a shell."
Among Bellard's other accomplishments are a victory in a 1997 "obfuscated C"
programming contest, and the creation of the "most efficient formula to date
to compute the nth binary digits of Pi." Bellard also made significant
contributions to a project to create GPL support for Memory
Technology Devices (MTD) DiskOnChip technology.
More details about TCC, TCCBoot and Bellard's other interesting projects are
available on his
website.
[Sept 10, 2004]
http://www.digitalmars.com/
Digital Mars C and C++ Compilers
for Win32, Win16, DOS32 and DOS. Fastest compile/link times, powerful
optimization technology, Design by Contract, complete library source, HTML
browsable documentation, disassembler, librarian, resource compiler, make,
etc., command line and GUI versions, tutorials, sample code, online updates,
Standard Template Library, and
much more!
[ Mar 24 2004]
Open
source development using C99 Is your C code up to
standard? by
Peter Seebach (developerworks@seebs.plethora.net)
What is C99? Who needs it? Is it available yet? Peter Seebach discusses the 1999
revision of the ISO C standard, with a focus on the availability of new features
on Linux and BSD systems.
... ... ...
There are two parts of the C programming language. These are, confusingly,
called the "language" and the "library." Historically, there was a bundle of
commonly used utility code that everyone tended to reuse; this was eventually
standardized into what's called the Standard C Library. The distinction was
pretty easy to understand at first: If the compiler did it, it was the
language; if it was in the add-on code, it was the library.
With time, however, the distinction has been blurred. For instance, some
compilers will generate calls to an external library for 64-bit arithmetic,
and some library functions might be handled magically by the compiler. For
the purposes of this article, the division follows the terminology of the
standard: features from the "Library" section of the standard are library
features and are discussed in the next section of the article. This section
looks at everything else.
The C99 language introduces a number of new features that are of potential
interest to software developers. Many of these features are similar to
features of the GNU C set of extensions to C; unfortunately, in some cases,
they are not quite compatible.
A few features popularized by C++ have made it in. In particular, //
comments and mixed declarations and code have become standard features of
C99. These have been in GNU C forever and should work on every
platform. In general, though, C and C++ remain separate languages; indeed,
C99 is a little less compatible with C++ than C89 was. As always, trying to
write hybrid code is a bad idea. Good C code will be bad C++ code.
C99 added some support for Unicode characters, both within string literals
and in identifiers. In practice, the system support for this probably isn't
where it needs to be for most users; don't expect source that uses this to be
accessible to other people just yet. In general, the wide character and
unicode support is mostly there in the compiler, but the text processing
tools aren't quite up to par yet.
The new variable-length array (VLA) feature is partially available.
Simple VLAs will work. However, this is a pure coincidence; in fact, GNU C
has its own variable-length array support. As a result, while simple code
using variable-length arrays will work, a lot of code will run into the
differences between the older GNU C support for VLAs and the C99 definition.
Declare arrays whose length is a local variable, but don't try to go much
further.
Compound literals and designated initializers are a wonderful code
maintainability feature. Compare these two code fragments:
Listing 1. Delaying for n microseconds in C89
/* C89 */
{
struct timeval tv = { 0, n };
select(0, 0, 0, 0, &tv);
}
|
Listing 2. Delaying for n microseconds in C99
// C99
select(0, 0, 0, 0, & (struct timeval) { .tv_usec = n });
|
The syntax for a compound literal allows a brace-enclosed series of values
to be used to initialize an automatic object of the appropriate type. The
object is reinitialized each time its declaration is reached, so it's safe
with functions (such as some versions of select) that may modify
the corresponding object. The designated initializer syntax allows you to
initialize members by name, without regard to the order in which they appear
in an object. This is especially useful for large and complicated objects
with only a few members initialized. As with a normal aggregate initializer,
missing values are treated as though they'd been given 0 as an initializer.
Other initialization rules have changed a bit. For instance, you're now
allowed to have a trailing comma after the last member of an enum
declaration, to make it just a bit easier to write code generators.
For years, people have been debating extensions to the C type system, such
as long long. C99 introduces a handful of new integer types. The
most widely used is long long. Another type introduced by the
standards process is intmax_t. Both of these types are available
in gcc. However, the integer promotion rules are not always correct for types
larger than long. It's probably best to use explicit casts.
There are also a lot of types allowing more specific descriptions of
desired qualities. For instance, there are types with names like
int_least8_t, which has at least 8 bits, and int32_t,
which has exactly 32 bits. The standard guarantees access to types of at
least 8, 16, 32, and 64 bits. There is no promise that any exact-width types
will be provided. Don't use such types unless you are really, totally sure
that you can't accept a larger type. Another optional type is the new
intptr_t type, which is an integer large enough to hold a pointer. Not
all systems provide such a type (although all current Linux and BSD
implementations do).
The C preprocessor has a number of new features. It allows empty
arguments, and it supports macros with varying numbers of arguments. There is
a _Pragma operator for macro-generating pragmas, and there's a
__func__ macro, which always contains the name of the current
function. These features are available in current versions of gcc.
C99 added the inline keyword to suggest function inlining.
GNU C also supports this keyword, but with slightly different semantics. If
you're using gcc, you should always use the static keyword on
inline functions if you want the same behavior as C99 would give for the
code. This may be addressed in future revisions; in the meantime, you can use
inline as a compiler hint, but don't depend on the exact
semantics.
C99 introduced a qualifier, restrict, which can give a
compiler optimization hints about pointers. Because there is no requirement
that a compiler do anything with this, it's done in that gcc accepts it. The
degree of optimization done varies. It's safe to use, but don't count on it
making a huge difference yet. On a related note, the new type-aliasing rules
are fully supported in gcc. This mostly means that you must be more careful
about type punning, which is almost always going to invoke undefined
behavior, unless the type you're using to access data of the wrong sort is
unsigned char.
Array declarators as function arguments now have a meaningful difference
from pointer declarators: you can put in type qualifiers. Of particular
interest is the very odd optimizer hint of giving an array declarator the
static type modifier. Given this declaration: int foo(int
a[static 10]);
It is undefined behavior to call foo() with a pointer that
doesn't point to at least 10 objects of type int. This is an
optimizer hint. All you're doing is promising the compiler that the argument
passed to the function will be at least that large; some machines might use
this for loop unrolling. As old hands will be well aware, it's not a new C
standard without an entirely new meaning for the static keyword.
One last feature to mention is flexible array members. There is a common
problem of wanting to declare a structure that is essentially a header
followed by some data bytes. Unfortunately, C89 provided no good way to do
this without giving the structure a pointer to a separately allocated region.
Two common solutions included declaring a member with exactly one byte of
storage, then allocating extra and overrunning the bounds of the array, and
declaring a member with more storage than you could possibly need,
underallocating, and being careful to use only the storage available. Both of
these were problematic for some compilers, so C99 introduced a new syntax for
this:
Listing 3. A structure with a flexible array
struct header {
size_t len;
unsigned char data[];
};
|
This structure has the useful property that if you allocate space for
(sizeof(struct header) + 10) bytes, you can treat data as being
an array of 10 bytes. This new syntax is supported in gcc.
Library features
That's fine for the compiler. What about the standard library? A lot of the
library features added in C99 were based on existing practice, especially
practices found in the BSD and Linux communities. So, many of these features
are preexisting ones already found in the Linux and BSD standard libraries.
Many of these features are simple utility functions; almost all of them could
in principle be done in portable code, but many of them would be exceedingly
difficult.
Some of the most convenient features added in C99 are in the printf
family of functions. First, the v*scanf functions have become
standardized; for every member of the scanf family, there is a
corresponding v*scanf function that takes a va_list
parameter instead of a variable argument list. These functions serve the same
role as the v*printf functions, allowing user-defined functions
that take variable argument lists and end up calling a function from the
printf or scanf family to do the hard work.
Secondly, the 4.4BSD snprintf function family has been
imported. The snprintf function allows you to print safely into
a buffer of fixed size. When told to print no more than n bytes,
snprintf guarantees that it creates a string of length no more
than n-1, with a null terminator at the end of the string.
However, its return code is the number of characters it would have written if
n had been large enough. Thus, you can reliably find out how
much buffer space you
would need to format something completely. This function is available
everywhere, and you should use it just about all the time; a lot of security
holes have been based on buffer overruns in sprintf, and this
can protect against them.
A number of new math features, including complex math features and special
functions designed to help optimizing compilers for specific floating point
chips are in the new standard, but not reliably implemented everywhere. If
you need these functions, it is best to check on the exact platform you're
targeting. The floating point environment functions are not always supported,
and some platforms will not have support for IEEE arithmetic. Don't count on
these new features yet.
The strftime() function has been extended in C99 to provide a
few more commonly desired formatting characters. These new characters appear
to be available on recent Linux and BSD systems; they aren't always widely
available on somewhat older systems, though. Check the documentation before
using new formats.
As noted, most of the internationalization code is not reliably
implemented yet.
Other new library features are typically not universally available; the
math functions are likely to be available in supercomputer compilers, and the
internationalization functions are likely to be available in compilers
developed outside the United States. Compiler vendors implement the features
they have a call for.
... ... ...
Resources
[Aug 12 2003]
Simon hates software
From: Simon Cozens
Date: 12:36 on 12 Aug 2003
Subject: G"C"C
Arthur Bergman:
> it would be fine if it wasn't for the GODAMN ANAL LINKER that AIX uses!
No, it's that gcc is a complete and utter bloody joke. It will compile and
link almost anything. It would probably compile Perl without too much
modification and wouldn't even emit that many warnings. Look! Look at this!
*&(int)f = 1;
Is that C? I don't fucking think so. And look at this:
FILE *
concat_fopen (char *s1, char *s2, char *mode)
{
char str[strlen (s1) + strlen (s2) + 1];
...
}
Yes, that's supposed to be C, not C++, because the things they've done to C++
are almost bloody unspeakable. The words "embrace" and "extend" come to mind.
How about this, for instance:
It is very convenient to have operators which return the "minimum"
or the
"maximum" of two arguments. In GNU C++ (but not in GNU
C),
a <? b -- is the minimum, returning the smaller of the
numeric values a and b;
a >? b -- is the maximum, returning the larger of the
numeric values a and b.
What? What the hell is that about? And you know the worst thing? People
actually use these abortions in real code, because obviously, if it compiles
on Linux with gcc, it'll compile anywhere. That's why you're having problems
linking on AIX - because nobody's even thought about AIX before. We use
autoconf, right, so it must be portable?
Yeah, fucking right. Portable
between GNU OSes, I think you'll find.
Part of the reason Parrot 0.0.1 was so slow getting out of the door was
because of all these stupid idiots writing GCC "C" and not realizing how
completely fucking broken it was.
Of course, number one stupid idiot was myself, but the point remains,
dammit!
[Sep 24. 2003]
Cyclone
Synopsis
Cyclone is a
programming language based on C that is safe, meaning that it rules
out programs that have buffer overflows, dangling pointers, format string
attacks, and so on. High-level, type-safe languages, such as Java, Scheme, or
ML also provide safety, but they don't give the same control over data
representations and memory management that C does (witness the fact that the
run-time systems for these languages are usually written in C.) Furthermore,
porting legacy C code to these languages or interfacing with legacy C
libraries is a difficult and error-prone process. The goal of Cyclone is to
give programmers the same low-level control and performance of C without
sacrificing safety, and to make it easy to port or interface with legacy C
code.
Cyclone
achieves safety while remaining compatible with C by:
-
Enforcing type safety (e.g., a
cast from t1 to t2 is allowed only if it is safe to view a
t1 as a t2)
-
Not changing data
representation or calling conventions
-
Providing region-based, manual
memory management
-
Using a combination of type
information and run-time checks to prevent array-bound violations
-
Wrapping the C standard
library with appropriate run-time checks as necessary (e.g., has a FILE
already been closed)
Cyclone also provides modern
features for convenient programming:
-
Tagged unions
-
ML-style datatypes
-
Parametric polymorphism
-
Pattern matching
-
Exceptions
-
Anonymous structs equivalent
by structure
-
Parameterized typedefs
-
An extensive library for
container types and other common utilities
-
A lexer generator and parser
generator
-
Function-level debugging with
gdb and profiling with gprof
Software
Distribution
The Cyclone
compiler and tools, as well as some benchmark programs, are freely available
for download.
System
Requirements:
-
Cyclone is currently supported
on x86 Linux, and on Windows using Cygwin.
Other platforms may or may not work (our install process tries to detect
your configuration and automatically create the necessary
platform-specific information). We have had some success on BSD,
Irix, and Solaris systems, but your mileage may vary. In particular,
system-dependent behavior is largely untested.
-
You need to extract the files
from the "gzipped tarball" using gunzip and tar; under Windows, you can
also do this with Winzip.
-
You need GNU make, a fairly
recent version of gcc, a bash or ksh shell, and various common utilities (awk,
sed, grep). If you are using Cygwin, all of these come with it. gcc and
the utilities must be in your path. Other make programs and C compilers
will not work.
-
See the file INSTALL in the
distribution for details.
Licensing:
The files in the distribution come from a variety of sources and so come
under a variety of licenses. Please see each file and directory for its
licensing terms.
Papers
Cyclone: A Safe Dialect of C,
Trevor Jim, Greg Morrisett, Dan Grossman, Michael Hicks, James Cheney, and
Yanling Wang. USENIX Annual Technical Conference, pages 275--288,
Monterey, CA, June 2002.
PS
PDF
DVI
Region-based Memory Management in Cyclone,
Dan Grossman, Greg Morrisett, Trevor Jim, Michael Hicks, Yanling Wang, and
James Cheney. ACM Conference on Programming Language Design and
Implementation, pages 282--293, Berlin, Germany, June, 2002.
PS
PDF
DVI
Cornell CS Technical Report TR2001-1856 contains the full definition and
safety proof for the formal language sketched in the paper:
PS
PDF
DVI
Safe
and Flexible Memory Management in Cyclone, Mike Hicks, Greg Morrisett,
Dan Grossman, and Trevor Jim. University of Maryland Technical Report
CS-TR-4514, July 2003.
This paper describes how we have integrated unique pointers, reference
counted objects, and dynamic regions into the language.
More information
Mailing Lists:
We have set up three mailing lists for public use:
Go to
http://lists.cs.cornell.edu/mailman/listinfo/ to subscribe/unsubscribe,
or click the links below to send a message (only list members may submit to
Cyclone-l).
-
Cyclone-l:
public discussion on the Cyclone language
-
Cyclone-announce-l:
announcements of new code distributions
-
Cyclone-bugs-l: for reporting bugs and
providing comments
Credits:
Cyclone is a joint project of AT&T Labs
Research and Greg Morrisett's group at Cornell University; see the
Acknowledgments section of the documentation for details. Here are some links
to personal web pages:
Trevor Jim
Greg Morrisett
Dan Grossman
James Cheney
Mike Hicks
Mathieu Baudet
Matthew Harris
Yanling Wang
C Elements of Style
C Elements of Style was published by M&T books in 1992. An abbrivated
copy of the book is presented here. This book covers only the C language and is
a bit out dated. However it still contains a lot of good advice. Note: The HTML
conversion is not perfect. If you want things nicely formatted, get the PDF
version.
Open Watcom - Portable
Compilers and Tools
Open Watcom is a joint effort between
SciTech Software Inc,
Sybase®, and the
Open Source development community to maintain and enhance the Sybase Watcom
C/C++ and Fortran compiler products. Plans for Open Watcom include porting
the compiler to the Linux and FreeBSD platforms, as well as updating the
compilers to support the latest C and C++ ANSI standards.
The Open Watcom development team
has released version 1.1. You can download the source and binaries
here.
To keep up
with the latest news, participate in the
Open Watcom community
Intel® Compiters
icc The New Intel C Compiler for Linux by Jake
Jenkins
Table of Contents:
Overview
The Good
The Bad
Optimization Switches
Performance vs. GCC
Caveats
Overview
Recently, Intel released its newest compilers
for C/C++ and Fortran77/90 for the Linux platform. Calling them simply C++
and Fortran Compilers 5.0 does a them bit of disservice. These compilers have
been long awaited by developers and it seems the wait has been worth it. Most
serious developers on the Intel architecture know that while these processors
are capable of performance rivaling that of high end workstation CPU's,
making the Intel's peak performance available required the purchase of
commercial libraries or hours of tedious assembly coding. This is due to the
fact that there exists on every Intel processor since the Pentium III, a
special feature known as SSE. SSE is really just a marketing term for the
Pentium III and later's high powered floating point unit. SSE is actually
composed of 8 128bit registers whose sole purpose is to perform floating
point arithmetic. As you will see later in the article, utilizing these extra
registers to their fullest potential can lead to tremendous performance
increases in applications which make heavy use of floating point
calculations.
The Good
Fortunately for those of us who use Linux, the
compilers can be had for free for non-commercial use. For Windows users in an
academic environment, the cost is relatively low as well at under $100 per
user.
Links
Intel
A
good benchmark repository
MCSR
[SLL] A small page about the Intel C-C++ compiler
Take a look at
http://developer.intel.com/software/products/eval/
Note that Linux products have two links:
30-day Free Evaluation Software (Includes Support)
Non-Commercial Unsupported Software
I wrote a small document months ago (when there was only available beta
releases) about installing the compiler, but now its outdated (the
non-commercial version doesn't require you to mess with the temible flex
license manager :-)
Small Tip for Debian Potato users: choose the Red Hat 6.2 on IA-32
Processors, do an apt-get install rpm and you should have no problems
installing it
About the GCC efficiency (or lack of it): All I can say is that much has been
discussed about this issue and the conclusión I most often hear is that GCC
strength is not in the efficency of the generated binary (which is not so bad
in some platforms) but its portability (you can find a gcc _everywhere_).
Haven't tested gcc 3.x yet, but seems that a lot of work has been done in
this subject (anyone here has experiences with the 3.x branch?)
About the fortran compiler: haven't used it, but I'm pretty sure it has full
fortran 95 support (as well as other goodies like openMP).
FTR: The intel compilers (at least the C/C++ one) are based on the KAI
compiler set (take a look at www.kai.com)
Regards
Pedro
Best Practices for Programming in C -- a typical style-related recipes for C
without any new insights, but still the paper has a reasonably balanced approach
to the topic. As always some tips are questionable. I doubt that declaration
of the counter in the loop (for(int i=0; i<100; i++) array[i]=0;) is
a portable construct. A lot of C-compilers will reject this. As one of
Linux Today readers noted "...if you copy things verbatim from others,
*including comments* etc, it would be nicer to name the original authors, ie (?)
Henry Spencer ('Ten commandments for C programmers') and
Robert Pike ('Notes on programming in C'). But this
certainly doesn't ever happen in code written by IBM-employees, does it?"
On a positive side they note usefulness goto in
organizing breaks from inner loops:
- goto statements
goto should be used sparingly. The one
place where they can be usefully employed is to break out of several levels
of switch, for, and while nesting, although the need to
do such a thing may indicate that the inner constructs should be broken out
into a separate function.
for (...) {
while (...) {
...
if (wrong)
goto error;
}
}
...
error:
print a message
|
When a goto is necessary the
accompanying label should be alone on a line and either tabbed one stop to
the left of the code that follows, or set at the beginning of the line. Both
the goto statement and target should be commented to their utility and
purpose.
|
Sean Etc. -
Subject: Pointer qualifer location... ugh ( Jul 28, 2003, 13:39:28 )
|
"The pointer qualifier, '*', should be with the variable name rather
than with the type."
I've never understood this one, and will never agree with it. The pointer
qualifier _is_ part of the type, and _belongs_ with part of the type. The
only problem with this is when you declare multiple variables, in C, you
need a pointer qualifier per identifier; this is just a big bug in C
design, so far as I'm concerned.
If you try writing your software with the * next to the type name, it
really does get a lot cleaner looking. The * doesn't mesh with and
semi-obfuscate the identifier, it doesn't look the same as a pointer
dereference, and the type is much more clearly a pointer.
Good Clean Prototype:
mytype_t* foo (char* string);
Problematic C Declarations:
char* string, not_a_string;
Usual Ugly C Style:
mytype_t *foo (char *string);
char *string, *string2;
string = *other_string;
It does rather irritate me tho that most programmers don't move to the
cleaner style untils years after they start coding (when they make the
sudden realization that it's a better style), because so many teachers and
other coders keep teaching the brain-dead style...
|
|
horne - Subject:
Re: Best practices about C hmm ( Jul 28, 2003, 14:17:31 )
|
for(i=0 to 100)
array[i]=0
I would write the following instead:
for(int i=0; i<100; i++) array[i]=0;
Actually, to ensure the scope of the variable i is limited to the current
construct, the use of curly brackets is recommended.
for (int i; i<100; i++) { array[i]=0; }
This way the variable named 'i' can be reused within the same function
without contamination and as a debugging aid. Nobody writes perfect code
the first time do they?
|
Tech News
- CNET.com Torvalds: What, me worry? (an interview with
Linux Torvalds)
Are there other open-source software development communities you
particularly admire?
If I'd have to pick two, I'd pick KDE
and the
GCC group. I often end up
clashing with the compiler people, because the kernel ends up having rather
strict needs, and I hate how much slower GCC has become over the years. But
there's no question that they're doing some good stuff.
... ... ...
I was talking to (Red Hat Chief Technology Officer and GCC backer)
Michael Tiemann about his view that general-purpose compilers, well written,
will incorporate broad optimizations that ultimately will produce superior
code to compilers with chip-specific optimizations. But now I'm hearing that
Intel's C compiler produces much faster software than GCC. Have you tried the
Intel compilers, now that you can compile Linux with them?
I haven't really ever gotten around to using the Intel compilers, but I like
the fact that there is competition and a baseline to compare against.
And I personally disagree with Michael about general-purpose compilers.
Yes, there should be a lot of shared code, but when it comes down to it, the
thing that matters most for modern CPUs is just generating good tight code,
and the generic optimizations aren't that interesting. But hey, that's just
my personal opinion. I'm not really a huge compiler person; I just don't like
how the high-level optimizations in current GCC versions are slowing things
down without actually giving much of a boost to generated code for C.
Programming in
standard C and C++
Dr.
Dobb's Journal Interview with Alex Stepanov
Let's consider now why C is a
great language. It is commonly believed that C is a hack which was successful
because Unix was written in it. I disagree. Over a long period of time
computer architectures evolved, not because of some clever people figuring
how to evolve architectures---as a matter of fact, clever people were pushing
tagged architectures during that period of time---but because of the demands
of different programmers to solve real problems. Computers that were able to
deal just with numbers evolved into computers with byte-addressable memory,
flat address spaces, and pointers. This was a natural evolution reflecting
the growing set of problems that people were solving. C, reflecting the
genius of Dennis Ritchie, provided a minimal model of the computer that had
evolved over 30 years. C was not a quick hack. As computers
evolved to handle all kinds of problems, C, being the minimal model of such a
computer, became a very powerful language to solve all kinds of problems in
different domains very effectively. This is the secret of C's
portability: it is the best representation of an abstract computer that we
have. Of course, the abstraction is done over the set of real computers, not
some imaginary computational devices. Moreover, people could understand the
machine model behind C. It is much easier for an average engineer to
understand the machine model behind C than the machine model behind Ada or
even Scheme. C succeeded because it was doing the right thing, not
because of AT&T promoting it or Unix being written with it.
[May. 12, 2003]
ONLamp.com What I Hate About Your Programming Language What I Hate About C
- It's often used inappropriately. Unless I'm writing a kernel, a device
driver, a virtual machine, or an interface to a C or C++ library, writing in
C is a probably premature optimization.
- Thirty years of growth and patches to the standard library have produced
many, many similar functions with similar, terse names. Quick, Unix coders,
what are the differences between
execl, execlp,
execle, execv, and execvp? If you're
not using these every day, you'll be hitting man 3 exec whenever
you see them.
Unix.se -
Interview - Dennis Ritchie
C is declining somewhat in usage
compared to C++, and maybe Java, but perhaps even more compared to
higher-level scripting languages. It's still fairly strong for the basic
system-type things.
Do you agree with Rob
Pike's thoughts on the (ir)relevance of systems research?
(http://cm.bell-labs.com/who/rob/utah2000.ps)
Dennis Ritchie: Pretty much, although Rob was stating his case in a
deliberately provocative way. It's true that compared with the scene when
Unix started, today the ecological niches are fairly full, and fresh new OS
ideas are harder to come by, or at least to propagate.
Any thoughts about the
GNU project? How did you first learn about it?
Dennis Ritchie: I can't remember when I first learned about it, but a
long time ago. The True-GNU philosophy is more extreme than I care for, but
it certainly laid a foundation for the current scene, as well as providing
real software. The interesting thing is the way that free-software ideas have
begun to influence major existing commercial players. At the same time, much
of it seems to have to do with recreating things we or others had already
done; it seems rather derivative intellectually; is there a dearth of really
new ideas? But still, it's a great satisfaction that so much of it has built
on top of a basis we helped to establish.
New:
Top:
Other:
Bookshelf
See also
TutorialSearch Two good books on C are available online. So do not waste
time browsing the WEB searching for tutorials -- use
www.informit.com and save time.
Believe me it would be difficult to find something even close to the quality of
a published book on the WEB, unless this is a WEB variant of a published book
;-). All reference below are supplementary. You can also try to buy a
decent book , actually you can do this for
approximately the cost of your monthly WEB access ;-). As for sites with
similar collections of links also
LEARN CC++ TODAY - a list
of C/C++ tutorials by Vinit Carpenter
(old, last updated in 1996), and CSS
Program Web Library; C programming.
These notes are part of the UW Experimental College
course on Introductory C Programming. They are based on notes prepared
(beginning in Spring, 1995) to supplement the book The C Programming
Language, by Brian Kernighan and Dennis Ritchie, or K&R as the book and
its authors are affectionately known. (The second edition was published in
1988 by Prentice-Hall, ISBN 0-13-110362-8.) These notes are now (as of
Winter, 1995-6) intended to be stand-alone, although the sections are
still cross-referenced to those of K&R, for the reader who wants to pursue
a more in-depth exposition.