C Programming For Beginners

Learn C in ten easy steps on Windows, Mac OS X or Linux
4.2 (743 ratings)
Instead of using a simple lifetime average, Udemy calculates a
course's star rating by considering a number of different factors
such as the number of ratings, the age of ratings, and the
likelihood of fraudulent ratings.
9,808 students enrolled Bestselling in C
80% off
Take This Course
  • Lectures 73
  • Length 8 hours
  • Skill Level All Levels
  • Languages English
  • Includes Lifetime access
    30 day money back guarantee!
    Available on iOS and Android
    Certificate of Completion
Wishlisted Wishlist

How taking a course works


Find online courses made by experts from around the world.


Take your courses with you and learn anywhere, anytime.


Learn and practice real-world skills and achieve your goals.

About This Course

Published 10/2013 English

Course Description

The fastest, easiest way to learn to program C on a Mac or Windows. This course will teach you to program the C language from the ground up. You will learn everything from the very fundamentals of programming right through to the complexities of pointers, addresses and File IO. Maybe you've tried to master C before but failed. Or maybe you are new to C or new to programming. If so, this is the course for you!

C is one of the most important of all programming languages. It is used to program desktop applications, compilers, tools and utilities and even hardware devices. The C language is fast and efficient – but it can be hard to learn. Unless you use this course. This course begins with a gentle introduction to C but quickly moves on to explain some of its most confusing features: everything from C's 'scoping' rules to the curious connection between arrays and memory addresses. By the end of the course you will have a deep understanding both of the C language itself and also of the underlying 'architecture' of your computer.

What you will learn:

  • The fundamentals of programming – from the ground up
  • How to program on a Mac or on Windows
  • The nitty-gritty details of the C language
  • Advanced topics such as memory allocation, the stack and heap, and binary file IO

Who should take the course

  • Beginners – if you've never coded before, you can learn C step by step
  • Programmers switching to C from some other language such as Java, Ruby or Python
  • Cross-platform developers – there are C compilers for all major operating systems
  • Anyone who needs to program C++ or Objective-C. The C language is the place to start

What are the requirements?

  • A C compiler and code editor (both are available free)

What am I going to get from this course?

  • Master C programming concepts from the ground up
  • Use the source code examples to learn step-by-step
  • Understand the special features of C: pointers, header files, null-terminated strings, buffers, IO
  • Videos explain everything in minute detail
  • Read the supplied eBook, The Little Book Of C, to explore the topics in even more depth
  • Test your understanding with end-of-section quizzes

Who is the target audience?

  • Newcomers to programming
  • Programmers of other languages who want a fast way into C
  • Anyone who needs to master C as a basis for using C++ or Objective-C

What you get with this course?

Not for you? No problem.
30 day money back guarantee.

Forever yours.
Lifetime access.

Learn on the go.
Desktop, iOS and Android.

Get rewarded.
Certificate of completion.


Section 1: Getting Ready

C is an important cross-platform programming language. In this video, I provide a quick introduction to the language and how to learn it using this course.


In order to write C programs you will need a suitable editor or IDE (Integrated Development Environment). Here are a few suggestions…

23 pages

This is a PDF document containing answers to a number of common questions that have been asked by students.


In this course I will generally use the free CodeLite C editor which is available for OS X, Windows and Linux. This video shows how to get CodeLite installed on your computer.


If you are using a Mac you may need to download some additional tools in order that an editor such as CodeLite is able to find a compiler to build and run your programs.


Let’s take a quick look at the features of the CodeLite editor – from syntax colouring to keyboard shortcuts. CodeLite makes light work of creating C projects on Windows and OS X.


Maybe you are already use the NetBeans IDE for Java programming. Or maybe you'd just like to use NetBeans as your C environment. Here I explain how to get up and running.


There are various ways of importing source code into NetBeans. Here I show a simple way of creating a NetBeans C project using the files from one of my sample projects.


You don't need to use an IDE at all. If you are having problems installing an IDE or if you'd prefer to use a simple text editor, you can do so – and compile your programs in a System or Terminal window.


