Reader small image

You're reading from  Learn C Programming. - Second Edition

Product typeBook
Published inAug 2022
PublisherPackt
ISBN-139781801078450
Edition2nd Edition
Right arrow
Author (1)
Jeff Szuhay
Jeff Szuhay
author image
Jeff Szuhay

Jeff Szuhay is the principal developer at QuarterTil2 which specializes in graphics-rich software chronographs for desktop environments. In his software career of over 35 years, he has engaged in a full range of development activities from systems analysis and systems performance tuning to application design, from initial development through full testing and final delivery. Throughout that time, he has taught computer applications and programming languages at various educational levels from elementary school students to university students, as well as developed and presented professional, on-site training.
Read more about Jeff Szuhay

Right arrow

Understanding the program development cycle

There are two main types of development environments:

  • Interpreted: In an interpreted environment such as Python or Ruby, the program can be entered, line by line, and run at any point. Each line is evaluated and executed as it's entered, and the results are immediately returned to the console. Interpreted environments are dynamic because they provide immediate feedback and are useful for the rapid exploration of algorithms and program features. Programs entered here tend to require the interpreting environment to be running as well.
  • Compiled: In a compiled environment, such as C, C++, C#, or Objective-C, programs are entered into one or more files, then compiled all at once, and if no errors are found, the program can be run as a whole. Each of these phases is distinct, with separate programs used for each phase. Compiled programs tend to execute faster since there is a separate, full compilation phase that can be run independently of the interpreting environment.

As with shampoo, where we are accustomed to wet hair, lather, rinse, and repeat, we will do the same with C – we will become familiar with the edit, compile, run, verify, and repeat cycle.

Edit

Programs are generated from text files whose filenames use predefined file extensions. These are known as source files or source code files. For C, the .c file extension indicates a C source code file. A .h extension (which is present in our Hello, world! program) indicates a C header file. The compiler looks for .c and .h files as it encounters them, and because each has a different purpose, it also treats each of them differently. Other languages have their own file extensions; the contents of a source code file should match the language that the compiler expects.

To create and modify C files, you will need a plain text editor. This is a program that allows you to open, modify, and save plain text without any formatting such as font size, font family, and font style. For instance, on Windows, Notepad is a plain text editor while Word is not. The plain text editor should have the following capabilities:

  • File manipulation: This allows you to open a file, edit a file, save the file and any changes that have been made to it, and, finally, save the file with another name.
  • The ability to navigate the file: This allows you to move up, down, left, or right to the beginning of the line, end of the line, beginning of the file, end of the file, and more.
  • Text manipulation: This allows you to insert text, delete text, insert a line, delete a line, selection, cut, copy, paste, undo/redo, and more.
  • Search and replace: This allows you to find text, replace text, and more.

The following capabilities are handy but not essential:

  • Automatic indentation
  • Syntax coloring for the specific programming language
  • Automatic periodic saving

Almost any plain text editor will do. Do not get too caught up in the features of any given text editor. Some are better than others; some are free, while others are costly and might not immediately be worth the expense (perhaps later, one or more might be worthwhile but not at this time), and none will do 100% of what you might want them to do.

Here are some free plain text editors that are worth installing on your computer and trying out:

  • Everywhere: Nano, which runs in a Terminal window; it has a moderate learning curve.
  • Linux/Unix: This consists of the following:

a. Vim or vi: This runs in a Terminal window; it has a moderate learning curve. It is on every Linux/Unix system, so it's worth learning how to use its basic features.

b. gedit: This is a powerful general-purpose editor.

c. Emacs: This is an everything and the kitchen sink editor; it has a very large learning curve.

  • Windows: This consists of the following:

a. Notepad: This is very simple – sometimes too simple for programming – but included in every Windows system.

b. Notepad++: This is a better version of Notepad with many features for programming.

  • macOS only: This runs BBEdit (the free version), which is a full-featured GUI programming text editor.

There are many text editors, each with its own strengths and weaknesses. Pick a text editor and get used to it. Over time, as you use it more and more, it will become second nature.

Compile

The compiler is a program that takes input source code files – in our case, the .c and .h files – translates the textual source code found there into machine language, and links together all the predefined parts required to enable the program to run on our specific computer hardware and OS. It generates an executable file that consists of machine language.

