delight !
Table of Contents
Introduction to C
C is credibly the most widely known scheduling language. It is used as the reference book language for calculator science courses all over the world, and it ‘s credibly the language that people learn the most in school along with Python and Java. I remember it being my second gear scheduling speech always, after Pascal. C is not just what students use to learn programming. It ‘s not an academician language. And I would say it ‘s not the easiest terminology, because C is a preferably first gear charge program language. today, C is widely used in embed devices, and it powers most of the Internet servers, which are built using Linux. The Linux kernel is built using C, and this besides means that C powers the core of all Android devices. We can say that C code runs a good share of the entire world. right nowadays. Pretty noteworthy. When it was created, C was considered a high level language, because it was portable across machines. today we kind of necessitate for granted that we can run a program written on a Mac on Windows or Linux, possibly using Node.js or Python. once upon a meter, this was not the lawsuit 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 compile program language, like Go, Java, Swift or Rust. other popular programming terminology like Python, Ruby or JavaScript are interpreted. The dispute is consistent : a compose terminology generates a binary star 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 undertaking and one that requires a set of care to prevent bugs, but it is besides what makes C ideal to write programs for embedded devices like Arduino. C does not hide the complexity and the capabilities of the car underneath. You have a lot of exponent, once you know what you can do. I want to introduce the first C plan now, which we ‘ll call “ Hello, World ! ” hello.c
#include
int main(void) {
printf("Hello, World!");
}
Let ‘s describe the course of study reference code : we first import the stdio
library ( the appoint stands for standard input-output library ). This library gives us access to input/output functions. C is a identical belittled speech at its core, and anything that ‘s not separate of the congress of racial equality 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()
officiate. This function is wrapped into a main()
affair. The main()
routine is the introduction point of any C program. But what is a function, anyhow ? A serve is a routine that takes one or more arguments, and returns a single value. In the subject 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 reappearance value. The affair has a body, which is wrapped in curly braces. Inside the torso we have all the code that the function needs to perform its operations. The printf()
officiate is written differently, as you can see. It has no return prize defined, and we pass a string, wrapped in double quotes. We did n’t specify the type of the argumentation. That ‘s because this is a function invocation. Somewhere, inside the stdio
library, printf
is defined as
int printf(const char *format, ...);
You do n’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 affair 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 compose language. To run the program we must first compile it. Any Linux or macOS calculator already comes with a C compiler built-in. For Windows, you can use the Windows Subsystem for Linux ( WSL ). In any casing, when you open the terminal window you can type gcc
, and this command should return an error saying that you did n’t specify any file : That ‘s beneficial. 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 program in the instruction line : Type the program :
now weight-lift
ctrl-X
to exit : confirm by pressing the
y
key, then imperativeness enroll to confirm the file mention : That ‘s it, we should be back to the terminal now :
now character
gcc hello.c -o hello
The program should give you no errors : but it should have generated a
hello
feasible. now type
./hello
to run it : I prepend
./
to the program name to tell the terminal that the command is in the current folder amazing ! now if you call ls -al hello
, you can see that the platform is only 12KB in size : This is one of the pros of C : it ‘s highly optimized, and this is besides one of the reasons it ‘s this estimable for embedded devices that have a very limited total of resources .
Variables and types
C is a statically type language. This means that any varying has an associated character, and this type is known at compilation time. This is very different than how you work with variables in Python, JavaScript, PHP and early rede languages. When you create a variable in C, you have to specify the type of a variable at the resolution. In this example we initialize a variable age
with type int
:
int age;
A varying appoint can contain any capital or small letter letter, can contain digits and the underline character, but it ca n’t start with a digit. AGE
and Age10
are valid variable names, 1age
is not. You can besides 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 platform code. You can change its value at any time, using the =
operator for example, like in age = 100;
( provided the new rate is of the lapp type ). In this character :
#include
int main(void) {
int age = 0;
age = 37.2;
printf("%u", age);
}
the compiler will raise a warn at roll up time, and will convert the decimal act to an integer measure. 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 probably 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 normally used to store letters of the ASCII chart, but it can be used to hold little 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 lapp values for different environments. We only have an indication. The problem is that the demand 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 minimal values of each type, and thanks to it we can at least know what ‘s the minimal value we can expect to have at our administration. 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 dispute. On all Arduino boards, short
stores a 2 bytes value, ranging from -32,768
to 32,767
. long
memory 4 bytes, ranging from -2,147,483,648
to 2,147,483,647
.
Unsigned integers
For all the above datum types, we can prepend unsigned
to start the range at 0, alternatively of a veto number. This might make sense in many cases .
unsigned char
will range from0
to at least255
unsigned int
will range from0
to at least65,535
unsigned short
will range from0
to at least65,535
unsigned long
will range from0
to at least4,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 restrict ? 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
total at 255, and you increment it, you ‘ll get 0 in return. It resets starting from the initial potential value. If you have a unsigned char
number at 255 and you add 10 to it, you ‘ll get the count 9
:
#include
int main(void) {
unsigned char j = 255;
j = j + 10;
printf("%u", j); /* 9 */
}
If you do n’t have a signed prize, the demeanor is undefined. It will basically give you a huge act 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 worry of this yourself .
Warnings when declaring the wrong type
When you declare the variable and initialize it with the improper rate, the gcc
compiler ( the one you ‘re credibly 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 besides warns you in direct assignments :
#include
int main(void) {
char j;
j = 1000;
}
But not if you increase the phone number using, for example, +=
:
#include
int main(void) {
char j = 0;
j += 1000;
}
Floating point numbers
Floating point types can represent a a lot larger set of values than integers can, and can besides represent fractions, something that integers ca n’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 early apparently eldritch ways. The adopt types :
float
double
long double
are used to represent numbers with decimal points ( floating point types ). All can represent both cocksure and negative numbers. The minimal 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 claim figures, as with integer values, depend on the implementation. On a modern Mac, a float
is represented in 32 bits, and has a preciseness of 24 meaning bits. 8 bits are used to encode the advocate. A double
number is represented in 64 bits, with a preciseness of 53 significant bits. 11 bits are used to encode the advocate. The type long double
is represented in 80 bits, has a preciseness of 64 significant bits. 15 bits are used to encode the advocate. On your specific calculator, how can you determine the specific size of the types ? You can write a broadcast 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 nowadays 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 barely a conventionality, but one that can greatly help you while reading or writing a C course of study as it improves readability. Uppercase diagnose means constant, small letter name means varying. A constant name follows the same rules for variable names : can contain any uppercase or small letter letter, can contain digits and the underline character, but it ca n’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 character, you do n’t need to add a type, and you do n’t besides need the =
equal sign of the zodiac, and you omit the semicolon at the end. The C compiler will infer the type from the value specified, at compose time .
Operators
C offers us a wide kind of operators that we can use to operate on data. In particular, we can identify respective 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 fanciful variables a
and b
as examples. I am keeping bitwise operators, structure operators and cursor operators out of this tilt, to keep things simple
Arithmetic operators
In this macro group I am going to separate binary star operators and unary operators. binary operators work using two operands :
Operator | Name | Example |
---|---|---|
= |
Assignment | a = b |
+ |
Addition | a + b |
- |
Subtraction | a - b |
* |
Multiplication | a * b |
/ |
Division | a / b |
% |
Modulo | a % b |
unary operators only take one operand :
Operator | Name | Example |
---|---|---|
+ |
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 exercise :
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 decrease operator .
Comparison operators
Operator | Name | Example |
---|---|---|
== |
Equal operator | a == b |
!= |
Not equal operator | a != b |
> |
Bigger than | a > b |
< |
Less than | a < b |
>= |
Bigger than or equal to | a >= b |
<= |
Less than or equal to | a <= b |
Logical operators
!
NOT (example:!a
)&&
AND (example:a && b
)||
OR (example:a || b
)
Those operators are great when working with boolean values .
Compound assignment operators
Those operators are utilitarian to perform an assignment and at the lapp time perform an arithmetical operation :
Operator | Name | Example |
---|---|---|
+= |
Addition assignment | a += b |
-= |
Subtraction assignment | a -= b |
*= |
Multiplication assignment | a *= b |
/= |
Division assignment | a /= b |
%= |
Modulo assignment | a %= b |
The ternary operator
The ternary hustler is the lone operator in C that works with 3 operands, and it ’ s a abruptly way to express conditionals. This is how it looks :
? :
Example :
a ? b : c
If a
is evaluated to true
, then the b
statement is executed, differently c
is. The three operator is functionality-wise lapp as an if/else conditional, except it is shorter to express and it can be inlined into an expression .
sizeof
The sizeof
operator returns the size of the operand you pass. You can pass a variable star, or even a type. case custom :
#include
int main(void) {
int age = 37;
printf("%ld\n", sizeof(age));
printf("%ld", sizeof(int));
}
Operator precedence
With all those operators ( and more, which I have n't covered in this post, including bitwise, structure operators, and pointer operators ), we must pay attention when using them in concert in a single formula. Suppose we have this mathematical process :
int a = 2;
int b = 4;
int c = b + a * a / b - a;
What 's the value of c
? Do we get the accession being executed before the generation and the division ? There is a located of rules that help us solve this puzzle. In ordain from less priority to more precedence, we have :
- the
=
assignment operator - the
+
and-
binary operators - the
*
and/
operators - the
+
and-
unary operators
Operators besides have an associativity rule, which is always left to right except for the unary operators and the appointment. In :
int c = b + a * a / b - a;
We inaugural execute a * a / b
, which, due to being left-to-right, we can separate into a * a
and the solution / b
: 2 * 2 = 4
, 4 / 4 = 1
. then we can perform the total and the subtraction : 4 + 1 - 2. The respect of c
is 3
. In all cases, however, I want to make certain you realize you can use parentheses to make any alike expression easier to read and comprehend. Parentheses have higher precedence over anything else. The above example expression can be rewritten as :
int c = b + ((a * a) / b) - a;
and we do n't have to think about it that a lot .
Conditionals
Any scheduling lyric provides the programmers the ability to perform choices. We want to do X in some cases, and Y in other cases. We want to check data, and make choices based on the state of that data. C provides us 2 ways to do so. The first gear is the if
affirmation, with its else
assistant, and the second is the switch
statement .
if
In an if
statement, you can check for a stipulate to be true, and then execute the block provided in the curly brackets :
int a = 1;
if (a == 1) {
/* do something */
}
You can append an else
obstruct to execute a different auction block if the original condition turns out to be false :
int a = 1;
if (a == 2) {
/* do something */
} else {
/* do something else */
}
Beware of one common source of bugs - constantly use the comparison operator ==
in comparisons, and not the grant operator =
. If you do n't, the if
conditional check will always be true, unless the argument is 0
, for example if you do :
int a = 0;
if (a = 0) {
/* never invoked */
}
Why does this happen ? Because the conditional check will look for a boolean consequence ( the consequence of a comparison ), and the 0
number constantly equates to a false value. Everything else is true, including negative numbers. You can have multiple else
blocks by stacking together multiple if
statements :
int a = 1;
if (a == 2) {
/* do something */
} else if (a == 1) {
/* do something else */
} else {
/* do something else again */
}
switch
If you need to do besides many if / else / if blocks to perform a check, possibly because you need to check the exact rate of a varying, then switch
can be very utilitarian to you. You can provide a variable as condition, and a series of case
entrance points for each prize you expect :
int a = 1;
switch (a) {
case 0:
/* do something */
break;
case 1:
/* do something else */
break;
case 2:
/* do something else */
break;
}
We need a break
keyword at the end of each case to avoid the adjacent case being executed when the one before ends. This `` cascade '' effect can be useful in some creative ways. You can add a `` catch-all '' case at the end, labeled default
:
int a = 1;
switch (a) {
case 0:
/* do something */
break;
case 1:
/* do something else */
break;
case 2:
/* do something else */
break;
default:
/* handle all the other cases */
break;
}
Loops
C offers us three ways to perform a loop : for loops, while loops and do while loops. They all allow you to iterate over arrays, but with a few differences. Let 's see them in detail .
For loops
The first and credibly most common room to perform a loop is for loops. Using the for
keyword we can define the rules of the loop up front, and then provide the blockage that is going to be executed repeatedly. Like this :
for (int i = 0; i <= 10; i++) {
/* instructions to be repeated */
}
The (int i = 0; i <= 10; i++)
forget contains 3 parts of the loop details :
- the initial condition (
int i = 0
) - the test (
i <= 10
) - the increment (
i++
)
We foremost define a loop variable, in this case named i
. i
is a common variable name to be used for loops, along with j
for cuddle loops ( a cringle inside another loop ). It 's equitable a convention. The variable is initialized at the 0 measure, and the first iteration is done. then it is incremented as the increase part says ( i++
in this case, incrementing by 1 ), and all the motorbike repeats until you get to the total 10. Inside the loop independent jam we can access the variable i
to know at which iteration we are. This program should print 0 1 2 3 4 5 5 6 7 8 9 10
:
for (int i = 0; i <= 10; i++) {
/* instructions to be repeated */
printf("%u ", i);
}
Loops can besides start from a high number, and go a lower phone number, like this :
for (int i = 10; i > 0; i--) {
/* instructions to be repeated */
}
You can besides increment the loop variable by 2 or another value :
for (int i = 0; i < 1000; i = i + 30) {
/* instructions to be repeated */
}
While loops
While loops is simpler to write than a for
loop, because it requires a bit more workplace on your share. alternatively of defining all the loop data up front when you start the loop, like you do in the for
loop, using while
you merely check for a condition :
while (i < 10) {
}
This assumes that i
is already defined and initialized with a value. And this closed circuit will be an infinite loop unless you increment the i
variable star at some luff inside the loop topology. An infinite loop is bad because it will block the plan, allowing nothing else to happen. This is what you need for a `` right '' while loop :
int i = 0;
while (i < 10) {
/* do something */
i++;
}
There 's one exception to this, and we 'll see it in one minute. Before, let me introduce do while
.
Do while loops
While loops are bang-up, but there might be times when you need to do one particular thing : you want to constantly execute a block, and then possibly repeat it. This is done using the do while
keyword. In a way it 's identical alike to a while
iteration, but slenderly different :
int i = 0;
do {
/* do something */
i++;
} while (i < 10);
The block that contains the /* do something */
remark is always executed at least once, regardless of the circumstance control at the bottom. then, until i
is less than 10, we 'll repeat the block .
Breaking out of a loop using break
In all the C loops we have a way to break out of a loop at any point in clock, immediately, careless of the conditions set for the coil. This is done using the break
keyword. This is utilitarian in many cases. You might want to check for the value of a variable, for exercise :
for (int i = 0; i <= 10; i++) {
if (i == 4 && someVariable == 10) {
break;
}
}
Having this option to break out of a loop is particularly interest for while
loops ( and do while
besides ), because we can create apparently infinite loops that end when a condition occurs. You define this inside the coil block :
int i = 0;
while (1) {
/* do something */
i++;
if (i == 10) break;
}
It 's preferably common to have this kind of loop in C .
Arrays
An array is a variable star that stores multiple values. Every value in the array, in C, must have the same type. This means you will have arrays of int
values, arrays of double
values, and more. You can define an array of int
values like this :
int prices[5];
You must always specify the size of the array. C does not provide dynamic arrays out of the box ( you have to use a data structure like a linked list for that ). You can use a ceaseless to define the size :
const int SIZE = 5;
int prices[SIZE];
You can initialize an array at definition time, like this :
int prices[5] = { 1, 2, 3, 4, 5 };
But you can besides assign a value after the definition, in this way :
int prices[5];
prices[0] = 1;
prices[1] = 2;
prices[2] = 3;
prices[3] = 4;
prices[4] = 5;
Or, more hardheaded, using a cringle :
int prices[5];
for (int i = 0; i < 5; i++) {
prices[i] = i + 1;
}
And you can reference an detail in the range by using square brackets after the array varying mention, adding an integer to determine the index rate. Like this :
prices[0]; /* array item value: 1 */
prices[1]; /* array item value: 2 */
Array indexes start from 0, then an array with 5 items, like the prices
array above, will have items ranging from prices[0]
to prices[4]
. The concern thing about C arrays is that all elements of an array are stored consecutive, one right after another. not something that normally happens with higher-level program languages. Another matter to thing is this : the variable list of the range, prices
in the above example, is a pointer to the first gear element of the align. As such it can be used like a normal cursor. More on pointers soon .
Strings
In C, strings are one special kind of array : a string is an range of char
values :
char name[7];
I introduced the char
type when I introduced types, but in short it is normally used to store letters of the ASCII chart. A string can be initialized like you initialize a normal array :
char name[7] = { "F", "l", "a", "v", "i", "o" };
Or more handily with a string literal ( besides called string constant ), a sequence of characters enclosed in doubly quotes :
char name[7] = "Flavio";
You can print a string via printf()
using %s
:
printf("%s", name);
Do you notice how `` Flavio '' is 6 chars farseeing, but I defined an array of distance 7 ? Why ? This is because the last character in a string must be a 0
value, the string exterminator, and we must make outer space for it. This is authoritative to keep in mind specially when manipulating strings. Speaking of manipulating strings, there 's one important criterion library that is provided by C : string.h
. This library is essential because it abstracts many of the low level details of working with strings, and provides us with a sic of useful functions. You can load the library in your plan by adding on top :
#include
And once you do that, you have entree to :
strcpy()
to copy a string over another stringstrcat()
to append a string to another stringstrcmp()
to compare two strings for equalitystrncmp()
to compare the firstn
characters of two stringsstrlen()
to calculate the length of a string
and many, many more .
Pointers
Pointers are one of the most confusing/challenging parts of C, in my opinion. particularly if you are new to program, but besides if you come from a higher flush programming terminology like Python or JavaScript. In this section I want to introduce them in the simplest yet not-dumbed-down way potential. A pointer is the address of a block of memory that contains a variable. When you declare an integer numeral like this :
int age = 37;
We can use the &
operator to get the respect of the address in memory of a variable :
printf("%p", &age); /* 0x7ffeef7dcb9c */
I used the %p
format specified in printf()
to print the savoir-faire value. We can assign the address to a varying :
int *address = &age
Using int *address
in the declaration, we are not declaring an integer variable, but rather a pointer to an integer. We can use the cursor operator *
to get the prize of the variable an address is pointing to :
int age = 37;
int *address = &age
printf("%u", *address); /* 37 */
This time we are using the pointer hustler again, but since it 's not a announcement this time it means `` the value of the variable star this arrow points to ''. In this exercise we declare an age
varying, and we use a pointer to initialize the value :
int age;
int *address = &age
*address = 37;
printf("%u", *address);
When working with C, you 'll find that a fortune of things are built on crown of this simple concept. so form certain you familiarize with it a bite by running the above examples on your own. Pointers are a great opportunity because they force us to think about memory addresses and how data is organized. Arrays are one case. When you declare an array :
int prices[3] = { 5, 4, 3 };
The prices
variable is actually a pointer to the inaugural token of the range. You can get the value of the first item using this printf()
function in this subject :
printf("%u", *prices); /* 5 */
The cool thing is that we can get the moment detail by adding 1 to the prices
cursor :
printf("%u", *(prices + 1)); /* 4 */
And indeed on for all the other values. We can besides do many nice string handling operations, since strings are arrays under the hood. We besides have many more applications, including passing the reference of an object or a function around to avoid consuming more resources to copy it .
Functions
Functions are the way we can structure our code into subroutines that we can :
- give a name to
- call when we need them
Starting from your very first program, a `` Hello, World ! ``, you immediately make consumption of C functions :
#include
int main(void) {
printf("Hello, World!");
}
The main()
routine is a very crucial routine, as it 's the entry luff for a C program. here 's another function :
void doSomething(int value) {
printf("%u", value);
}
Functions have 4 significant aspects :
- they have a name, so we can invoke ("call") them later
- they specify a return value
- they can have arguments
- they have a body, wrapped in curly braces
The routine body is the set of instructions that are executed any clock time we invoke a officiate. If the function has no hark back value, you can use the keyword void
before the officiate list. Otherwise you specify the routine return value type ( int
for an integer, float
for a floating distributor point value, const char *
for a string, etc ). You can not return more than one prize from a function. A affair can have arguments. They are optional. If it does not have them, inside the parentheses we insert void
, like this :
void doSomething(void) {
/* ... */
}
In this event, when we invoke the function we 'll call it with nothing in the parentheses :
doSomething();
If we have one argument, we specify the type and the name of the parameter, like this :
void doSomething(int value) {
/* ... */
}
When we invoke the officiate, we 'll pass that parameter in the parentheses, like this :
doSomething(3);
We can have multiple parameters, and if indeed we separate them using a comma, both in the declaration and in the invocation :
void doSomething(int value1, int value2) {
/* ... */
}
doSomething(3, 4);
Parameters are passed by copy. This means that if you modify value1
, its value is modified locally. The value outside of the affair, where it was passed in the invocation, does not change. If you pass a pointer as a parameter, you can modify that variable measure because you can now access it directly using its memory address. You ca n't define a nonpayment value for a argument. C++ can do that ( and indeed Arduino Language programs can ), but C ca n't. Make sure you define the function before calling it, or the compiler will raise a warn and an error :
➜ ~ gcc hello.c -o hello; ./hello
hello.c:13:3: warning: implicit declaration of
function 'doSomething' is invalid in C99
[-Wimplicit-function-declaration]
doSomething(3, 4);
^
hello.c:17:6: error: conflicting types for
'doSomething'
void doSomething(int value1, char value2) {
^
hello.c:13:3: note: previous implicit declaration
is here
doSomething(3, 4);
^
1 warning and 1 error generated.
The warning you get regards the order, which I already mentioned. The error is about another thing, related. Since C does not `` see '' the affair resolution before the conjuring, it must make assumptions. And it assumes the function to return int
. The function however returns void
, therefore the error. If you change the serve definition to :
int doSomething(int value1, int value2) {
printf("%d %d\n", value1, value2);
return 1;
}
you 'd fair get the warn, and not the error :
➜ ~ gcc hello.c -o hello; ./hello
hello.c:14:3: warning: implicit declaration of
function 'doSomething' is invalid in C99
[-Wimplicit-function-declaration]
doSomething(3, 4);
^
1 warning generated.
In any subject, make certain you declare the function before using it. Either move the function up, or add the function prototype in a header file. Inside a routine, you can declare variables .
void doSomething(int value) {
int doubleValue = value * 2;
}
A variable is created at the point of invocation of the function and is destroyed when the function ends. It 's not visible from the outside. Inside a serve, you can call the function itself. This is called recursion and it 's something that offers peculiar opportunities .
Input and output
C is a belittled terminology, and the `` effect '' of C does not include any Input/Output ( I/O ) functionality. This is not something singular to C, of course. It 's coarse for the linguistic process core to be agnostic of I/O. In the case of C, Input/Output is provided to us by the C Standard Library via a dress of functions defined in the stdio.h
header file. You can import this library using :
#include
on top of your C file. This library provides us with, among many early functions :
printf()
scanf()
sscanf()
fgets()
fprintf()
Before describing what those functions do, I want to take a minute to talk about I/O streams. We have 3 kinds of I/O streams in C :
stdin
(standard input)stdout
(standard output)stderr
(standard error)
With I/O functions we constantly work with streams. A stream is a high grade interface that can represent a device or a file. From the C point of view, we do n't have any difference in reading from a file or interpretation from the command lineage : it 's an I/O pour in any casing. That 's one thing to keep in mind. Some functions are designed to work with a specific stream, like printf()
, which we use to print characters to stdout
. Using its more general counterpart fprintf()
, we can specify which pour to write to. Since I started talking about printf()
, let 's introduce it now. printf()
is one of the beginning functions you 'll use when learning C program. In its simplest usage shape, you pass it a string misprint :
printf("hey!");
and the program will print the capacity of the string to the screen. You can print the measure of a varying. But it 's a sting crafty because you need to add a special character, a placeholder, which changes depending on the type of the variable. For exercise we use %d
for a signed decimal integer digit :
int age = 37;
printf("My age is %d", age);
We can print more than one variable star by using comma :
int age_yesterday = 37;
int age_today = 36;
printf("Yesterday my age was %d and today is %d", age_yesterday, age_today);
There are other format specifiers like %d
:
%c
for a char%s
for a char%f
for floating point numbers%p
for pointers
and many more. We can use escape characters in printf()
, like \n
which we can use to make the output signal create a new argumentation .
scanf()
printf()
is used as an output function. I want to introduce an input signal affair now, so we can say we can do all the I/O thing : scanf()
. This function is used to get a value from the exploiter running the course of study, from the command line. We must first define a variable that will hold the value we get from the input signal :
int age;
then we call scanf()
with 2 arguments : the format ( type ) of the variable, and the address of the varying :
scanf("%d", &age);
If we want to get a bowed stringed instrument as input, remember that a string name is a pointer to the first character, so you do n't need the &
quality before it :
char name[20];
scanf("%s", name);
here 's a small program that uses both printf()
and scanf()
:
#include
int main(void) {
char name[20];
printf("Enter your name: ");
scanf("%s", name);
printf("you entered %s", name);
}
Variable scope
When you define a variable in a C broadcast, depending on where you declare it, it will have a different scope. This means that it will be available in some places, but not in others. The stead determines 2 types of variables :
- global variables
- local variables
This is the difference : a variable declared inside a function is a local variable, like this :
int main(void) {
int age = 37;
}
local variables are only accessible from within the officiate, and when the officiate ends they stop their universe. They are cleared from the memory ( with some exceptions ). A variable defined outside a function is a ball-shaped variable, like in this case :
int age = 37;
int main(void) {
/* ... */
}
ball-shaped variables are accessible from any routine of the program, and they are available for the whole execution of the program, until it ends. I mentioned that local variables are not available any more after the routine ends. The reason is that local variables are declared on the stack, by default, unless you explicitly allocate them on the pile using pointers. But then you have to manage the memory yourself .
Static variables
Inside a function, you can initialize a static variable using the static
keyword. I said `` inside a function '' because global variables are static by default, so there 's no need to add the keyword. What 's a static variable ? A inactive variable is initialized to 0 if no initial value is specified, and it retains the value across function calls. Consider this serve :
int incrementAge() {
int age = 0;
age++;
return age;
}
If we call incrementAge()
once, we 'll get 1
as the hark back value. If we call it more than once, we 'll constantly get 1 back, because age
is a local variable and it 's re-initialized to 0
on every single officiate call. If we change the routine to :
int incrementAge() {
static int age = 0;
age++;
return age;
}
now every time we call this affair, we 'll get an incremented value :
printf("%d\n", incrementAge());
printf("%d\n", incrementAge());
printf("%d\n", incrementAge());
will give us
1
2
3
We can besides omit initializing age
to 0 in static int age = 0;
, and precisely write static int age;
because static variables are mechanically set to 0 when created. We can besides have static arrays. In this case, each single item in the range is initialized to 0 :
int incrementAge() {
static int ages[3];
ages[0]++;
return ages[0];
}
Global variables
In this incision I want to talk more about the deviation between global and local variables. A local variable is defined inside a function, and it 's only available inside that officiate. Like this :
#include
int main(void) {
char j = 0;
j += 10;
printf("%u", j); //10
}
j
is not available anywhere outside the main
officiate. A global variable is defined outside of any affair, like this :
#include
char i = 0;
int main(void) {
i += 10;
printf("%u", i); //10
}
A global variable can be accessed by any routine in the program. Access is not limited to reading the rate : the variable can be updated by any serve. due to this, global variables are one way we have of sharing the same data between functions. The chief difference with local variables is that the memory allocated for variables is freed once the affair ends. global variables are merely dislodge when the program ends .
Type definitions
The typedef
keyword in C allows you to defined new types. Starting from the built-in C types, we can create our own types, using this syntax :
typedef existingtype NEWTYPE
The new type we create is normally, by convention, uppercase. This it to distinguish it more well, and immediately recognize it as type. For exemplar we can define a new NUMBER
type that is an int
:
typedef int NUMBER
and once you do indeed, you can define modern NUMBER
variables :
NUMBER one = 1;
now you might ask : why ? Why not just use the built-in type int
rather ? well, typedef
gets truly useful when paired with two things : enumerated types and structures .
Enumerated types
Using the typedef
and enum
keywords we can define a character that can have either one value or another. It 's one of the most important uses of the typedef
keyword. This is the syntax of an count type :
typedef enum {
//...values
} TYPENAME;
The count type we create is normally, by convention, capital. here is a bare case :
typedef enum {
true,
false
} BOOLEAN;
C comes with a bool
type, so this case is not actually practical, but you get the theme. Another model is to define weekdays :
typedef enum {
monday,
tuesday,
wednesday,
thursday,
friday,
saturday,
sunday
} WEEKDAY;
here 's a elementary course of study that uses this count type :
#include
typedef enum {
monday,
tuesday,
wednesday,
thursday,
friday,
saturday,
sunday
} WEEKDAY;
int main(void) {
WEEKDAY day = monday;
if (day == monday) {
printf("It's monday!");
} else {
printf("It's not monday");
}
}
Every detail in the enum definition is paired to an integer, internally. then in this exemplar monday
is 0, tuesday
is 1 and therefore on. This means the conditional could have been if (day == 0)
alternatively of if (day == monday)
, but it 's way simple for us humans to reason with names rather than numbers, so it 's a very convenient syntax .
Structures
Using the struct
keyword we can create complex data structures using basic C types. A structure is a solicitation of values of different types. Arrays in C are limited to a type, so structures can prove to be very interesting in a set of use cases. This is the syntax of a structure :
struct {
//...variables
};
Example :
struct person {
int age;
char *name;
};
You can declare variables that have as type that structure by adding them after the close curly bracket, before the semicolon, like this :
struct person {
int age;
char *name;
} flavio;
Or multiple ones, like this :
struct person {
int age;
char *name;
} flavio, people[20];
In this case I declare a single person
variable named flavio
, and an range of 20 person
named people
. We can besides declare variables subsequently on, using this syntax :
struct person {
int age;
char *name;
};
struct person flavio;
We can initialize a structure at declaration time :
struct person {
int age;
char *name;
};
struct person flavio = { 37, "Flavio" };
and once we have a social organization defined, we can access the values in it using a department of transportation :
struct person {
int age;
char *name;
};
struct person flavio = { 37, "Flavio" };
printf("%s, age %u", flavio.name, flavio.age);
We can besides change the values using the department of transportation syntax :
struct person {
int age;
char *name;
};
struct person flavio = { 37, "Flavio" };
flavio.age = 38;
Structures are very useful because we can pass them around as function parameters, or hark back values, embedding diverse variables within them. Each variable has a label. It 's authoritative to note that structures are passed by copy, unless of run you pass a cursor to a struct, in which case it 's passed by citation. Using typedef
we can simplify the code when working with structures. Let 's look at an case :
typedef struct {
int age;
char *name;
} PERSON;
The structure we create using typedef
is normally, by conventionality, capital. now we can declare modern PERSON
variables like this :
PERSON flavio;
and we can initialize them at contract in this room :
PERSON flavio = { 37, "Flavio" };
Command line parameters
In your C programs, you might need to accept parameters from the command telephone line when the command launches. For simple needs, all you need to do to do indeed is change the main()
function touch from
int main(void)
to
int main (int argc, char *argv[])
argc
is an integer phone number that contains the numeral of parameters that were provided in the command line. argv
is an array of strings. When the program starts, we are provided the arguments in those 2 parameters. note that there 's always at least one detail in the argv
array : the name of the course of study Let 's take the example of the C compiler we use to run our programs, like this :
gcc hello.c -o hello
If this was our program, we 'd have argc
being 4 and argv
being an range contain
gcc
hello.c
-o
hello
Let 's write a program that prints the arguments it receives :
#include
int main (int argc, char *argv[]) {
for (int i = 0; i < argc; i++) {
printf("%s\n", argv[i]);
}
}
If the name of our program is hello
and we run it like this : ./hello
, we 'd get this as output signal :
./hello
If we pass some random parameters, like this : ./hello a b c
we 'd get this output to the terminal :
./hello
a
b
c
This system works great for bare needs. For more complex needs, there are normally used packages like getopt .
Headers files
simpleton programs can be put in a individual file. But when your program grows larger it 's impossible to keep it all in good one file. You can move parts of a program to a break file. then you create a header file. A header file looks like a normal C file, except it ends with .h
rather of .c
. rather of the implementations of your functions and the other parts of a program, it holds the declarations. You already used header files when you inaugural used the printf()
officiate, or other I/O routine, and you had to type :
#include
to use it. #include
is a preprocessor directive. The preprocessor goes and looks up the stdio.h
file in the standard library because you used brackets around it. To include your own header files, you 'll use quotes, like this :
#include "myfile.h"
The above will look up myfile.h
in the stream booklet. You can besides use a folder structure for libraries :
#include "myfolder/myfile.h"
Let 's look at an model. This program calculates the years since a given class :
#include
int calculateAge(int year) {
const int CURRENT_YEAR = 2020;
return CURRENT_YEAR - year;
}
int main(void) {
printf("%u", calculateAge(1983));
}
Suppose I want to move the calculateAge
function to a divide file. I create a calculate_age.c
file :
int calculateAge(int year) {
const int CURRENT_YEAR = 2020;
return CURRENT_YEAR - year;
}
And a calculate_age.h
file where I put the function prototype, which is the same as the function in the .c
file, except the body :
int calculateAge(int year);
now in the independent .c
file we can go and remove the calculateAge()
routine definition, and we can import calculate_age.h
, which will make the calculateAge()
function available :
#include
#include "calculate_age.h"
int main(void) {
printf("%u", calculateAge(1983));
}
Do n't forget that to compile a program composed by multiple files, you need to list them all in the command line, like this :
gcc -o main main.c calculate_age.c
And with more complex apparatus, a Makefile is necessity to tell the compiler how to compile the program .
The preprocessor
The preprocessor is a tool that helps us a batch when programming with C. It is part of the C Standard, just like the language, the compiler, and the standard library. It parses our program and makes surely that the compiler gets all the things it needs before going on with the process. What does it do, in drill ? For model, it looks up all the heading files you include with the #include
directive. It besides looks at every constant you defined using #define
and substitutes it with its actual value. That 's just the begin. I mentioned those 2 operations because they are the most coarse ones. The preprocessor can do a lot more. Did you notice #include
and #define
have a #
at the beginning ? That 's common to all the preprocessor directives. If a line starts with #
, that 's taken care of by the preprocessor .
Conditionals
One of the things we can do is to use conditionals to change how our program will be compiled, depending on the value of an expression. For example we can check if the DEBUG
ceaseless is 0 :
#include
const int DEBUG = 0;
int main(void) {
#if DEBUG == 0
printf("I am NOT debugging\n");
#else
printf("I am debugging\n");
#endif
}
Symbolic constants
We can define a symbolic constant :
#define VALUE 1
#define PI 3.14
#define NAME "Flavio"
When we use NAME or PI or VALUE in our program, the preprocessor replaces its identify with the prize before executing the program. emblematic constants are very useful because we can give names to values without creating variables at compilation meter .
Macros
With #define
we can besides define a macro. The difference between a macro and a symbolic ceaseless is that a macro can accept an argument and typically contains code, while a symbolic ceaseless is a respect :
#define POWER(x) ((x) * (x))
Notice the parentheses around the arguments : this is a dependable drill to avoid issues when the macro is replaced in the precompilation process. then we can use it in our code like this :
printf("%u\n", POWER(4)); //16
The big difference with functions is that macros do not specify the type of their arguments or come back values, which might be handy in some cases. Macros, however, are limited to one line definitions .
If defined
We can check if a symbolic ceaseless or a macro is defined using #ifdef
:
#include
#define VALUE 1
int main(void) {
#ifdef VALUE
printf("Value is defined\n");
#else
printf("Value is not defined\n");
#endif
}
We besides have #ifndev
to check for the face-to-face ( macro not defined ). We can besides use #if defined
and #if !defined
to do the same undertaking. It 's common to wrap some stop of code into a block like this :
#if 0
#endif
to temporarily prevent it from running, or to use a DEBUG emblematic constant :
#define DEBUG 0
#if DEBUG
//code only sent to the compiler
//if DEBUG is not 0
#endif
Predefined symbolic constants you can use
The preprocessor besides defines a number of emblematic constants you can use, identified by the 2 underscores before and after the name, including :
__LINE__
translates to the current line in the source code file__FILE__
translates to the name of the file__DATE__
translates to the compilation date, in theMmm gg aaaa
format__TIME__
translates to the compilation time, in thehh:mm:ss
format
Conclusion
Thanks a batch for reading this handbook ! I hope it will inspire you to know more about C. For more tutorials, check out my blog flaviocopes.com.
Send any feedback, misprint, or opinions at hey @ flaviocopes.com And remember : You can get a PDF and ePub version of this C Beginner 's Handbook You can reach me on Twitter @ flaviocopes .