If you are using Windows, Microsoft’s Visual Studio (even the free edition) may be used to create C projects. Here I explain how to do that.

92 pages

The Little Book Of C is the course eBook. There is a chapter for each step of the course. Use the book, the source code and the videos together for a full understanding of the topics discussed.

C Source Code Archive
3 questions

Getting ready for C programming

Section 2: C programming basics

Here I take a look at a simple program that just displays “Hello world” and discover that even a few lines of C code illustrate a number of important features of the C language.


Some “Hello world” programs are more complex than others. Here I look at a program that takes some data as ‘arguments’ and returns a value.


Here I explain how to pass arguments from the commandline (or Terminal) to your program.


If you don’t know how to open a command window on Windows or the Terminal on OS X and use it to run your programs, this lesson explains all.


The printf() function lets you display the output from your program. It’s a very useful function but must be used with care – as I explain here.


You can document your code with comments that are ignored by the compiler. Here I explain two types of comment.

3 questions

Fundamental features of a C program

Section 3: Variables, constants and types

Variables are identifiers whose values may vary during the running of your program. This video explains the basics of variables and their types in C.


You may do calculations with both full numbers – integers – and fractional numbers – floating points. But be careful: the end results may not be what you expect!


If you want to create identifiers to store values that are not expected to change during the execution of a program, you can #define them.


Identifiers that are created using #define are often used as ‘constants’ – but, in fact, there is an alternative – using the keyword ‘const’. Here I explain the difference.


What should you call your variables and constants? Here I consider some of the naming conventions adopted by man C programmers.

3 questions

How to declare and use variables and constants

Section 4: Operators, tests and user input

There are two ‘equals’ operators in C – one uses a single equals sign to assign a value to a variable. Another uses two equals signs to test for equality. Here I explain the difference.


You will frequently need to make comparisons between one value and some other value. C has a number of ‘relational operators’ to help you do this.


Some assignment operators in C perform a calculation prior to assigning the result to a variable. These are called ‘compound assignment operators’.


You can use ++ and – to add and subtract 1 from a variable. But be careful – you can put these operators either before or after a variable and the position matters!


There are times when you need to take different actions according to some test condition. Here I explain how to use if..else tests.


It may seem easy to get input with gets() but this may cause problems. The fgets() function is a safer alternative – but that too may cause its own problems, as I explain here.


Sometimes you may have more data lurking in the dark corners of your computer’s than you are expecting. Here I explain some of the mysteries of buffers and why they need to be flushed.


Here I look at two possible ways of writing a function that safely reads in characters entered at the command prompt and also flushes any unneeded characters from the buffer.


If you need to chain together conditions when making tests, you need to use C’s ‘logical operators’.

3 questions

Testing and assigning values

Section 5: Functions, arguments and switch

We’ve used functions from the very start of this course. In this lesson I explain more about what functions are and how they really work.


You can pass data to functions are arguments that are assigned to ‘named parameters’. Here I explain the nitty-gritty details of arguments .


There may be times when you need to take many different possible actions depending on the value of some variable. The switch statement can help out.


In this lesson I look at more examples of switch statements, including some options that are only available with some C compilers.

3 questions

Declaring functions and making multi-part tests with switch statements

Section 6: Arrays, loops and break

Arrays are sequential collections. You can use arrays to store lists of chars, ints and other types of data. Here I explain the fundamentals.


You can add elements to an array at the same time the array is declared. Here I show how to do this and I also explain how the results of calculations may change according to the ‘precedence’ of operators.


Sometimes you might want to run some code not for a predetermined number of times but for just as long as some condition remains true. You can use a ‘while’ loop to do this.


In some circumstances the code in a ‘while’ may never be run. If you want to ensure that your code is always run at least once, use a ‘do..while’ loop.


Sometimes it is useful to break out of a loop even if the loop’s test condition is not false. Here I explain how to use break in a ‘while’ or ‘for’ loop.


Sometimes you may want to break from a loop once but then continue running the loop afterwards. Here I explain the difference between ‘break’ and ‘continue’.