Machine language is a series of instructions and data that a specific Central Processing Unit (CPU) knows how to fetch from the program execution stream and execute on the computer one by one. Each CPU has its own machine language or instruction set. By programming in a common language, such as C, the programmer is shielded from the details of machine language; that knowledge is embodied in the compiler.

Sometimes, assembler language is called machine language, but that is not quite accurate since assembler language still contains text and symbols, whereas machine language only consists of binary numbers. Today, very few people have the skills to read machine language directly; at one time, many more programmers were able to do it. Times have changed!

When we compile our programs, we invoke the compiler to process one or more source files. The result of this invocation is either a success and an executable file is generated, or it will identify the programming errors it found during compilation. Programming errors can range from a simple misspelling of names or omitted punctuation to more complex syntax errors. Typically, the compiler tries to make sense of any errors it finds; it attempts to provide useful information for the problem it has found. Note that try and attempts are merely goals; in reality, the compiler might spew many lines of error messages that originate from a single error. Furthermore, the compiler will process the entire source code when invoked. You might find many different errors in different parts of the program for each compiler invocation.

A complete, runnable program consists of our compiled source code – the code we write – and predefined compiled routines that come with the OS, that is, code written by the authors of the OS. The predefined program code is sometimes called the runtime library. It consists of a set of callable routines that know how to interact, in detail, with the various parts of the computer. For example, in Hello, world!, we don't have to know the detailed instructions to send characters to the computer's screen – we can simply call a predefined function, printf();, to do it for us. printf() is part of the C runtime library, along with many other routines, as we will discover later. In the next chapter, we will explore the role of functions. How text is sent to the console is likely to be different for each OS, even if those OSes run on the same hardware. So, the programmers are shielded not only from the minutiae of the machine language, but they are also shielded from the varying implementation details of the computer itself.

It follows from this that for each OS, there is a compiler and a runtime library specific to it. A compiler designed for one OS will most likely not work on a different OS. If, by chance, a compiler from one OS just happens to or even appears to run on a different OS, the resulting programs and their executions would be highly unpredictable. Mayhem is likely.

Many C compilers for every OS

You can learn C on many computer platforms. Common compilers in use on Unix and Linux OS are either the GNU Compiler Collection (GCC) or the LLVM compiler project, Clang. For Windows, GCC is available via the Cygwin project or the MinGW project. You could even learn C using a Raspberry Pi or Arduino, but this is not ideal because of the special considerations required for these minimal computer systems. It is recommended that you use a desktop computer since many more computer resources (such as memory, hard drive space, CPU capability, and more) are available on any such computer that can run a web browser.

Important Note

You should be aware that there are many variants of C. Typically, these are created by hardware vendors for the specific needs of their customers or by other groups who wish to retain extended features not approved in the standard specification.

Also, be aware that there are areas of the C specification that are undefined; that is, the standards committees have left those implementation details up to the creators of a given compiler for a given hardware system.

In this book, we will describe and focus strictly upon what the C standard provides. However, our approach will be such that we will emphasize how to verify program behavior so that we will always know when our program behaves differently. Because compilers are written by humans and not autogenerated from a specification, we subscribe to the guidance: trust but verify.

A note about Integrated Development Environments

On many OSes, the compiler is installed as a part of an Integrated Development Environment (IDE) for that OS. An IDE consists of a set of programs needed to create, build, and test programs for that OS. It manages one or more files associated with a program, has its own integrated text editor, can invoke the compiler and present its results, and can execute the compiled program. Typically, the programmer never leaves this environment while developing. Often, the IDE streamlines the production of a standalone working program.

There are many such IDEs – Microsoft's Windows-only Visual Studio, Microsoft's multiplatform Visual Studio Code, Apple's Xcode for macOS and other Apple hardware platforms, Eclipse Foundation's Eclipse, and Oracle's Netbeans, to name a few. Each of these IDEs can develop programs in a variety of languages. Nearly all of the programs used in this book were developed using a simple IDE named CodeRunner for macOS.

