Logical operators perform logic tests, and you should already know what these do. They alter the bits that make up bytes and other data types in various ways. Assignment operators simply assign expressions to variables, but C combines a large number of other operators with assignment.
So when I say and-equal, I mean the bitwise operators, not the logical operators. Study your flash cards while you continue with the book. If you spent 15—30 minutes a day before studying, and another 15—30 minutes before bed, you could most likely memorize all of these in a few weeks. Exercise 6. Memorizing C Syntax.
Trust me when I tell you that the small amount of time spent memorizing these things will pay huge dividends later as you go through the book. You can and should do both. Use your flash cards as a warm up before coding that day.
Take them out and drill on them for 15—30 minutes, then sit down and do some more exercises in the book. The Keywords The keywords of a language are words that augment the symbols so that the language reads well.
In the middle are languages like C, Python, Ruby, and many more that mix sets of keywords with symbols to create the basis of the language. The technical term for processing the symbols and keywords of a programming language is lexical analysis. The word for one of these symbols or keywords is a lexeme. Syntax Structures I suggest you memorize those keywords, as well as memorizing the syntax structures.
A syntax structure is a pattern of symbols that make up a C program code form, such as the form of an if-statement or a while-loop. You should find most of these familiar, since you already know one language. The only trouble is then learning how C does it. The best way to test your memory of syntax structures is to open a text editor, and where you see switch-statement, try to write the code form after saying what it does.
You can also use continue to cause it to loop. You can also use break to exit a loop. It can also have continue and break inside to control how it operates. Finally, union creates something like a struct, but the elements will overlap in memory. In the video for this exercise, I show you how to use Anki to do this efficiently, but you can replicate the experience with simple index cards, too. Look at this as an opportunity to improve your memorization and learning skills.
The more you do it, the better at it you get and the easier it gets. You might spend 15 minutes and simply hate doing it and feel like a total failure.
Perseverance will get you past the initial frustration, and this little exercise will teach you two things: 1. You can use memorization as a self-evaluation of your competence. Nothing tells you how well you really know a subject like a memory test of its concepts.
The way to conquer difficulty is a little piece at a time. Take this as an opportunity to build your confidence in tackling large tasks in small pieces. Never confuse an ability to regurgitate memorized facts with ability to actually do something well.
To do that you need to apply these facts in different situations until you know how to use them. Exercise 7. Variables and Types. I can break it down as follows: ex7. Adding l the letter tells the program to print the number as a long decimal. This is effectively the number 0. This demonstrates an ugly hack you might see sometimes. This source file demonstrates how some math works with different types of variables.
At the end of the program, it also demonstrates something you see in C but not in many other languages. To C, a character is just an integer. This means you can do math on them, and a lot of software does just that—for good or bad. This last bit is your first glance at how C gives you direct access to the machine. The entire universe has bugs. You are expected to have That is only a 1. How to Break It Again, go through this and try to break the printf by passing in the wrong arguments.
When you break it, run it under the debugger to see what it says about what you did. Exercise 8. If, Else-If, Else. This is another example of C being closer to how a computer works, because to a computer, truth values are just integers.
However, C does have a typical if-statement that uses this numeric idea of true and false to do branching. You suck. The braces make it clear where one branch of code begins and ends.
Other than that, the code works the way it does in most other languages. Fix it. Exercise 9. While-Loop and Boolean Expressions. So to replicate how the for-loop works, we need to do our own initializing and incrementing of i.
What You Should See The output is basically the same, so I just did it a little differently so that you can see it run another way. Exercise 9 Session. Depending on what i starts with, the loop might not run at all, or run for an extremely long time. Now your second loop might or might not run.
Exercise Switch Statements. In other languages, like Ruby, you have a switch-statement that can take any expression. For these languages, switch-statements are more like alternatives to if-statements and work the same internally.
In C, the switch-statement is actually quite different and is really a jump table. Instead of random Boolean expressions, you can only put expressions that result in integers. These integers are used to calculate jumps from the top of the switch to the part that matches that value. In this program, we take a single command line argument and print out all vowels in an incredibly tedious way to demonstrate a switch-statement.
This is why you have break on some of the case blocks but not on others. You can also break this program in a few other ways. See if you can bust it yourself. Which do you like better? Arrays and Strings. You probably clued in to this in the last exercise since we did it manually.
In this code, we set up some arrays the tedious way, by assigning a value to each element. What You Should See When you run this code, you should first see the arrays printed with their contents initialized to 0 zero , then in its initialized form.
Exercise 11 Session. This is a shortcut in C. Look at how they changed. Now the numbers are set, but do you see how the name string prints my name correctly? The first one is less common and the second is what you should use for string literals like this. Try moving where you declare the variables to see if you get an error. This is part of the voodoo of C: Sometimes just where the variable is located changes the bug.
What kind of compiler warnings do you get? What does the debugger think of that? How might you accomplish this crazy hack? Sizes and Arrays. Before I can really explain the significance of this, I have to introduce a couple more concepts: sizeof and arrays.
In this code, we create a few arrays with different data types in them. Because arrays of data are so central to how C works, there are a huge number of ways to create them.
C is all about the size and location of pieces of memory, and what you do with them. To help you keep this straight, it gives you sizeof so that you can ask how big something is before you work with it. Now you see the output of these different printf calls and start to get a glimpse of what C is doing.
Your output could actually be totally different from mine, since your computer might have different size integers. Looking at the code, this matches what we put in the initializer. Make sure you can go through and see how these output lines match what was created. How to Break It Breaking this program is fairly easy. Run it under the debugger too.
Try running it under the debugger a few times and see if you get some new errors. In some cases, you might still get lucky and not catch any errors. See what the debugger thinks of that. For-Loops and Arrays of Strings. You can make an array of various types with the idea that a string and an array of bytes are the same thing. The next step is to do an array that has strings in it. What You Should See To play with this program, then, you have to run it two ways.
The first way is to pass in some command line arguments so that argc and argv get set. Exercise 13 Session. Another way to figure this is out is to build the same structure in a programming language you are more familiar with, like Python or Ruby.
See if you can bust it by giving it way too many arguments. Do you have to adjust argc as well, or does it just work? Why does 0-based indexing work here? Try the inverse. Writing and Using Functions. What You Should See To play with this program, you just feed it different command line arguments, which get passed through your functions.
The isalpha and isblank do all the work of figuring out if the given character is a letter or a blank. Use other similar functions to print out only digits or other characters. Pointers, Dreaded Pointers. Pointers are famous mystical creatures in C. As you go through this detailed description, try to answer the questions for yourself on a piece of paper, then see if what you guessed matches my description of pointers later.
This is using i to index into the array. Seeing the similarity yet? Study that and try to explain it to yourself, too. Why does that work? What You Should See After you run this program, try to trace back each line printed out to the line in the code that produced it.
Exercise 15 Session. Frank has 43 years alive. Mary has 12 years alive. John has 89 years alive. Lisa has 2 years alive. Frank is 43 years old. Mary is 12 years old. John is 89 years old. Lisa is 2 years old. Frank is 43 years old again. Mary is 12 years old again. John is 89 years old again. Lisa is 2 years old again. Frank lived 43 years so far. Mary lived 12 years so far.
John lived 89 years so far. Lisa lived 2 years so far. The ages array name is actually an address in the computer. That leads to a certain realization: C thinks your whole computer is one massive array of bytes.
Yes, and that thing is called a pointer. The example program is then moving them around or doing math on them to get values out of the memory. In the last for-loop, though, these two pointers are being moved on their own, without i to help out. In that loop, the pointers are treated like a combination of array and integer offset rolled into one. C knows where pointers are pointing, knows the data type they point at, the size of those types, and how to get the data for you.
Just like with i, you can increment, decrement, subtract, or add to them. But, just like ages, you can also get values out, put new values in, and use all of the array operations.
In almost all other cases, you actually want to use an array. A pointer gives you raw, direct access to a block of memory so you can work with it.
The final thing to grasp at this stage is that you can use either syntax for most array or pointer operations. You can take a pointer to something, but use the array syntax to access it. You can take an array and do pointer arithmetic with it. For nearly everything else, you might see people use pointers when they should be using arrays. In the early days of C programming, people used pointers to speed up their programs, because the compilers were really bad at optimizing array usage.
Instead, you should go with arrays whenever you can, and then only use pointers as a performance optimization if you absolutely have to. This is harder than it looks. Try to pass pointers to these functions so that they work on the data. Remember you can declare a function to accept a pointer, but just use it like an array. Structs And Pointers to Them. What does each give you? The final result is a new compound type that lets me reference these elements all as one or each piece by name.
The strdup actually is like malloc, and it also copies the original string into the memory it creates. Then I use the function free to return the memory I got with malloc and strdup. You can also search online for the information.
A structure in C is a collection of other data types variables that are stored in one block of memory where you can access each variable independently by name. They are similar to a record in a database table, or a very simplistic class in an OOP language.
In fact, in most early Assembler code and even some now , this is what you would do. In C, you can let it handle the memory structuring of these compound data types and then focus on what you do with them. Figure out the options you need to pass to the debugger to get it to print how you leaked this memory. Again, use the right options to see how the debugger tells you exactly where you messed up. Extra Credit In this part of the exercise, I want you to attempt something difficult for the extra credit: Convert this program to not use pointers and malloc.
Heap and Stack Memory Allocation. It also introduces memory allocation more formally, and gets you started working with files. Fixed sized structs The Address struct then uses these constants to create a piece of data that is fixed in size, making it less efficient but easier to store and read. That lets you write the whole thing to disk in one move later. These are just numbers, so you can use perror to print the error message. Read up on this and similar functions.
In some rare systems, NULL will be stored in the computer represented as something not 0, but the C standard says you should still be able to write code as if it has a 0 value.
Heap versus Stack Allocation You kids have it great these days. You play with your Ruby or Python and just make objects and variables without any care for where they live.
It all depends on where you get the storage. Each time you call malloc, the OS uses internal functions to register that piece of memory to you, and then returns a pointer to it. Failing to do this will cause your program to leak memory, but Valgrind will help you track these leaks down. The stack is a special region of memory that stores temporary variables, which each function creates as locals to that function.
How it works is that each argument to a function is pushed onto the stack and then used inside the function. This also happens with all local variables like char action and int id in main. The advantage of using a stack for this is simply that when the function exits, the C compiler pops these variables off of the stack to clean up.
This is simple and prevents memory leaks if the variable is on the stack. In this case, use the heap with malloc. Finally, when a program exits, the OS will clean up all of the resources for you, but sometimes not immediately. A common idiom and one I use in this exercise is to just abort and let the OS clean up on error. For example, remove the check on line that prevents you from passing in any record number. Open it in any editor and change random bytes, and then close it.
For example, getting the file and action backward will make it create a file named after the action, and then do an action based on the first character. Go read about strncpy and try to find out what happens when the name or address you give is greater than bytes.
Try to see what the biggest database is before you cause the program to die due to lack of memory from malloc. See if you can calculate a new size after adding more fields. Hint: Use set -e at the top of a bash to make it abort the whole script if any command has an error. How does this new version of the program compare to the other one?
Pointers to Functions. Functions in C are actually just pointers to a spot in the program where some code exists. The main use for this is to pass callbacks to other functions, or to simulate classes and objects. This is similar to how pointers to arrays can be used just like the arrays they point to.
Pointers to functions can be used like the functions they point to but with a different name. The solution is to use typedef, which is a C keyword for making new names for other, more complex types. I demonstrate this in the following exercise code: ex This is now a function that will return a comparison between two integers for sorting.
What You Should See Running this program is simple, but you should try different combinations of numbers, or even other characters, to see what it does. Exercise 18 Session. These function pointers are like every other pointer, so they point at blocks of memory. C has this ability to take one pointer and convert it to another so you can process the data in different ways. This loop is sort of like converting your function to a string, and then printing out its contents.
That should be the raw assembler byte code of the function itself, and you should see that they start the same but then have different endings.
Rerun your program and see what happens. Strings you find are the easiest things to change. Then, run the debugger and see what that reports. Use it to test both of your algorithms. You can thank me later when you realize how insanely awesome these macros are. I owe you my firstborn child because you saved me a decade of heartache and prevented me from killing myself more than once.
The C Error-Handling Problem Handling errors is a difficult activity in almost every programming language.
There are entire programming languages that try as hard as they can to avoid even the concept of an error. Other languages invent complex control structures like exceptions to pass error conditions around. C tackles the problem by returning error codes and setting a global errno value that you check.
This makes for complex code that simply exists to check if something you did had an error. This means for every function call and yes, every function , you are potentially writing three or four more lines just to make sure it worked.
No point in bothering with cleanup when the OS will do it for you. Today, though, many C programs need to run for weeks, months, or years, and handle errors from many different sources gracefully. Other languages solve this problem with exceptions, but those have problems in C and in other languages, too.
Trying to marshal exceptions up the stack in C is difficult, and no other libraries will understand it. This system is easy to understand, works with every library, and makes C code more solid and clearer. You can use a macro called check to check return codes, print an error message, and then jump to the cleanup section. You can combine that with a set of logging functions for printing out useful debug messages. You can see in this case the define debug is just replaced with nothing the right side is empty.
Very helpful. Using dbg. Click here to view code image 1 include "dbg. See how it reports the exact line number where the check failed? Also, see how it prints the error message for you when errno is set? Again, that will save you hours of debugging. Imagine that I have a function called dosomething that returns the typical 0 for success and -1 for an error.
What I want to use the CPP for is to encapsulate this if-statement into a more readable and memorable line of code. It simply replaces itself with a call to fprintf to stderr. The only tricky part of this macro is the use of What this does is let you pass variable arguments to the macro, so you can pass in the arguments that should go to fprintf. How do they get injected into the fprintf call?
The arguments age, name are the See the age, name at the end? The next thing to study is how check crafts the if-statement for the error checking. Which means: If A is false, then clear errno and goto the error label. What you should be getting from this trip through these two macros is that the CPP replaces macros with the expanded version of their definition, and it will do this recursively, expanding all of the macros in macros.
The CPP, then, is just a recursive templating system, as I mentioned before. Its power comes from its ability to generate whole blocks of parameterized code, thus becoming a handy code generation tool. That leaves one question: Why not just use a function like die?
The reason is that you want file:line numbers and the goto operation for an error handling exit. By wrapping the if-statement in a macro called check, you make it clear that this is just error checking, and not part of the main flow. You can see this already in the dbg. Advanced Debugging Techniques. This is another video-focused exercise where I show you advanced debugging tricks with my technique.
The discussion below reinforces the video, so watch the video first. Debugging will be much easier to learn by watching me do it first.
The problem many programmers have with this approach is that they feel like it will slow them down. With GDB, you have to configure the same information uniquely for every defect you have to hunt down. Debuggers are always obtuse and weird with their own quirky interfaces and inconsistencies. You can actually just compile the debugs while you work, and when a unit test explodes, just go look at the logs at any time.
Despite all of these reasons that I rely on debug over GDB, I still use GDB in a few situations, and I think you should have any tool that helps you get your work done. Sometimes, you just have to connect to a broken program and poke around. My use of GDB in this case is entirely to gather information. Use this for every bug until you only need it on the very difficult ones.
You write down a set of hypotheses, then you use debugging to prove or disprove them. This gives you insight into more possible causes and eventually you find it.
You can also do this with debug printing, the only difference is that you actually write out your hypotheses in the source code instead of in the notes. In a way, debug printing forces you to tackle bugs scientifically because you have to write out hypotheses as print statements. This core file is like a postmortem of the program that you can load up to see what happened right at the crash and what caused it. Change ex Advanced Data Types and Flow Control. This exercise will be a complete compendium of the available C data types and flow control structures you can use.
For this exercise to be useful, you should spend at least a week hammering in the content and filling out all of the elements that are missing here.
If one side of an expression is on this list, then the other side is converted to that type before the operation is done. It goes in this order: 1. Use explicit casting operations to make it exactly what you want. Putting the type you want in parentheses before the variable name is how you force it into the type you really need.
The important thing, though, is always promote up, not down. Type Sizes The stdint. This is easier to work with than the older limits. Pay attention! This N could be any number—8, 16, 32, 64, maybe even There are also macros in stdint. Compilers have to at least have these, and then they can allow other, larger types. Here is a full list that should be in stdint.
Data Operators These are used to access data in different ways and forms. Logic Operators These handle testing equality and inequality of variables. Bit Operators These are more advanced and are for shifting and modifying the raw bits in integers. Boolean Operators These are used in truth testing. Study the ternary operator carefully. Most of the above operations can also be combined into a compound assignment operator. Research it online so you know you got it right.
The Stack, Scope, and Globals. The concept of scope seems to confuse quite a few people when they first start programming. It originally came from the use of the system stack which we lightly covered earlier , and how it was used to store temporary variables.
The real purpose of this exercise, though, is to learn where the hell things live in C. Once you know where things are, the concept of scope becomes easier. This exercise will require three files: ex It uses a function level static variable ratio to keep track of what the ratio currently is. Click here to view code image 1 include "ex This version of count is actually a whole new variable.
You have count in two places: the parameters to this function, and in the if-statement. Just start to realize that if you make a variable inside a block as in if-statements or while-loops , then those variables are new variables that exist only in that block.
This is crucial to understand, and is also a source of many bugs. The big thing to notice is that the local count variable remains unchanged. To do that, you need our old friend the pointer. If you were to pass a pointer to this count, then the called function would have the address of it and could change it. What You Should See This time, instead of using your Makefile, I want you to build these two files manually so you can see how the compiler actually puts them together.
The most important thing to realize is that all of this causes bugs. If you want people to modify or set this variable, then make accessor functions.
Just create things with malloc. The constraints of different platforms even make it necessary sometimes. What if you had a pointer to it? Write an example of both. This little slice of awesome evil? Figuring out how it works is also a good, fun puzzle. Part of the fun of C is that you can come up with crazy hacks like this, but this is also what makes C annoying to use.
But you should never use this. Always strive for easy-to-read code. Study these three functions before continuing. Run it under your debugger to see if you can catch any more errors. Try causing some of your own, as I showed you in Exercise 4. Solving the Puzzle The first thing to understand is that C is rather loose regarding some of its syntax. This is why you can put half of a do-while in one part of a switch-statement, then the other half somewhere else, and the code will still work.
The second thing is how the default fallthrough semantics of switch-statements let you jump to a particular case, and then it will just keep running until the end of the switch. Why Bother? The purpose of this trick is to manually do loop unrolling. For example, if you know a loop runs a minimum of 20 times, then you can put the contents of the loop 20 times in the source code. Can you do a macro that lays down eight at a time?
Input, Output, Files. This program is deceptively simple, and introduces a function called fscanf, which is the file scanf. The scanf family of functions are the inverse of the printf versions. Where printf printed out data based on a format, scanf reads or scans input based on a format. You can see that the same format string is used as printf to print an integer.
You should also see that you have to give the address of you. What You Should See When you run this program, you should see your inputs being properly converted. Make sure you try to give it bogus input too, so you can see how it protects against the input. Exercise 24 Session. Zed What's your Last Name? Shaw How old are you?
How to Break It This is all fine and good, but the really important part of this exercise is how scanf actually sucks. That function has no idea how big the input buffer is at all and will just trash your program.
Notice it seems to read too much and then eat your enter key? Next, change the fgets to use gets, then run your debugger on ex This feeds random garbage into your program.
You should never use this function, ever. Finally, take the input for you. Then, feed it bad numbers like -1 or Do this under the debugger so you can see what happens there, too. Create flash cards that have the function name and all the variants similar to it. Finally, use man to read the help for each variant to get the information you need for the flash cards.
For example, the page for fscanf comes from man fscanf. Variable Argument Functions. In C, you can create your own versions of functions like printf and scanf by creating a variable argument function, or vararg function. These functions use the header stdarg. They are handy for certain types of builder functions, formatting functions, and anything that takes variable arguments.
Understanding vararg functions is not essential to creating C programs. However, knowing how a vararg function works will help you debug the programs you use and gives you a better understanding of the computer. This program is similar to the previous exercise, except I have written my own scanf function to handle strings the way I want. This configures the gear in stdarg.
I just have integers, characters, and strings. Exercise 25 Session. Zed What's your initial? A What's your last name? Some platforms will use macros, others will use functions, and some will have these do nothing. It all depends on the compiler and the platform you use. Project logfind. This is a small project for you to attempt on your own. In this exercise, I describe a tool I want you to implement, and I describe it in a vague way on purpose.
This is done so that you will try to implement whatever you can, however you can. Think of this project as a real-world puzzle that you might have to solve. The logfind Specification I want a tool called logfind that lets me search through log files for text. This tool is a specialized version of another tool called grep, but designed only for log files on a system. The idea is that I can type: logfind zedshaw.
The logfind tool should have these basic features: 1. So logfind zedshaw smart guy will find all files that have zedshaw and smart and guy in them. It takes an optional argument of -o if the parameters are meant to be or logic. The list of file names can be anything that the glob function allows. Refer to man 3 glob to see how this works. I suggest starting with just a flat list of exact files, and then add glob functionality. You should output the matching lines as you scan, and try to match them as fast as possible.
Remember that this may be very hard, so take it a tiny bit at a time. Write some code, test it, write more, test that, and so on in little chunks until you have it working. Start with the simplest thing that gets it working, and then slowly add to it and refine it until every feature is done. Creative and Defensive Programming.
You have now learned most of the basics of C programming and are ready to start becoming a serious programmer. This is where you go from beginner to expert, both with C and hopefully with core computer science concepts. Before I can do that, I have to teach you some basic skills and ideas that will help you make better software. Exercises 27 through 31 will teach you advanced concepts, featuring more talking than coding. The first step in getting better at writing C code and really any language is to learn a new mind-set called defensive programming.
Defensive programming assumes that you are going to make many mistakes, and then attempts to prevent them at every possible step. Fear will quickly kill creativity, so the mind-set I adopt, and many programmers copy, is that accidents are designed to make you unafraid of taking chances and looking like an idiot. I only adopt this mind-set temporarily, and even have little tricks to turn it on.
By doing this, I can come up with ideas, find creative solutions, open my thoughts to odd connections, and just generally invent weirdness without fear. Where other people make a mistake is carrying the creative mind-set into their implementation phase. These are lies. The trick to being creative and making solid software is the ability to adopt a defensive programming mind-set.
This mind-set lets you be honest about your work and critically analyze it for improvements. It says your code is full of errors. This is a significant thing to understand because it gives you the power of objectivity for the next implementation.
Just like the creative mind-set, the defensive programming mind-set has a dark side, as well. Defensive programmers are paranoid, and this fear prevents them from ever possibly being wrong or making mistakes. While I work on the real version, I ruthlessly follow these strategies and try to remove as many errors as I can, thinking like someone who wants to break the software.
Prevent Errors If an error is possible, no matter how probable, try to prevent it. Fail Early and Openly Fail early, cleanly, and openly, stating what happened, where, and how to fix it. Document Assumptions Clearly state the pre-conditions, post-conditions, and invariants.
Automate Everything Automate everything, especially testing. Simplify and Clarify Always simplify the code to the smallest, cleanest form that works without sacrificing safety.
Applying the Eight Strategies These ideas are all as great pop-psychology platitudes, but how do you actually apply them to working code? Take a look at these two functions that both copy a string and a simple main to test out the better one. A cornerstone of writing solid code is never writing loops that can possibly loop forever. The safercopy function tries to solve this by requiring the caller to give the lengths of the two strings it must deal with.
It can check that the lengths are right, and that the to string has enough space, and it will always terminate. This is the idea behind never trusting the inputs you receive. If you need the arguments to never be NULL, then you should check for that, too. If the sizes should be within sane levels, then check that. You simply assume that whoever is calling you got it wrong, and then try to make it difficult for them to give you another bad state. This extends to software you write that gets input from the external universe.
If you are writing a library, favor errors over failures. This little hack does the test, and when it fails, the OS will typically print the assert line for you that includes that message. For example, people frequently forget to check the return codes from fopen or fread, which causes them to use the resources the return codes give despite the error. This causes your program to crash or open an avenue for an attack.
Just doing these simple things will improve your resource handling and prevent quite a few errors. Probability is a funny thing because people are incredibly bad at guessing the probability of any event. People are, however, much better at determining if something is possible. The key reason is that for something to be probable, it first has to be possible.
Determining the possibility is easy, since we can all imagine something happening. Who knows? It may not be feasible to handle all of the possible ways your software can be broken, but you have to attempt it. You could also just give an easy or hard metric, or any metric that prevents you from working on the impossible when there are easier things to fix still on the list.
This little process will give you a nice list of things to do, but more importantly, keep you from working on useless things when there are other more important things to work on. You can also be more or less formal with this process. This is just how it is, so what you need to do is make sure the failures happen quickly, are clearly documented, give an error message, and are easy for the programmer to avoid. For every error you find, it prints a message, the file and line number where it happened, and forces a return code.
I tend to prefer returning an error code to aborting the program. Instead of having the programmer experience a segmentation fault explosion somewhere, I catch it right away and abort. In libraries, however, I try my hardest to never abort. Finally, a big part of being open about errors is not using the same message or error code for more than one possible error. You typically see this with errors in external resources. When designing your error reporting, make sure you give a different error message for the different possible errors.
The next step is to complete the contract and add invariants and postconditions. An invariant is a condition that must be held true in some state while the function runs.
Another example would be that a sorted data structure is always sorted during processing. A postcondition is a guarantee on the exit value or result of a function running. Or, you can use NULL to indicate an error, so that your postcondition checks that the resource is deallocated on any errors. In C programming, invariants and postconditions are usually used more in documentation than actual code or assertions.
The best way to handle them is to add assert calls for the ones you can, then document the rest. If you do that, when people hit an error they can see what assumptions you made when writing the function. Prevention over Documentation A common problem when programmers write code is that they will document a common bug rather than simply fix it. My favorite is when the Ruby on Rails system simply assumed that all months had 30 days.
Calendars are hard, so rather than fix it, programmers threw a tiny little comment somewhere that said this was on purpose, and then they refused to fix it for years. In the case of Ruby on Rails, not having date functions would have been better than including purposefully broken ones that nobody could use. As you go through your defensive programming cleanups, try to fix everything you can.
If you really have to keep this horribly broken feature, then I suggest you write it, document it, and then find a new job before you are blamed for it. Automate Everything You are a programmer, and that means your job is putting other people out of jobs with automation. The pinnacle of this is putting yourself out of a job with your own automation. The easiest way to do this is to write automated tests, or unit tests.
Or, if this is fun to you, then maybe you should work on software that makes automating these things easier. Simplify and Clarify The concept of simplicity is a slippery one to many people, especially smart people.
They generally confuse comprehension with simplicity. The actual test of simplicity is comparing something with something else that could be simpler. A love affair with complexity is a programming sickness. They are wrong, no matter how elegant they think their complex monstrosity is. If not, then pick the one that has the result you need. Simple and dirty beats complex and clean any day.
Simplicity is ironically a very complex thing, so using your taste as a guide is the best way to go. Question Authority The final strategy is the most important because it breaks you out of the defensive programming mind-set and lets you transition into the creative mind-set.
Defensive programming is authoritarian and can be cruel. This authoritarian attitude has the disadvantage of disabling independent creative thought. Rules are necessary for getting things done, but being a slave to them will kill your creativity. This final strategy means you should periodically question the rules you follow and assume that they could be wrong, just like the software you are reviewing. What I will typically do is go take a nonprogramming break and let the rules go after a session of defensive programming.
You need both creativity and strictness to do programming well, so work on both if you want to improve. Submit a patch that fixes a bug. Intermediate Makefiles. This skeleton directory will be used for the rest of the book.
The purpose of this structure is to make it easy to build medium-sized programs without having to resort to configure tools. If done right, you can get very far with just GNU make and some small shell scripts.
The Basic Project Structure The first thing to do is make a c-skeleton directory, and then put a set of basic files and directories in it that many projects have.
At the end you see me do a ls -l so that you can see the final results. It ends in. Makefile The main build file for the project. Remember that you need to consistently indent the Makefile with tab characters. Your text editor should know that and do the right thing. No programmer should use an editor that fails at something so simple. The Header This Makefile is designed to build a library reliably on almost any platform using special features of GNU make.
Book Site. For Weather, Flights, Runways of Airports all over the world, etc, click here. Book Description In this book, you'll learn C by working through 52 brilliantly crafted exercises. It'll Be Hard At First. His software has been used by many large and small companies. His essays are often quoted and read by members of many geek communities.
An entertaining and lively writer, he will keep you laughing and make you think. All Categories. Recent Books. Miscellaneous Books. Computer Languages.
Computer Science. Electrical Engineering. Linux and Unix. Microsoft and. Mobile Computing. Networking and Communications. Software Engineering.
0コメント