Arrays can have multiple dimensions to let you star arrays inside arrays. Here I explain how you can think of a two-dimensional array as being a bit like a spreadsheet with intersecting rows and columns.

3 questions

Creating and iterating over sequential lists

Section 7: Strings, chars and pointers

In order to understand strings in C you need to understand how computer memory can be represented by ‘addresses’ and how pointer variables can refer to those addresses.


Many programming languages have a dedicated string data-type. Here we revise the essential features of C strings and explain the significance of its lack of a string type.


At first sight there may seem to be no difference between an array of chars such as char str[] and a char-pointer such as char *str. In fact the difference is profound and important.


Here I look at some more examples of using arrays and pointers and consider why you can assign to a pointer variable but not to an array name.


How do you return strings from functions And we also look at the importance of understanding the ‘stack and ‘heap’ in your computer’s memory.


C compilers come with ready-to-use string functions. Here I look at some of the traditional functions as well as some more modern alternatives.


There are also functions that let you analyse individual characters in order to determine to which category each char belongs.


What is the difference between ‘x’ and “x”? They may look almost identical but, in fact, they are completely different – as this lesson explains.

3 questions

Strings and memory addresses

Section 8: Structs, enums, header files and scope

Let’s imagine that you want to create a catalogue of your CD collection in which each record contains a name, the artist name, the number of tacks and a user rating. Here I explain how structs can help.


C lets you define your own named types. This makes it possible to create type names for everything from an int to a string to a custom record or struct.


Enums let you created groups of named constants that can help to document your code. Here I explain their value – and their limitations.


What is the purpose of the ‘.h’ header files that most C programs include? Here I explain why header files are useful and how they are used during the compilation of your programs.


Here I look at an example of a header file that provides access to a set of functions and constants that I have written.


‘Scope’ defines the visibility of functions and variables to your code. Here I explain local and global scope and look at the scoping of two variables with the same name.


What is the scope of functions declared in external files – that is, functions that are in different files but the same project?


Sometimes you may want your functions to be ‘private’ – hidden from code in other files. Here I explain how static functions can do this, and I also explain static variables.


To understand better how the compiler and linker work and how the compiler may rely on information from header files, try compiling your projects at the system prompt.

3 questions

Delving deeper into C

Section 9: File-handling

In this step we look at file operations. In this video I explain how to open and close disk files in order to save and load data to and from them.


When you open a file you can use a short string to indicate the file ‘mode’. A file mode may make a file available for reading, writing or appending in text or binary format.


Here I go through the code in a sample project to show how text can be saved to and loaded from a file, how the file contents can be erased and how the file itself can be deleted.


Once you’ve opened a text file you may want to do something with the text it contains. In this video I show how to count the number of lines in a file.


Now you know how to read and write text files you can write programs to process the text in a variety of ways – for example, to search for words in a file or encrypt its contents.

3 questions

Opening and closing files for reading and writing

Section 10: Binary files and memory allocation

Not all files contain plain text. Some files may contain binary data – for example, if I were to save a CD database to disk, the data stored in each CD struct would have a binary representation. This video explains the basics.


Sometimes you need to allocate memory dynamically. But once you’ve finished with that memory you need to free it. This lesson gives an example of code that does this.


The C language provides a number of standard data types. Sometimes it is useful to treat one type as another type. In this lesson I explain the hows and whys of ‘type-casting’.


The final project in this course creates a database of CD structs that are saved in a binary file on disk. This video introduces you to this project.


Here I explain how to save a collection of records (structs) into a binary data file and how to calculate the number of records stored before allocating memory when reading them in again.


Here I explain how to create a new CD struct in memory and then append its data to the end of an existing binary file storing CD records.


Finally I show an example of how to find a record in a binary file and modify the data it contains. You can use the sample program as a basis for your own data-saving application.

3 questions

More on how C deals with pointers and memory allocation


We’ve covered a lot of ground in the course on C programming. Now it’s time to move on…

Students Who Viewed This Course Also Viewed

  • Loading
  • Loading
  • Loading

Instructor Biography