We will not use an IDE to learn C. In fact, at this stage of your learning, it is not advised for several reasons. To begin, learning and using an IDE can be a daunting learning task in and of itself. This task can and should be put off until you have more experience with each of the individual parts of the program development cycle. IDEs, while they have common functions, are sometimes implemented in vastly different ways with far too many different features to explore. Learn C first; then, you can learn an IDE for your desired environment later.

Installing a compiler on Linux, macOS, or Windows

Here are the steps to follow to install a C compiler on the major desktop computer environments – Linux, macOS, and Windows. For other platforms, you'll have to do some investigation to find the compiler you need. However, since those platforms want you to use them, they'll likely make those instructions easy to find and follow:

  • For Linux, perform the following steps:

1. If you are running a Red Hat Package Manager (RPM)-based Linux, such as Red Hat, Fedora, or CentOS, enter the following command on the command line:

$ sudo yum group install development-tools

2. If you are running Debian Linux, open a Terminal window and enter the following command from the command line:

$ sudo apt-get install build-essential

3. Verify your installation by entering the following command on the command line:

$ cc --version

From the preceding command, you will observe that you likely have GCC or Clang. Either one is fine. You are now ready to compile C programs on your version of Linux.

  • For macOS, perform the following steps:

1. Open Terminal.app and enter the following on the command line:

$ cc --version

2. If the development tools have not been installed yet, simply invoking the preceding command will guide you through their installation.

3. Once the installation is complete, close the Terminal window, open a new one, and enter the following:

$ cc --version

You are now ready to compile C programs on your version of macOS.

  • For Windows, perform the following steps:

1. Install either Cygwin (http://www.cygwin.com) or MinGW (http://mingw-w64.org/) from their respective websites. Either one will work well. If you choose to install Cygwin, be sure to also install the extra package for the GCC. This will install a number of other necessary compilers and debugging programs with GCC.

2. Once the installation is complete, open Command Prompt and enter the following:

$ cc --version

You are now ready to compile C programs on your version of Windows.

Note that compilation is a two-part process – compiling and linking. Compiling involves syntax checking and converting source code into nearly complete executable code. In the linking phase, the nearly complete machine code is merged with the runtime library and becomes complete. Typically, when we invoke the compiler, the linker is also invoked. If the compiler phase succeeds (that is, with no errors), the linking phase is automatically invoked. Later, we will discover that we can get error messages from the compiler either at compile time – the compiling phase – or at link time – the linking phase – when all of the program's pieces are linked together.

We will learn how to invoke the compiler later when we compile our first program.

Throughout this book, once you have a working program, you will be directed to purposely break it – causing the compilation of your program to fail. This is so that you can start learning about the correlation of various program errors with compiler errors, so you will not be afraid of breaking your program. You will simply undo the change and success will be yours once more.

Run

Once compilation has been completed successfully, an executable file will be generated. This executable file, unless we provide an explicit name for it, will be named a.out. Typically, the executable file will be created in the same directory the compiler was invoked from. For the most part, we will make sure our current working directory has the same location as the source files.

Running an executable file is performed by invoking it from the command line. When invoked, the executable is loaded into the computer's memory and then becomes the CPU's program execution stream. Once loaded into memory, the CPU begins at the special reserved word, known as main(), and continues until either return; or a closing } character is encountered. The program stops and the executable is then unloaded from memory.

To run an executable, open Command Prompt (on Windows) or a Terminal window (on Linux and Mac), navigate with cd to the directory of the executable file, and simply enter the executable's name (for example, a.out or whatever you've specified).

Note

If you successfully navigate to the same location as the executable and you have verified it exists, but you get an error message from the command interpreter, you likely have a problem with your command interpreter's built-in PATH variable. To quickly work around this, enter the $ ./a.out command to run it. This instructs the command interpreter to look in the current directory for the file named a.out.

As the program runs, any output will be directed to the Terminal or console window. When the program has ended, the command interpreter will present you with a new command prompt.

Verify

At this point in the cycle, you might feel that simply getting your program to compile without errors and running it without crashing your computer means you are done. However, you are not. You must verify that what you think your program was supposed to do is what it actually did do. Did your program solve the problem it was intended to? Is the result correct?

