Which of the following is the directive you must use to access files from a C++ program

This C Beginner's Handbook follows the 80/20 rule. You'll learn 80% of the C programming language in 20% of the time.

This approach will give you a well-rounded overview of the language.

This handbook does not try to cover everything under the sun related to C. It focuses on the core of the language, trying to simplify the more complex topics.

And note: You can get a PDF and ePub version of this C Beginner's Handbook here.

Enjoy!

Table of Contents

  1. Introduction to C
  2. Variables and types
  3. Constants
  4. Operators
  5. Conditionals
  6. Loops
  7. Arrays
  8. Strings
  9. Pointers
  10. Functions
  11. Input and output
  12. Variables scope
  13. Static variables
  14. Global variables
  15. Type definitions
  16. Enumerated Types
  17. Structures
  18. Command line parameters
  19. Header files
  20. The preprocessor
  21. Conclusion

Introduction to C

C is probably the most widely known programming language. It is used as the reference language for computer science courses all over the world, and it's probably the language that people learn the most in school along with Python and Java.

I remember it being my second programming language ever, after Pascal.

C is not just what students use to learn programming. It's not an academic language. And I would say it's not the easiest language, because C is a rather low level programming language.

Today, C is widely used in embedded devices, and it powers most of the Internet servers, which are built using Linux. The Linux kernel is built using C, and this also means that C powers the core of all Android devices. We can say that C code runs a good portion of the entire world. Right now. Pretty remarkable.

When it was created, C was considered a high level language, because it was portable across machines. Today we kind of take for granted that we can run a program written on a Mac on Windows or Linux, perhaps using Node.js or Python.

Once upon a time, this was not the case at all. What C brought to the table was a language that was simple to implement and that had a compiler that could be easily ported to different machines.

I said compiler: C is a compiled programming language, like Go, Java, Swift or Rust. Other popular programming language like Python, Ruby or JavaScript are interpreted. The difference is consistent: a compiled language generates a binary file that can be directly executed and distributed.

C is not garbage collected. This means we have to manage memory ourselves. It's a complex task and one that requires a lot of attention to prevent bugs, but it is also what makes C ideal to write programs for embedded devices like Arduino.

C does not hide the complexity and the capabilities of the machine underneath. You have a lot of power, once you know what you can do.

I want to introduce the first C program now, which we'll call "Hello, World!"

hello.c

#include 

int main[void] {
    printf["Hello, World!"];
}

Let's describe the program source code: we first import the stdio library [the name stands for standard input-output library].

This library gives us access to input/output functions.

C is a very small language at its core, and anything that's not part of the core is provided by libraries. Some of those libraries are built by normal programmers, and made available for others to use. Some other libraries are built into the compiler. Like stdio and others.

stdio is the library that provides the printf[] function.

This function is wrapped into a main[] function. The main[] function is the entry point of any C program.

But what is a function, anyway?

A function is a routine that takes one or more arguments, and returns a single value.

In the case of main[], the function gets no arguments, and returns an integer. We identify that using the void keyword for the argument, and the int keyword for the return value.

The function has a body, which is wrapped in curly braces. Inside the body we have all the code that the function needs to perform its operations.

The printf[] function is written differently, as you can see. It has no return value defined, and we pass a string, wrapped in double quotes. We didn't specify the type of the argument.

That's because this is a function invocation. Somewhere, inside the stdio library, printf is defined as

int printf[const char *format, ...];

You don't need to understand what this means now, but in short, this is the definition. And when we call printf["Hello, World!"];, that's where the function is run.

The main[] function we defined above:

#include 

int main[void] {
    printf["Hello, World!"];
}

will be run by the operating system when the program is executed.

How do we execute a C program?

As mentioned, C is a compiled language. To run the program we must first compile it. Any Linux or macOS computer already comes with a C compiler built-in. For Windows, you can use the Windows Subsystem for Linux [WSL].

In any case, when you open the terminal window you can type gcc, and this command should return an error saying that you didn't specify any file:

That's good. It means the C compiler is there, and we can start using it.

Now type the program above into a hello.c file. You can use any editor, but for the sake of simplicity I'm going to use the nano editor in the command line:

Type the program:

Now press ctrl-X to exit:

Confirm by pressing the y key, then press enter to confirm the file name:

