This repository contains the practice programs I created to learn the C programming language. I started to learn C on the 10th of July and would like to practice at least one program daily to build an effective learning habit.
Currently, I am following a few YouTube videos and articles listed below. I will add more once I complete these -
I will save 5-10 minutes from my daily routine and write down what I learned today.
Since I reset my laptop, I had to reinstall VSCode and configure necessary tools like Git and GnuPG. These articles made the process easier -
I downloaded the Gpg4win binary release from GnuPG's downloads section and installed it on my system. Having done that I tried to sign my first Git commit using the terminal but got the following error message:
gpg: signing failed: secret key not available
error: gpg failed to sign the data
fatal: failed to write commit object
After looking up a few sites online, this Stack Overflow answer finally solved the issue.
I signed two commits and called it a day!
Initially, I wanted to learn C++ because it seemed intriguing. Later I found this comparison video about C, C++, and C# and decided to learn C. Also watched C in 100 Seconds to get an overview.
Today I collected several C/C++ courses and put them in this YouTube playlist.
-
I learned about compilers (GCC and Clang) and how to install them. I haven't done this kind of thing before, so it seemed a bit confusing (especially the relation between MinGW and GCC). I ran through some issues while installing MinGW and found this video that solved the problem.
-
I wrote my first C program and compiled it as a Windows executable.
Having done that, I looked up for solutions to customize the executable's icon and file details. I came to know about windres and resource files. This Stack Overflow answer helped me to accomplish what I was trying to do.
-
I created an icon, dropped in the working directory, and created a
icon.rcfile -id ICON "my-program.ico" -
I converted that
.rcfile into a.resfile using the windres command -windres icon.rc -O coff -o icon.res -
Then I compiled the C program with GCC -
gcc 01-my-first-program.c icon.res -o 01-my-first-program.exe
-
I created another
.rcfile namedversion.rcand wrote the following -1 VERSIONINFO FILEVERSION 1,0,0,0 PRODUCTVERSION 1,0,0,0 BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" BEGIN VALUE "CompanyName", "Awesome Nonsense" VALUE "FileDescription", "My First C Program" VALUE "FileVersion", "1.0.0.0" VALUE "InternalName", "01-my-first-program" VALUE "LegalCopyright", "S. Shahriar <shadowshahriar.dev@gmail.com>" VALUE "OriginalFilename", "01-my-first-program.exe" VALUE "ProductName", "My App" VALUE "ProductVersion", "1.0.0.0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1252 END END -
Likewise, I converted the
.rcfile into a.resfile -windres version.rc -O coff -o version.res -
Then I compiled the program with both
.resfiles -gcc 01-my-first-program.c icon.res version.res -o 01-my-first-program.exe
C comments are like JavaScript comments. We type two forward slashes for an inline comment -
// This is a single line comment...and two asterisks surrounded by forward slashes (/**/). Anything inside the asterisks are ignored by the compiler -
/**
* This
* is
* a
* multi-line
* comment
*/-
Escape sequences are also the same as JavaScript. Escape sequences begin with a backslash followed by specific characters or symbols. For example -
\n,\t,\r -
Unlike
console.log, theprintfmethod in C enters inline text in the terminal.
-
Since C is a statically typed language, type checking is done at compile-time. (See Stack Overflow answer: What is the difference between statically typed and dynamically typed languages?)
-
A variable is an allocated space in the memory to store a value. We refer to the name of the variable to access its stored value. We need to tell what kind of data we store in the variable. This process is called Declaration.
-
Creating a variable is done in two steps - Declaration and Initialization. First, we specify the variable's data type, then we initialize it with a value.
-
To display a variable's value in a
printfstatement, we add our variable (maxZoom) as an argument and specify a placeholder (%d) in the statement -int maxZoom = 5; printf("Maximum zoom is %d times", maxZoom);
What placeholder we set depends on the variable's data type. For example -
Data Type Placeholder int(Integer)%dfloat(Floating Point Number)%fchar(Single Character)%cchar x[](Array of Characters)%s -
While initializing a variable, we use single quotes (
'') for single characters and double quotes ("") for an array of characters. For example -char zoomAxis = 'Y'; char pane[] = "XZ";
The ANSI C spec standard determines the minimum values of each type.
| Data Type | Minimum Range | Format Specifier | Memory Size | |
|---|---|---|---|---|
| char (Single Character) | -128 to 127 | %c or %d |
1 byte | |
| unsigned | char (Single Character) | 0 to 255 | %c or %d |
1 byte |
| char x[] (Array of Characters) | %s |
|||
| bool (Boolean) | 0 (false) and 1 (true) | %d |
1 byte | |
| int (Integer) | -2,147,483,648 to -2,147,483,647 | %d |
4 bytes | |
| unsigned | int (Integer) | 0 to 4,294,967,295 | %u |
4 bytes |
| short (Integer) | -32,768 to 32,767 | %d |
2 bytes | |
| long (Integer) | -2,147,483,648 to -2,147,483,647 | %ld |
4 bytes | |
| unsigned | long (Integer) | 0 to 4,294,967,295 | %lu |
4 bytes |
| long long (Integer) | -9 quintillion to 9 quintillion | %lld |
8 bytes | |
| unsigned | long long (Integer) | 0 to 18 quintillion | %llu |
8 bytes |
| float (Floating Point Number) | first 6-7 digits | %f |
4 bytes (32 bits of precision) | |
| double (Double of float's precision) | first 15-16 digits | %lf |
8 bytes (64 bits of precision) |
charis generally used to hold letters of the ASCII chart (%, A, b...). But can be used to store small integers ranging from-128to127.- We can prepend
unsignedkeyword to start the range from 0. In this way, the range for thechartype becomes0to255. - By default, most data types are already signed and we don't need to explicitly add the
signedkeyword. doubleis more accurate thanfloatbecause it has more precision and uses more memory.- Technically it takes 1 bit to represent a
boolean. But thebooltype still takes 1 byte of memory.
-
If we go beyond a type's range, it will reset to whatever the beginning is. For example, the
unsigned charcan hold integers from0to255. If we initialize with256, the result becomes0. -
The range for other integer types such as
int,short, andlongvaries depending on the implementation and Operating System. We can create a program to retrieve the size of data types using thesizeofmethod.
-
We use format specifiers in a
printfstatement to specify the type of the data we want to display. Most common format specifiers are -- %c (Character)
- %d (Integer)
- %s (Array of Characters or string)
- %f (Float)
- %lf (Double)
-
We also use format specifiers to format the type of data to be displayed -
%.1: This one sets the decimal precision of a floating-point number. In other words, it specifies how many digits we want after the decimal point. For example -
float seek_at = 10.2324; printf("Seek at: %.2f\n", seek_at); // Seek at: 10.23 printf("Seek at: %.3f\n", seek_at); // Seek at: 10.232
%1: This one sets a minimum field width for the data. For example -
int seek = 10; printf("Seek to: %9d\n", seek); // Seek to: <7 spaces>10 printf("Seek to: %4d\n", seek); // Seek to: <2 spaces>10
%-: By default, the field text is right-aligned. We can append a minus after the percent sign for left-aligned text. For example -
int seeknext = 10; int seekprev = 7; printf("Next: %-9d\n", seeknext); // Seek to: 10<7 spaces> printf("Back: %-9d\n", seekprev); // Seek to: 7<8 spaces>
Obviously, we can mix them and create a custom format specifier -
float audience_retention = 57.33214; float click_through_rate = 6.461127; int impressions = 29501; printf("Audience Retention: %12.3f\n", audience_retention); printf("Click-through Rate: %12.3f\n", click_through_rate); printf("Impressions: %19d\n", impressions);
A constant is a fixed value that is unchanged during the program execution. That means a constant is a variable's value that can't be initialized twice. Let me clear it -
-
Regular variables can be initialized more than one time -
float pi = 3.141592; // declaration + initialization pi = 42.7; // re-initialization printf("%f", pi); // 42.700001
-
But
constvariables can be initialized only once -const float PI = 3.141592; PI = 42.7; printf("%f", PI); // error: assignment of read-only variable 'PI'
It is considered good practice to type constant variable names in upper case.
Alternatively, we can initialize a constant right after including the header files -
#include <stdio.h>
#define RESOLUTION 1080
printf("%u", RESOLUTION); // 1080In this way -
- We omit the
=and the semicolon;. - We don't declare the constant's data type because the compiler will infer that from the value.
-
C was developed in 1972 by Dennis Ritchie.
-
C is a procedural programming language: The commands are stored in a sequence and executed in that order (the top-down approach)
-
C is statically typed: Type checking is done during compile-time.
-
The syntax of other programming languages such as C++, C#, Java, JavaScript, Perl, and PHP was inspired by the C language's syntax. That's why C is called the mother of all languages.
This is the first C program that I wrote a few days ago -
#include <stdio.h>
int main()
{
printf("My first C program.");
printf("\n");
printf("C programming language was created in 1972 by Dennis Ritchie. Woah, that's 50 years ago!");
return 0;
}Let's break down the code into small chunks -
-
#includeis a preprocessor directive that tells C to insert the source code of the specified file. Imagine it as theimportstatement in JavaScript. -
stdiostands for Standard Input Output.stdio.his a header file and contains necessary input/output functions (for example- thatprintfmethod). The source code of it would be inserted at the beginning of our C program. -
We can add more header files and include their source code in our final program -
#include <stdio.h> #include <stdbool.h> // ...
-
main()is the main function that will execute during run-time. Since the main function returns0so we declare the function's data typeint-int main() { // ... return 0; }
-
We have learned that
intdata type can store a big set of positive and negative integers. But if we know our function will return a positive integer, why don't we declare its type tounsigned int? -
Because
main()will return0only when it executes without errors. In other cases, it might return a negative value.
-
-
The
printfstatement logs inline text into the terminal. It can accept one or more arguments.
int x = 10;
int y = 20;| Operator | Operation | Output |
|---|---|---|
Addition (+) |
x + y | 30 |
Subtraction (-) |
x - y | -10 |
Multiplication (*) |
x * y | 200 |
| y / x | 2 | |
Modulus (%) |
x % y | 10 |
| y % x | 0 | |
Increment (++) |
x++ | 11 |
Decrement (--) |
y-- | 19 |
Specifying the wrong data types can yield unexpected results -
int x = 10;
int y = 20;
int a = 0.5;
float z = 0.5;| Operator | Code | Output | Reason |
|---|---|---|---|
Addition (+) |
int result = x + z |
10 | result has an int type |
Subtraction (-) |
int result = x - z |
9 | result has an int type |
Multiplication (*) |
float result = x * a |
0 | a has an int type |
Division (/) |
float result = x / y |
0.000000 | y has an int type |
When we divide an integer by another integer, we need to cast it with the float type -
float result = x / (float)y;
printf("%f", result); // 0.500000Augmented Assignment Operators are used to replace a statement where an operator takes a variable as one of its arguments and then assigns the result back to that variable.
For example -
int x = 10;
x = x + 10;x = x + 10 could be replaced by x += 10
int x = 10;
x += 10;This could be done for all arithmetic operators -
int a = 4, b = 32, c = 11, d = 44, e = 68;
a += 10;
printf("%d\n", a);
b -= 2;
printf("%d\n", b);
c *= 6;
printf("%d\n", c);
d /= 4;
printf("%d\n", d);
e %= 2;
printf("%d\n", e);Couldn't practice anything because of an upcoming exam. I will try tomorrow.
-
We use the
scanfmethod to take user input.scanfcan accept numerical and character arrays. -
However,
scanfdoesn't accept white spaces. If we enter Shadow Shahriar, it will return Shadow. In such cases, we use thefgetsmethod. -
The
fgetsmethod also renders the trailing newline character at the end of the input. To get rid of it, we replace the last character with\0.
I remember completing exercises with scanf during my high school years.