So, you have to return to writing your original program and then compare that to the output your program gives. If your intended result matches, your program is correct; only then are you done.

As we get further into writing more complex programs, we will discover that a proper or good program exhibits each of the following qualities:

  • Correct: The program does what it's supposed to do.
  • Complete: The program does everything it's supposed to do.
  • Concise: The program does no more than it's supposed to do and it does so as efficiently as possible.
  • Clear: The program is easily understandable to those who use it and to those who must maintain it.

For most of this book, we will concern ourselves largely with correctness, completeness, and clarity. Currently, hello1.c is not complete, nor clear, and we will understand why shortly.

Repeat

Unlike our shampoo metaphor that we mentioned earlier, which was wet hair, lather, rinse, and repeat, instead of repeating the instructions just once, you will repeat this cycle multiple times.

Rarely will you be able to go through the program development cycle with only one iteration. Most likely, you have to will repeat parts of it many more times. For example, you might edit the source code, compile it, and find that the compiler failed. In this scenario, you will have to go back to edit the source code and compile it, each time figuring out what went wrong and then fixing it. Once it compiles successfully, you will move on to running and verifying it. If the output is not correct or the program crashes, you will have to figure out what went wrong and start editing the source code again.

Does this sound frustrating? It can be – especially when you don't know why something went wrong or you can't figure out what the compiler or computer is saying is wrong.

Many years ago, when compilers were simpler and not as forgiving as they are today (actually, compilers are still not very forgiving – they've just gotten better at figuring out what we humans might have done wrong with our programs and communicating it to us in better ways!), the very first time I attempted to compile my Hello, world! program on a Digital Equipment Virtual Address Extension (VAX) Virtual Memory System (VMS) C compiler, the compiler gave me 23 thousand error messages. It turns out that I had overlooked a single ; character somewhere. One character. Sheesh!

The point of that story is to reassure you that you will make mistakes, mostly missing or erroneous punctuation or misspelled variables, and you will get frustrated. Part of learning to program is learning how to deal with your frustration and how to become a sleuth to track down the picayune errors that will crop up.

When that happens, get up and take a break from the computer. Take a walk. Have a laugh. Then, get back to work. Don't omit the first parts (that is, laughing and walking around a bit).

A note about debugging

As you go through the program development cycle, and as you get more familiar with the development language, the development tools, and yourself (yes, you are learning about yourself while you program), this will all become second nature to you, as it should. When you make a typing error, or when you get an obviously incorrect result, these are not bugs – they are just mistakes. Bugs are far more subtle.

There is a deeper trap that is very difficult for most beginner programmers to see; that is, their assumptions about what should happen without any evidence of what did happen. Most of the most difficult bugs that I introduced in my code were those that I assumed the program would work on in a certain way, but I did not verify that. When I finally went back to my assumptions and proved them in code, I was able to get beyond my self-imposed bugs.

So, can you avoid this trap?

Yes. Throughout this book, we will attempt to mitigate this subtle problem with a method we will use to develop programs. As we proceed, we will use trial and error, guided discovery, and evidence through observation. Sometimes, we will purposefully break our programs to see what happens. Additionally, we will try to prove each concept so that the expected behavior matches the actual behavior.

This is not to say that even with such an approach, bugs won't creep in. They will. But with careful attention to your assumptions, observed behavior, and the collection of evidence that you have gathered to prove any assumption, most bugs can be avoided.

Now that we have introduced the program development cycles, you are now ready to write your first program. As you progress through this book, you will become very familiar with these cycles.

Previous PageNext Page
You have been reading a chapter from
Learn C Programming. - Second Edition
Published in: Aug 2022Publisher: PacktISBN-13: 9781801078450
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €14.99/month. Cancel anytime

Author (1)

author image
Jeff Szuhay

Jeff Szuhay is the principal developer at QuarterTil2 which specializes in graphics-rich software chronographs for desktop environments. In his software career of over 35 years, he has engaged in a full range of development activities from systems analysis and systems performance tuning to application design, from initial development through full testing and final delivery. Throughout that time, he has taught computer applications and programming languages at various educational levels from elementary school students to university students, as well as developed and presented professional, on-site training.
Read more about Jeff Szuhay