That's it, we should be back to the terminal now:

Now type

gcc hello.c -o hello

The program should give you no errors:

but it should have generated a hello executable. Now type

./hello

to run it:

I prepend ./ to the program name to tell the terminal that the command is in the current folder

Awesome!

Now if you call ls -al hello, you can see that the program is only 12KB in size:

This is one of the pros of C: it's highly optimized, and this is also one of the reasons it's this good for embedded devices that have a very limited amount of resources.

Variables and types

C is a statically typed language.

This means that any variable has an associated type, and this type is known at compilation time.

This is very different than how you work with variables in Python, JavaScript, PHP and other interpreted languages.

When you create a variable in C, you have to specify the type of a variable at the declaration.

In this example we initialize a variable age with type int:

int age;

A variable name can contain any uppercase or lowercase letter, can contain digits and the underscore character, but it can't start with a  digit. AGE and Age10 are valid variable names, 1age is not.

You can also initialize a variable at declaration, specifying the initial value:

int age = 37;

Once you declare a variable, you are then able to use it in your program code. You can change its value at any time, using the = operator for example, like in age = 100; [provided the new value is of the same type].

In this case:

#include 

int main[void] {
    int age = 0;
    age = 37.2;
    printf["%u", age];
}

the compiler will raise a warning at compile time, and will convert the decimal number to an integer value.

The C built-in data types are int, char, short, long, float, double, long double. Let's find out more about those.

Integer numbers

C provides us the following types to define integer values:

  • char
  • int
  • short
  • long

Most of the time, you'll likely use an int to store an integer. But in some cases, you might want to choose one of the other 3 options.

The char type is commonly used to store letters of the ASCII chart, but it can be used to hold small integers from -128 to 127. It takes at least 1 byte.

int takes at least 2 bytes. short takes at least 2 bytes. long takes at least 4 bytes.

As you can see, we are not guaranteed the same values for different environments. We only have an indication. The problem is that the exact numbers that can be stored in each data type depends on the implementation and the architecture.

We're guaranteed that short is not longer than int. And we're guaranteed long is not shorter than int.

The ANSI C spec standard determines the minimum values of each type, and thanks to it we can at least know what's the minimum value we can expect to have at our disposal.

If you are programming C on an Arduino, different board will have different limits.

On an Arduino Uno board, int stores a 2 byte value, ranging from -32,768 to 32,767. On a Arduino MKR 1010, int stores a 4 bytes value, ranging from -2,147,483,648 to 2,147,483,647. Quite a big difference.

On all Arduino boards, short stores a 2 bytes value, ranging from -32,768 to 32,767. long store 4 bytes, ranging from -2,147,483,648 to 2,147,483,647.

Unsigned integers

For all the above data types, we can prepend unsigned to start the range at 0, instead of a negative number. This might make sense in many cases.

  • unsigned char will range from 0 to at least 255
  • unsigned int will range from 0 to at least 65,535
  • unsigned short will range from 0 to at least 65,535
  • unsigned long will range from 0 to at least 4,294,967,295

The problem with overflow

Given all those limits, a question might come up: how can we make sure our numbers do not exceed the limit? And what happens if we do exceed the limit?

If you have an unsigned int number at 255, and you increment it, you'll get 256 in return. As expected. If you have an unsigned char number at 255, and you increment it, you'll get 0 in return. It resets starting from the initial possible value.

If you have a unsigned char number at 255 and you add 10 to it, you'll get the number 9:

#include 

int main[void] {
  unsigned char j = 255;
  j = j + 10;
  printf["%u", j]; /* 9 */
}

If you don't have a signed value, the behavior is undefined. It will basically give you a huge number which can vary, like in this case:

#include 

int main[void] {
  char j = 127;
  j = j + 10;
  printf["%u", j]; /* 4294967177 */
}

In other words, C does not protect you from going over the limits of a type. You need to take care of this yourself.

Warnings when declaring the wrong type

