C Programming Language
This page is dedicated to the ANSI C Programming language. We've assembled links and copies of interesting or educational articles on C programming, look at Windows Win32 programming, and consider why we still prefer ANSI C to C++.
Contents
Recommended reading
- Secure Coding in C and C++ by Robert C. Seacord. Look Inside
- The Standard C Library by P.J. Plauger
- C Interfaces and Implementations by David R. Hanson
- The C Programming Language, 2nd Edition by Brian W. Kernighan and Dennis M. Ritchie. Look Inside
Affiliate disclosure: we get a small commission for purchases made through the above links
- What's new?
- Why we prefer C to C++
- Recommended books on the C Programming Language
- Secure Programming in C
- Win32 Programming using C
- Books on Win32 Programming
- Recommended sites on Win32 Programming
- Avoiding buffer overflows - strlcpy and strlcat
- More on buffer overflows - MSVC's _snprintf
- Our safe versions of strncpy and strncat
- Some cute programs in C including the first 800 digits of pi in 160 characters of code, Diffie-Hellman in 10 lines of C, and a tricky one-liner by David Korn.
- Free C compilers and utilities to download
- Interesting articles and links on C Programming
- Quotes from Writing Unmaintainable Code: "Testing is for cowards", "Never, Ever Do Any Performance Testing" and "Never Validate".
- Comments
What's new?
- 1 June 2016. Update to An all-singing, all-dancing C function to create a temporary file.
- 23 December 2015. Updated our Getting rid of that MSVC++ warning C4996: 'may be unsafe/disable deprecation' page to show how you can avoid that annoying warning message and always default to an empty project each time, instead of that precompiled stdafx rubbish. For real C programmers only.
- December 2013. Update to Using flex and bison in MSVC++ .
- October 2013. Our safe versions of strncpy and strncat.
- November 2011. Avoiding buffer overflows - strlcpy and strlcat.
Why we prefer C to C++
- C is simpler and more compact
- The reference book is thinner
- It's usually easier to work out what the program's doing
- It generates smaller executables (much smaller in the case of MFC)
- It's more portable
- With good structure and coding practices, it's often just as "safe" as OOP
- Programs written 20 years ago are still just as valid today as they were then
- We're old luddites who hate learning new tricks
We note that many C++ programs we come across are really just plain old C with the
printf
replaced by cout<<
.
Where C++ is better
- C++ gives more opportunities for obfuscation
- People know that C++ is a programming language. But "C", well, try doing a web search on that.
Grudgingly, we have to admit that there are areas where C++ may be better. For example, using the overload operators would make something like a large digit arithmetic package much simpler to use. Having constructors and destructors means you can manage memory allocation and deletion more safely. And, er..., yes, we're sure there really are more reasons.
"C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do, it blows away your whole leg."
- Bjarne Stroustrup
"If C gives you enough rope to hang yourself, then C++ gives you enough rope to bind and gag your neighborhood, rig the sails on a small ship, and still have enough rope to hang yourself from the yardarm"
- Anonymous quote from the The UNIX-HATERS Handbook
A interview apparently with Bjarne Stroustrup from 1998: The truth about 'C++' revealed. This extract was originally suppressed 'for the good of the industry'. Did he really say such things as
I thought 'I wonder what would happen, if there were a language so complicated, so difficult to learn, that nobody would ever be able to swamp the market with programmers?' ... all the ingredients for what I wanted. A really ridiculously complex syntax, obscure functions, and pseudo-OO structure... I've put in enough pitfalls to make sure that only the most trivial projects will work first time.
Note how the original proposals for, say, the cryptographic algorithms AES and the NIST SHA-3 competition are still written in C. Besides, C++ isn't even a real OO programming language anyway.
A more detailed criticism is given at Defective C++.
D: If you want better garbage collection, look instead at the D Programming Language.
Recommended books on the C Programming Language
- The C Programming Language, 2nd Edition by Brian W. Kernighan and Dennis M. Ritchie. Still reads well. I still have and use the first edition dated 1978.
- The C Puzzle Book by Alan R. Feuer. The best ever book on understanding the quirks of C and how to use pointers properly.
- The Practice of Programming by Brian W. Kernighan and Rob Pike. An update of an earlier book written in the 1970s. Timeless advice, not just on C but on Java, C++ and Perl.
- The Standard C Library by PJ Plauger. Literally how it all works with full source code and tests.
If you read and understand the first three of these books, you will be an expert in C, I promise you. Plauger is a useful reference book that makes up the set. And better, all four of them will span no more than 4 inches on your book shelf. You might try The C Answer Book: Solutions to the Exercises in 'The C Programming Language' by Clovis L. Tondo and Scott E. Gimpel.
For more advanced users, try C Interfaces and Implementations: Techniques for Creating Reusable Software by David R. Hanson. The source code and sample pages are available here.
Secure Programming in C
For a good introduction see Secure Coding in C and C++ by Robert C. Seacord. This not only explains clearly what the problems are but provides useful links <http://www.cert.org/books/secure-coding/>.
For a more detailed work with lots of instantly useable examples for both Windows and Unix, try Secure Programming Cookbook for C and C++ by John Viega and Matt Messier.
The GIAC Secure Software Programmer (GSSP) Certification Exam was developed in a joint effort involving the SANS Institute, CERT/CC, and several US government agencies. David Ireland was one of the first seven programmers to receive the GSSP-C qualification in the C programming language, see SANS Institute press release.
Win32 Programming using C
A personal story: I struggled for months to learn Windows programming with my (then) new copy of MSVC++5. I bought lots of books and followed their examples, but just didn't seem to get it.
Once you'd set up a simple example, such as "Hello World", it was very difficult to make simple changes to create a more complicated example. The object-oriented C++ syntax combined with MFC's attempts to hide the messy bits of Win32 programming made it difficult to trace and understand. Because you'd used wizards to set it all up, you didn't understand why things did what they did. The books all relied on pages and pages of screen dumps of the setup screens to explain what had to be done in a cookbook "follow the recipe" manner. So even if you remembered it perfectly, once you changed environment, you'd be lost again.
The one thing that made it all come together was finding the GENERIC.C example in the Windows SDK (currently at generic.c, unless they've moved it again - thanks, John). Here was a simple example of relatively few lines that worked. What's more, it didn't get complicated by introducing the extra complexities of C++. Better still, you didn't need to use macros to set things up and so could actually alter what you wanted to do without starting again.
The upshot is that the hardest bit about Win32 programming is the first bit. If you understand the basics of the WinMain loop, WM_CREATE, WM_PAINT, and the use of callback functions, you are a long way there. There is a standard wrapper about 30 lines long that you can use. Everything else generally comes down to finding the right Windows functions to use to do what you want (ironically, the best place to find this information and how to use it is often on a VB site!).
Personally I find that programming in plain Win32 C makes for smaller and neater programs. From what we can see, nothing fundamental has changed between Win95 and W8 from a standard programmer's point of view. Sure, there are more features and more complexity if you go looking, but the basics are unchanged. A text book on W95 is still quite valid for writing programs that will run on W8. It might get a bit messier when you want to start using WinSock or doing fancy graphics programming, but not that much. They've even made the transition from Win32 to Win64 really easy.
Free software: See our free Wclock world time display program written using straightforward ANSI C and so-called "SDK-C", i.e. the C code used in the Windows SDK. The source code is available.
Recommended books on Win32 Programming in C
- Programming Windows by Charles Petzold, 5th ed, Microsoft Press, 1999.
- Windows NT Win32 API SuperBible by Richard J Simon. There are variants of this book for all varieties of Windows from W95 to 2000 and beyond, but they are essentially all the same.
- Windows Graphics Programming by Feng Yuan, Prentice Hall, 2000. The ultimate guide to Win32 GDI and DirectDraw. OK, he uses C++ and MFC, but it's not hard to extract the important bits. Very comprehensive coverage.
Recommended sites on Win32 Programming in C
We have found these sites to be particularly helpful for learning and understanding Win32 programming using the C programming language.- theForger's Win32 API Tutorial: Brook Miles's tutorial attempts to get you started developing with the Win32 API as quickly and clearly as possible. You can download the entire site with source code, or get a PDF version. Includes a Spanish translation.
- Catch22 Win32 Tutorials: a collection of programming tutorials for Windows by James Brown. See especially his Win32 Tips and Tricks.
- Al's Programming Resource for Win32: set of tutorials on Win32 programming by Alan Baylis. Uses the evil Flash but he does provide all the example source code separately.
- EFNet #Winprog: with the primary focus on C/C++ Programming with the Win32 API or MFC.
- masmforum website: Tools, toys, tutes and files for assembler programmers. Check the Links to assembler topics.
Avoiding buffer overflows - strlcpy and strlcat
It is not wise to use the ANSI standard strcpy
and strcat
functions because of the possibility
of unchecked buffer overflows. The strncpy
function has the
problem that it may not properly terminate the string.
The safer ISO/IEC TR 24731 functions strncpy_s()
and strncat_s()
are not so convenient
and are missing in many compilers.
The following strlcpy
and strlcat
functions are simple
implementations that overcome the problems of their original ANSI ancestors.
#include <string.h> size_t strlcpy(char *d, const char *s, size_t bufsize) { size_t len; size_t ret; if (!d || !s || (int)bufsize <= 0) return 0; len = strlen(s); ret = len; if (len >= bufsize) len = bufsize-1; memcpy(d, s, len); d[len] = 0; return ret; } size_t strlcat(char *d, const char *s, size_t bufsize) { size_t len1; size_t len2; size_t ret; if (!d || !s || (int)bufsize <= 0) return 0; len1 = strlen(d); len2 = strlen(s); ret = len1 + len2; if (len1+len2 >= bufsize) len2 = bufsize - (len1 + 1); if (len2 > 0) { memcpy(d+len1, s, len2); d[len1+len2] = 0; } return ret; }
These functions are guaranteed not to cause a buffer overflow and the result is always null-terminated.
If there is not enough room in the destination buffer, then it truncates the output. The return value
is the number of bytes it should have copied, so you have a roundabout way of checking.
The value of bufsize
is the actual allocated size of the buffer,
so you can use sizeof(buf)
(providing buf
is not a pointer).
For more on the background to these functions see strlcpy and strlcat - consistent, safe, string copy and concatenation by Todd C. Miller and Theo de Raadt.
The above solutions do have the disadvantage that they always do at least a double pass along the strings, and so are only half as efficient as the originals which don't. This is really only a problem for very long strings.
We've never noticed any performance issue in practice with this code.
It's far, far more important
that we know we can use these functions in our applications safe in the knowledge that we will not
get a buffer overflow problem,
and that we won't be getting people writing in to say they got a run-time error with our program
and can they have their money back.
If it's really important that there is no truncation,
then do your own pre-condition checks and call the raw <string.h>
function.
Avoiding buffer overflows, more
To avoid the danger that you might pass too large a buffer length, try using this macro
#include <limits.h> #define ISTOOBIG(n) ((n)>(LONG_MAX>>1)) if (ISTOOBIG(bufsize)) return ERROR;
One of the dangers of using the unsigned size_t
typedef is when you accidentally cast a negative int
and end up unintentionally passing a very large unsigned integer as your buffer length.
int len;
//...
len = -1;
//...
strlcpy(d, s, (size_t)len); // cast as size_t to avoid annoying compiler warning
This actually passes a very large bufsize
argument which is not detected by if (bufsize <= 0)
.
Alternatively, you could do this
if ((int)bufsize <= 0) return ERROR;
Avoiding buffer overflows, continued
We use this modified version of strlen
when stupid programmers (i.e. ourselves) might pass a null string and cause a run-time error
size_t strllen(const char *string) { if (!string) return 0; return strlen(string); }
Windows programmers might refer to Microsoft's Using the Strsafe.h Functions. [As with all Microsoft's pages, it may have moved again.] We are tempted to agree with one commentator who said he'd rather learn Java than use the awfully-named functions in this library.
Users of MSVC 2005/8/10+ will no doubt be aware of the strcpy_s
family of functions and the annoying message
warning C4996: '_strcpy': This function or variable may be unsafe. Consider using _strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS.
If you are annoyed by this, see Getting rid of that MSVC++ warning C4996: 'may be unsafe/disable deprecation'.
And, yes, there is a IEC draft on the *_s
functions
TC1/SC22/WG14-C
Specification for Secure C Library Functions dating back to 2004.
This was apparently initiated after Microsoft implemented their _s
functions, but
the committee then recommended something different. Anyway, they won't work if you port to GNU.
A shame, because they really are good practice, even if they have some silly parameters.
More on buffer overflows - MSVC's _snprintf function
The functionssprintf
and Win32 wsprintf
have similar problems with buffer overflow.
The snprintf
function provided in (recent) Linux implementations like GCC solves the problem admirably.
In MSVC there is a similarly named _snprintf
function that you'd expect to do the same thing,
wouldn't you? Wrong! Spot the difference:
Linux: int snprintf(char *str, size_t size, const char *format, ...);
The snprintf() function will write at most size-1 of the
characters printed into the output string (the size'th character then
gets the terminating `\0'); if the return value is greater than or equal
to the size argument, the string was too short and some of the printed
characters were discarded. The output is always null-terminated.
MSVC: int _snprintf(char *buffer, size_t count, const char *format [,argument] ... );
_snprintf returns the number of bytes stored in buffer, not counting the terminating null character.
If the number of bytes required to store the data exceeds count, then count bytes of data are stored
in buffer and a negative value is returned.
Can you see the problem? In the Linux version, the output is always null-terminated. In MSVC, it's not.
Even more subtle is the difference between the size
parameter in Linux and count
parameter
in MSVC. The former is the size of the output buffer including the terminating null
and the latter is the maximum count of characters to store, which excludes the terminating null.
Here's a compromise solution we use in MSVC environments (last updated 2011-12-11). The output is always null-terminated and truncated if necessary. The return value is either the number of characters stored or -1 if truncation occurs.
#include <stdarg.h> #include <stdio.h> #ifdef _MSC_VER int slprintf(char *buffer, size_t bufsize, const char *fmt, ...) { va_list ap; int ret; if ((int)bufsize <= 0) return -1; va_start(ap, fmt); ret = _vsnprintf(buffer, bufsize-1, fmt, ap); if (ret < 0) buffer[bufsize-1] = '\0'; va_end(ap); return ret; } #endif
To be consistent when we port to a Linux GCC environment,
where the return values and size parameter of snprintf
and vsnprintf
are different from MSVC
(and, sadly, better), we use the following:
int slprintf(char *buffer, size_t bufsize, const char *fmt, ...) { va_list ap; int ret; if ((int)bufsize <= 0) return -1; va_start(ap, fmt); ret = vsnprintf(buffer, bufsize, fmt, ap); if (ret >= bufsize) ret = -1; va_end(ap); return ret; }
Our safe versions of strncpy and strncat
The Standard C functions strncpy()
and strncat()
have their problems.
The safer ISO/IEC TR 24731 functions strncpy_s()
and strncat_s()
are not so convenient
and are missing in many compilers.
Here are our versions:
/* strlncpy and strlncat have similar behaviour to strncpy_s and strncat_s, * but they *always* truncate, are only for single-byte ANSI characters, * and return the total length of the string they tried to create. */ size_t strlncpy(char *d, size_t bufsize, const char *s, size_t n) { size_t len; size_t ret; if (!d || !s || (int)bufsize <= 0) return 0; len = strlen(s); if (len > n) len = n; ret = len; if (len >= bufsize) len = bufsize-1; memcpy(d, s, len); d[len] = 0; return ret; } size_t strlncat(char *d, size_t bufsize, const char *s, size_t n) { size_t len1; size_t len2; size_t ret; if (!d || !s || (int)bufsize <= 0) return 0; len1 = strlen(d); len2 = strlen(s); if (len2 > n) len2 = n; ret = len1 + len2; if (len1+len2 >= bufsize) len2 = bufsize - (len1 + 1); if (len2 > 0) { memcpy(d+len1, s, len2); d[len1+len2] = 0; } return ret; }
Some cute programs in C
Calculate pi to 800 digits in 160 characters of code. Written by Dik T. Winter at CWI.
int a=10000,b,c=2800,d,e,f[2801],g;main(){for(;b-c;)f[b++]=a/5; for(;d=0,g=c*2;c-=14,printf("%.4d",e+d/a),e=d%a)for(b=c;d+=f[b]*a, f[b]=d%--g,d/=g--,--b;d*=b);}
Calculate the day of the week in 45 characters of code. Written by Mike Keith.
(d+=m<3?y--:y-2,23*m/9+d+4+y/4-y/100+y/400)%7
Diffie-Helman in 10 lines of code posted anonymously to sci.crypt and publicised by Adam Back. This actually carries out multiple precision modular exponentation using 8-bit digits. Set S to the number of 8-bit digits required plus 1. This example is for 1024 bits.
#include <stdio.h> /* Usage: dh base exponent modulus */ typedef unsigned char u;u m[1024],g[1024],e[1024],b[1024];int n,v,d,z,S=129;a( u *x,u *y,int o){d=0;for(v=S;v--;){d+=x[v]+y[v]*o;x[v]=d;d=d>>8;}}s(u *x){for( v=0;(v<S-1)&&(x[v]==m[v]);)v++;if(x[v]>=m[v])a(x,m,-1);}r(u *x){d=0;for(v=0;v< S;){d|=x[v];x[v++]=d/2;d=(d&1)<<8;}}M(u *x,u *y){u X[1024],Y[1024];bcopy(x,X,S );bcopy(y,Y,S);bzero(x,S);for(z=S*8;z--;){if(X[S-1]&1){a(x,Y,1);s(x);}r(X);a(Y ,Y,1);s(Y);}}h(char *x,u *y){bzero(y,S);for(n=0;x[n]>0;n++){for(z=4;z--;)a(y,y ,1);x[n]|=32;y[S-1]|=x[n]-48-(x[n]>96)*39;}}p(u *x){for(n=0;!x[n];)n++;for(;n< S;n++)printf("%c%c",48+x[n]/16+(x[n]>159)*7,48+(x[n]&15)+7*((x[n]&15)>9)); printf("\n");}main(int c,char **v){h(v[1],g);h(v[2],e);h(v[3],m);bzero(b,S);b[ S-1]=1;for(n=S*8;n--;){if(e[S-1]&1)M(b,g);M(g,g);r(e);}p(b);}Note: For strict ANSI you need to add
#include <string.h>
and substitute memset(x,0,S)
and memcpy(y,Y,S)
for the bzero() and bcopy() functions above (or write your own loops).
The Best One Liner winner in the 1987 International Obfuscated C Code Contest (IOCCC), written by David Korn and judged "the best one line entry ever received".
main() { printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);}This should compile without changes on a Unix or Cygwin system. If you are stuck, here are some hints to help understand how it works.
Free C compilers and utilities to download
- Pelles C: Pelles C for Windows is a complete development kit for Windows and Pocket PC.
- Borland bcc32 Compiler: You can still download the (old, perhaps obsolete) Borland C/C++ 5.5 command line compiler for free (you may need to fill in few forms). For a quick get-started guide see Using the Borland 5.5 Compiler and command-line tools by John Ray Thomas. For instructions on compiling and linking a Win32 program, see Brook Miles' tutorial.
- MinGW: Minimalist GNU for Windows. MinGW provides a complete Open Source programming tool set which is suitable for the development of native MS-Windows applications. It includes a port of the GNU Compiler Collection (GCC), including C.
- Cygwin: Cygwin is a Linux-like environment for Windows.
It comes with the standard GNU gcc compiler.
Particularly useful to test whether your MSVC code will compile on a Linux system
without having one available (well, almost).
If you're a really anally-retentive C programmer, use this command to compile your code:
gcc -ansi -pedantic -Wall -O foo.c -o foo
(that's a capital letter-O for Oscar, not zero). - Artistic Style: A Free, Fast and Small Automatic Formatter for C, C++, C#, and Java Source Code by Tal Davidson and Jim Pattee
- Free Debugging Tools, Program Checkers, Bug Trackers: Christopher Heng's thefreecountry™ links to debuggers, source code checkers (like lint, that scan your source code for suspicious constructs that might be a bug waiting to happen), debugging tools (like strace that traces a system call), API wrappers (that wrap calls to system libraries so that you can check for valid parameters/arguments in system calls), as well as bug trackers which allow you to keep track of bug reports from users and the status of the bugs. There is also a good link to free C/C++ compilers and interpreters.
- Splint - Secure Programming Lint: Splint is a tool for statically checking C programs for security vulnerabilities and coding mistakes. Published by the University of Virginia, Department of Computer Science. A nice utility to know about and claim you use. To be honest, we find it throws up too many false positives to be any practical use.
Some interesting articles
These are interesting articles or links on C Programming that we've stumbled across in the last few years. If there wasn't a valid link to the original site, then the articles are posted here as an educational aid. We don't intend to infringe anyone's copyright here. If you are the original author and have a problem with any of these articles being posted here, please contact us and we'll remove it or replace it with a link to your site.
Joy of Programming: The Legacy of C | S.G. Ganesh's column in "Linux ForYou" in memory of Dennis Ritchie, who died on 8 October 2011, which reflects on the unique aspects of the C programming language. Includes a link to the TIOBE Programming Community Index which shows that the C programming language is still very popular (in January 2104 it was number 1, with Java in second place). |
Interview with Brian Kernighan | Enjoyable reading with some support for our views on C++. Plus advice on how to pronounce his name correctly. |
comp.lang.c Frequently Asked Questions | Link to www version. Highly relevant. |
ANSI C Programming Lanuage ANSI C Standard Library | A useful short help on ANSI C Programming, including precedence and the preprocessor, and a summary of the ANSI C standard library functions. By Ross L Richardson of UTAS (original links no more). |
Standard C programming | This document by P.J. Plauger and Jim Brodie provides all the information you need to read and write programs in the Standard C programming language. |
Rationale for C90 ANSI C | A link to the original Rationale for American National Standard for Information Systems - Programming Language - C. This was included in the original ANSI standard explaining why things were done as they were. It was unfortunately dropped from the ISO standard. |
C99 Standard | The last public draft of the C99 standard (ISO/IEC 9899:1999) is available from the JTC1/SC22/WG14-C site as well as the Technical Corrigenda and the Rational for the C99 standard. |
The C Programming Language | A useful collection of links related to the C programming language. |
Style in C | Rob Pike's set of essays on coding style and a "philosophy of clarity in programming" as opposed to laying down hard rules. |
C Traps and pitfalls | An essay by Andrew Koenig of AT&T Bell Labs written as the basis to his book C Traps and Pitfalls. "The C language is like a carving knife: ... This paper shows some of the ways C can injure the unwary, and how to avoid injury." Worth reading (and then re-reading). |
Incompatibilities Between C and C++ | David R. Tribble's detailed listings of incompatibilities between ISO C and ISO C++. |
How To Steal Code | Henry Spencer's article on inventing the wheel only once. "Never build what you can (legally) steal! Done right, it yields better programs for less work". There are some useful C programs at the end. |
The Ten Commandments for C Programmers | Henry Spencer's amusing, but true, adaptation of the ten commandments for C programmers. We love number 6: If a function be advertised to return an error code in the event of difficulties, thou shalt check for that code, yea, even though the checks triple the size of thy code and produce aches in thy typing fingers, for if thou thinkest ``it cannot happen to me'', the gods shall surely punish thee for thy arrogance. Read and take note. |
How to write unmaintainable code | A link to Roedy Green's very amusing article on how to guarantee a lifetime's employment by making life impossible for maintenance programmers. (He actually has to explain that it's a joke!) Although focused on Java, he gives plenty of examples in C. If you give someone a program, you will frustrate them for a day; if you teach them how to program, you will frustrate them for a lifetime. More quotes below. |
Quotes from How to write unmaintainable code
Testing is for cowards: A brave coder will bypass that step. Too many programmers are afraid of their boss, afraid of losing their job, afraid of customer hate mail and afraid of being sued. This fear paralyzes action, and reduces productivity. Studies have shown that eliminating the test phase means that managers can set ship dates well in advance, an obvious aid in the planning process. With fear gone, innovation and experimentation can blossom. The role of the programmer is to produce code, and debugging can be done by a cooperative effort on the part of the help desk and the legacy maintenance group.
Never, Ever Do Any Performance Testing: Hey, if it isn't fast enough, just tell the customer to buy a faster machine. If you did do performance testing, you might find a bottleneck, which might lead to algorithm changes, which might lead to a complete redesign of your product. Who wants that? Besides, performance problems that crop up at the customer site mean a free trip for you to some exotic location. Just keep your shots up-to-date and your passport handy.
Never Validate: Never check input data for any kind of correctness or discrepancies. It will demonstrate that you absolutely trust the company's equipment as well as that you are a perfect team player who trusts all project partners and system operators. Always return reasonable values even when data inputs are questionable or erroneous.
Quotes from How to write unmaintainable code by Roedy Green.
Contact
For more information, or to comment on this page, please send us a message.
This page last updated 27 June 2020
[Go to first comment] [Go to top]
Comments
[Go to last comment] [Go to top]
24 comments so far [Sorry, comments are now closed. Thanks to everyone below for their contributions.]