Huw Collingbourne, Director of Technology, SapphireSteel Software

Huw Collingbourne is the technology director at SapphireSteel Software, developers of the “Sapphire” Ruby IDE for Visual Studio and the “Amethyst” IDE for the Adobe Flash Platform. He is author of The Book Of Ruby from No Starch Press. He runs Bitwise Courses and teaches courses on a range of programming topics.

Huw has been a programmer for more than 30 years. He is a well-known technology writer in the UK. For over ten years he wrote the Delphi and Java programming column for PC Plus Magazine. He has also written numerous opinion and programming columns (including tutorials on C#, Smalltalk, ActionScript and Ruby) for a number of computer magazines, such as Computer Shopper, Flash & Flex Developer’s Magazine, PC Pro, and PC Plus. He is the author of the free ebook The Little Book of Ruby and is the editor of the online computing magazine Bitwise.

In the 1980s he was a pop music journalist and interviewed most of the New Romantic stars, such as Duran Duran, Spandau Ballet, Adam Ant, Boy George, and Depeche Mode. He is now writing a series of New Romantic murder mysteries.

At various times Huw has been a magazine publisher, editor, and TV broadcaster. He has an MA in English from the University of Cambridge and holds a 2nd dan black belt in aikido, a martial art which he teaches in North Devon, UK. The aikido comes in useful when trying (usually unsuccessfully) to keep his Pyrenean Mountain Dogs under some semblance of control.

Ready to start learning?
Take This Course

C Programming-Top 10 Tips

C Programming-Top 10 Tips

C is one of the most important and widely used of all programming languages. It is a powerful language that can be used not only to build general-purpose applications but also to write "low-level" programs that interact very closely with the computer hardware.

C lets the programmer do things that many other languages do not. This means that good C programmers are able to write clever, efficient code. However, there is a downside: while many other languages, such as C# or Java, may try to protect you from writing dangerous code that could crash your programs, C often lets you write just about any code you want-even allowing you to code in mistakes that will end in disaster. In some ways, developing in C is the programming equivalent of a high-wire act-without a safety net.

Experienced C programmers have all kinds of tricks to make the most of the C language. Here is a list of the top 10 tips for both new and experienced C programmers.

(Note - the source code is provided in a downloadable archive if you'd like to try them out.)

The code examples are supplied read-to-run for the NetBeans IDE. If you want to use them with another IDE, just import the code files or simply copy and paste the source code into an existing file.


Sometimes it is useful to store a function in a variable. This isn't a technique that is normally used in day-to-day programming, but it can be used to increase the modularity of a program by, for example, storing the function to be used in handling an event in the event's data (or control) structure.

The key here is to define a type, "pointer-to function-returning-something" and then use that as a variable declaration-it makes the code a lot easier to read. Let's consider a simple example. First I define a type PFC, which is a Pointer to a Function returning a Character:

typedef char (*PFC)();

This is then used to create a variable z:

PFC z;

I define a function a():

char a() {
return 'a';

The address of this function is then stored in z:

z = a;

Note that you don't need the & ("address-of") operator here; the compiler knows that a must be the address of a function. This is because there are only two things you can do with a function: 1) call it, or 2) take its address. Since the function isn't called (there are no parentheses after a in the assignment above), the only option is to use the address of the function, which is then stored in the variable z.

To call the function whose address is in z, just add the parentheses:

printf("I am %c\n", z());

Normally you declare a function to take a fixed number of arguments. But it is also possible to define functions capable of taking variable numbers of arguments. The standard C function printf() is a function of this sort. You can put almost any number of integers, floats, doubles, and strings in the format specifier part (after the string argument), and the printf() function will figure out what to do with them. Just like printf(), you can declare your own functions that contain a variable number of arguments.

Here is an example:

int vararg(int arg_count, ...) {}

The first argument here, arg_count, is an integer that gives the actual number of arguments that follow it in the "variable" argument list, which is shown by the three dots.

There are a few built-in functions or macros that deal with variable arguments: va_list, va_start, va_arg, and va_end (these are defined in the stdarg.h header file).

First, you need to declare a pointer to the variable arguments:

va_list argp;

Next, set this argp variable to the first argument in the variable part. This is the argument after the last fixed argument; here arg_count:

va_start(argp, arg_count);

Then we can extract each variable argument one at a time from the variable part using va_arg:

for (i = 0; i < arg_count; i++) {
     j = va_arg(argp, int);          // get the next argument
     t += j;

Note that you need to know in advance the type of the argument being retrieved (here it's a simple int) and the number of arguments (here, given by the fixed argument arg_count).

Finally, you need to tidy up by calling va_end:


"Bit-twiddling", or manipulating the individual bits of items such as integers, is sometimes considered to be a dark art used by advanced programmers. It's true that setting individual bit values can seem a rather obscure procedure. But it can be useful, and it is a technique that is well worth knowing.

Let's first discuss why you would want to do this. Programs often use "flag" variables to hold Boolean values (that is, true or false). So you might have a number of variables like these:

int moving;
int decelerating;
int accelerating;

If these are related in some way, as the ones above are (they all define the state of some action related to movement), then it's often more convenient to store all the information in a single "state variable" and use a single bit in that variable for each possible state, like this:

#define MOVING (1 << 1)
#define DECELERATING (1 << 2)
#define ACCELERATING (1 << 3)
int state;

Then you can use bit-setting operations to set or clear an individual bit:

state |= MOVING;
state &= ~MOVING;

The advantage is that all the state information is stored in one place and it's clear that you are operating on a single logical entity.

The code archive contains an example that shows how to set, clear, and test a single bit in a integer. If you don't understand exactly what is going on here, don't worry. The best way to think of these are as standard "incantations."

To set a given bit in an int called value (in the range 0 to 31), use this expression:

value |= 1 << bit

To clear a given bit, use this:

value &= ~(1 << bit);

And to test if a bit is zero or one, use this:

r = value & (1 << bit);

C's logical operators, && ("and") and || ("or"), let you chain together conditions when you want to take some action only when all of a set of conditions are true (&&) or when any one set of conditions is true (||). But C also provides the & and | operators. And it is vital that you understand the difference in how these work. In brief, the double-character operators (&& and ||) are called "short-circuit" operators. When used between two expressions, the second expression is only evaluated when the first expression is found to be true; otherwise it is skipped. Let's look at an example to clarify this:

FILE *f = 0;

int short_circuit_ok() {
     int t;

     t = (int)f && feof(f);
     return t;

The test (int)f && feof(f) is intended to return a true value when the end of the file f is reached. This test evaluates f first; and this will be zero (a false value) if the file has not been opened. This is an error, so trying to read to the end of the file is not possible. However, since the first part of the test fails, the second part will not be evaluated, so no attempt is made on feof() . This shows the correct use of a short circuit operator to test if a file had been opened before an operation on the file is tried. But, see this code:

int short_circuit_bad() {
     int t;
     t = (int)f & feof(f);
     return t;

Here, the test uses the & operator instead of &&. The & operator is an instruction to evaluate both expressions in all circumstances. So, even if the first part of the test fails (as the file hasn't been opened), the second part will be evaluated (to test for the end of the file). This could be disastrous and might cause a segmentation fault (or similar) because there is no control over the order of evaluation.

In fact, the way in which these expressions are evaluated is, to some extent, dependent on the compiler and optimizer. So, it is possible that some compilers might be smart enough to realize that this code can never succeed and therefore might not evaluate both parts of the test. Many compilers are not this smart, however; so a test of this sort in a C program, where the evaluation of one part is dependent on the other to be true, is a very bad idea!


A ternary operation is one that takes three arguments. In C the ternary operator (? :) can be used as a shorthand way of performing if..else tests. The syntax can be expressed like this:

< Test expression > ? < If true, execute this code> : < lse execute this code >

For example, given two int variables, t and items I could use if..else to test the value of items and assign its value to t like this:

if (items > 0) {
         t = items;
     } else {
         t = -items;

Using the ternary operator, I could rewrite that entire code in a single line, like this:

t = items > 0 ? items : -items;

If you aren't used to them, ternary operators may look a bit odd, but they can shorten and simplify your code.

Here's another example. This code displays the first string when there is a single item and the second string when there are multiple items:

if (items == 1) {
         printf("there is %d item\n", t);
     } else {
         printf("there are %d items\n", t);

This can be rewritten as follows:

printf("there %s %d item%s", t == 1 ? "is" : "are", t, t == 1 ? "\n" : "s\n");

A "stack" is a last-in, first-out storage system. You can use address arithmetic to add elements to a stack (pushing) or remove elements from the stack (popping). When programmers refer to "the stack", they typically mean the structure that is used by the C compiler to store local variables declared inside a function. But, in fact, a stack is a generic type of data structure that you can create and use in your own code, which is what I discuss here.

The code below defines a very small stack: an array _stack of 2 integers. Remember, when testing, it is always better to use small numbers of items rather than large numbers. If your code contains errors, these will be easier to spot in an array of two items rather than in array of 100 items. I also declare a stack pointer _sp and set it to the base (the address) of the _stack array:

#define STACK_SIZE 2
static int _stack[STACK_SIZE];
static int* _sp = _stack;

I now define the push() function, which pushes an integer onto the stack, just as you might add a plate onto a stack of plates. It returns the new number of items on the stack, or -1 if the stack is full:

int push(int value) {
     int count;

     count = _sp - _stack;
     if (count >= STACK_SIZE) {
         count = -1;
     } else {
         *_sp++ = value;
         count += 1;
     return count;

In order to get an item from the stack, I need a pop() function. Remember a stack is a last-in, first-out structure. If I have stacked up ten plates to be washed, I pull the first plate off the top of the stack (which was the last plate I put on the stack), wash it, and then take off the next plate (the last-but-one plate that I put on the stack) and so on. My pop() function does this with the elements stored in my _stack data structure. It returns the new number of items on the stack, or -1 if it is empty:

int pop(int* value) {
     int count;

     count = _sp - _stack;
     if (count == 0) {
         count = -1;
     } else {
         *value = *--_sp;
         count -= 1;
     return count;


And here is some code showing how to push and pop items onto and off the stack:

void test_stack() {
     int i, r, v;

     for (i = 0; i < 4; i++) {
         v = i + 10;
         r = push(v);
         printf("push returned %d; v was %d\n", r, v);

     for (i = 0; i < 4; i++) {
         v = 0;
         r = pop(&v);
         printf("pop returned %d; v was %d\n", r, v);


Stacks are handy, temporary storage structures. It's worth getting to know them!


Here are three ways of copying data. The first uses the standard C function, memcpy(), which copies n bytes from the src to the dst buffer:

void copy1(void *src, void *dst, int n) {
     memcpy(src, dst, n);

Now let's look at a do-it-yourself alternative to memcpy(). This could be useful if you wanted to do some more processing or checking of the copied data:

void copy2(void *src, void *dst, int n) {
     int i;
     char *p, *q;

     for (i = 0, p = src, q = dst; i < n; i++) {
         *p++ = *q++;

And finally, here is a function that uses 32-bit integers to achieve faster copying. Bear in mind that this may not be faster than the compiler can achieve if it makes optimizations that are particular to the machine architecture. However, it can be useful in a microcontroller where speed is often very important. In this particular example, the code assumes that the data count n is a multiple of 4 since it is dealing with 4-byte integers:

void copy3(void *src, void *dst, int n) {
    int i;
     int *p, *q;

     for (i = 0, p = (int*)src, q = (int*)dst; i < n / 4; i++) {
         *p++ = *q++;

You can find some examples of copying strings using these three functions in the code archive.


C uses "header" (".h") files that may contain declarations of functions and constants. A header file may be included in a C code file by importing it using its name between angle brackets when it is one of the headers supplied with your compiler (#include < string.h >) or between double-quotes when it is a header that you have written: (#include "mystring.h"). But in a complex program containing many source code files, there is the danger that you may include the same header file more than once.

Suppose we have a simple header file, header1.h, that contains the following definitions:

typedef int T;
typedef float F;

const int T_SIZE = sizeof(T);

Then we make another header (header2.h) that contains this:

#include "header1.h"
typedef struct {
     T t;
     F f;
} U;

const int U_SIZE = sizeof(U);

Now, if in our main program, main.c, we have this:

#include "header1.h"
#include "header2.h"

When we compile the program, we will get a compilation error, because T_SIZE will be declared twice (because its definition in header1 is included in two different files). We have to include header1 in header2 in order to get header2 to compile in circumstances where we don't use header1. So, how can we fix this problem? The way around this is to define a "guard" macro that encloses all of the definitions in a header file, so that header1 becomes:

#ifndef HEADER1_H
#define HEADER1_H

typedef int T;
typedef float F;

const int T_SIZE = sizeof(T);


This sort of problem is so common that many IDEs such as NetBeans will do this for you when you create a new header. If you create the header file yourself, however, you need to do this explicitly. To avoid this sort of error, you must make sure that all your header definitions are within the "guard" #ifdef.


A competent and experienced C programmer will neither overuse nor underuse parentheses-the round bracket delimiters "(" and ")". But what exactly is the correct way to use parentheses?

There are a number of simple rules:

1) To change the normal operator precedence.
For example, 3 * (4 + 3) is not the same as 3 * 4 + 3 .

2) To make things clearer. It isn't absolutely necessary to use parentheses here:

t = items > 0 ? items : -items;

That's because the operator precedence of || is lower than < and >. However, you might find it clearer to write this:

(x > 0) || (x < 100 & y > 10) || (y < 0)

Using parentheses for clarity is useful because not many people can correctly list all the C operator priorities.

3) In a macro expression. It is a good idea to add parentheses when defining a constant like this:

#define MYCONST (4 + 3)

That's because you don't know where this constant might be used. In the example above, if there were no parentheses, you may not get what you expect. Consider this:


The resulting value would be different (due to the effects of operator precedence) if you omitted the parentheses in the constant declaration.

But there's one place where you don't need to use parentheses: in a return statement. For example, this...

return (x + y);

...has exactly the same effect as

return x + y;

Many programmers make a habit of using unnecessary parentheses in return statements. This may be because they are used to placing expressions between parentheses in other control statements such as if, while, for, and do. All of those statements require parentheses. But a return statement does not.


Programmers who come to C from another language frequently get confused when C treats an array as an address and vice versa. After all, isn't an array supposed to be some sort of container with a fixed number of slots, each of which holds a single item? An address is just a number indicating a memory location; so an array and an address are very different things, right?

Well, no, not right, as it turns out.

C is correct: an array is just the base address of a block of memory, and the array notation you may have come across when learning a language, such as Java or JavaScript, is merely syntactic sugar.
Look carefully at the following code:

static int _x[4];

test_array_as_address() {
     int i;

     for (i = 0; i < 4; i++) {
         _x[i] = (int) (_x + i);

     for (i = 0; i < 4; i++) {
         printf("%x:%x:%x\n", _x + i, _x[i], *(_x + i));


Here, the first for loop copies the address of each individual array element into the array itself:

_x[i] = (int) (_x + i);

At each turn through the loop, the address is incremented by the value of i. So the address of the array variable _x will be the first element (since i is 0 at the first turn through the loop), and each subsequent address will be the address of _x plus 1. When we add 1 to the array's address, the C compiler calculates the appropriate offset to the next array element according to the data-type of the array (here, that's 4 bytes for an array of integers).

The second for loop prints the values stored in the array, first printing the address of the element _x + i, then the value of the element using normal array indexing _x[i], and finally the contents of the array using pointer/address notation (where the * operator returns the contents of the address placed in parentheses): *(_x + i). In all cases, the three values are the same. This shows that the array and its address are the same thing, and each element in the array has an address given by the address of the array, plus the number of bytes needed to store an element.

Incidentally, note that you don't need to use the & operator to get the address of the array, because, to the compiler, the array is an address.

You can download the source code here if you'd like to try these tips on your own.