When you declare the variable and initialize it with the wrong value, the gcc compiler [the one you're probably using] should warn you:

#include 

int main[void] {
  char j = 1000;
}
hello.c:4:11: warning: implicit conversion 
  from 'int' to
      'char' changes value from 1000 to -24
      [-Wconstant-conversion]
        char j = 1000;
             ~   ^~~~
1 warning generated.

And it also warns you in direct assignments:

#include 

int main[void] {
  char j;
  j = 1000;
}

But not if you increase the number using, for example, +=:

#include 

int main[void] {
  char j = 0;
  j += 1000;
}

Floating point numbers

Floating point types can represent a much larger set of values than integers can, and can also represent fractions, something that integers can't do.

Using floating point numbers, we represent numbers as decimal numbers times powers of 10.

You might see floating point numbers written as

  • 1.29e-3
  • -2.3e+5

and in other seemingly weird ways.

The following types:

  • float
  • double
  • long double

are used to represent numbers with decimal points [floating point types]. All can represent both positive and negative numbers.

The minimum requirements for any C implementation is that float can represent a range between 10^-37 and 10^+37, and is typically implemented using 32 bits. double can represent a bigger set of numbers. long double can hold even more numbers.

The exact figures, as with integer values, depend on the implementation.

On a modern Mac, a float is represented in 32 bits, and has a precision of 24 significant bits. 8 bits are used to encode the exponent.

A double number is represented in 64 bits, with a precision of 53 significant bits. 11 bits are used to encode the exponent.

The type long double is represented in 80 bits, has a precision of 64 significant bits. 15 bits are used to encode the exponent.

On your specific computer, how can you determine the specific size of the types? You can write a program to do that:

#include 

int main[void] {
  printf["char size: %lu bytes\n", sizeof[char]];
  printf["int size: %lu bytes\n", sizeof[int]];
  printf["short size: %lu bytes\n", sizeof[short]];
  printf["long size: %lu bytes\n", sizeof[long]];
  printf["float size: %lu bytes\n", sizeof[float]];
  printf["double size: %lu bytes\n", 
    sizeof[double]];
  printf["long double size: %lu bytes\n", 
    sizeof[long double]];
}

In my system, a modern Mac, it prints:

char size: 1 bytes
int size: 4 bytes
short size: 2 bytes
long size: 8 bytes
float size: 4 bytes
double size: 8 bytes
long double size: 16 bytes

Constants

Let's now talk about constants.

A constant is declared similarly to variables, except it is prepended with the const keyword, and you always need to specify a value.

Like this:

const int age = 37;

This is perfectly valid C, although it is common to declare constants uppercase, like this:

const int AGE = 37;

It's just a convention, but one that can greatly help you while reading or writing a C program as it improves readability. Uppercase name means constant, lowercase name means variable.

A constant name follows the same rules for variable names: can contain any uppercase or lowercase letter, can contain digits and the underscore character, but it can't start with a digit. AGE and Age10 are valid variable names, 1AGE is not.

Another way to define constants is by using this syntax:

#define AGE 37

In this case, you don't need to add a type, and you don't also need the = equal sign, and you omit the semicolon at the end.

The C compiler will infer the type from the value specified, at compile time.

Operators

C offers us a wide variety of operators that we can use to operate on data.

In particular, we can identify various groups of operators:

  • arithmetic operators
  • comparison operators
  • logical operators
  • compound assignment operators
  • bitwise operators
  • pointer operators
  • structure operators
  • miscellaneous operators

In this section I'm going to detail all of them, using 2 imaginary variables a and b as examples.

I am keeping bitwise operators, structure operators and pointer operators out of this list, to keep things simpler

Arithmetic operators

In this macro group I am going to separate binary operators and unary operators.

Binary operators work using two operands:

OperatorNameExample
= Assignment a = b
+ Addition a + b
- Subtraction a - b
* Multiplication a * b
/ Division a / b
% Modulo a % b

Unary operators only take one operand:

OperatorNameExample
+ Unary plus +a
- Unary minus -a
++ Increment a++ or ++a
-- Decrement a-- or --a

The difference between a++ and ++a is that a++ increments the a variable after using it. ++a increments the a variable before using it.

For example:

int a = 2;
int b;
b = a++ /* b is 2, a is 3 */
b = ++a /* b is 4, a is 4 */

The same applies to the decrement operator.

Comparison operators

OperatorNameExample
== Equal operator a == b
!= Not equal operator a != b
> Bigger than a > b
= Bigger than or equal to a >= b

Chủ Đề