C

Compiling

- MinGW:

gcc app.c -o app.exe

To compile an EXE from Linux:

x86_64-w64-mingw32-gcc hello.c -o hello.exe

i686-w64-mingw32-gcc hello.c -lws2_32 -o hello.exe

To compile an ELF from Windows:

x86_64-linux-gnu-gcc hello.c -o hello

- VSCode:

C/C++ Extension

Terminal > Run Build Task > gcc build active file

- Visual Studio:

Create pertinent project, then Build > Release > Build Solution

If you want to debug it, press the play debug boton.

Variables

These are named entities in your program that are used to store data of a particular type. When you declare a variable, you specify its data type, which tells the compiler how much memory to allocate for the variable and what kind of data it will hold.

// Code with no variables:
#include <stdio.h>
#include <stdlib.h>

int main ()
{
	printf("There once was a man called John\n");
	printf("He was 35 years old.\n");
	printf("He really liked the name John\n");
	printf("but did not like being 35.\n");
	
	return 0;
}

// Code with variables:
#include <stdio.h>
#include <stdlib.h>


int main ()
{
	char characterName[] = "John";
	int characterAge = 35;
	
	printf("There once was a man called %s\n", characterName);
	printf("He was %d years old.\n", characterAge);
	printf("He really liked the name %s\n", characterName);
	printf("but did not like being %d.\n", characterAge);
	
	return 0;
}

// To modify the value stored inside the variable;
#include <stdio.h>
#include <stdlib.h>


int main ()
{
	char characterName[] = "John";
	int characterAge = 35;
	
	printf("There once was a man called %s\n", characterName);
	printf("He was %d years old.\n", characterAge);
	
	characterAge= 40;
	printf("He really liked the name %s\n", characterName);
	printf("but did not like being %d.\n", characterAge);
	
	return 0;
}

%s

Format and print a string (character array or pointer to a character array).

printf("Name: %s\n", "John");

%c

Format and print a single character.

printf("First letter: %c\n", 'A');

%d

Format and print signed decimal integers.

printf("Age: %d\n", 30);

%ld

Format and print signed long integers.

printf("Population: %ld\n", 1000000L);

%f

Format and print floating-point numbers (float).

printf("Price: %.2f\n", 19.99f);

%lf

Format and print double-precision floating-point numbers (double).

printf("Value: %.4lf\n", 3.141592653589793);

%x

Format and print hexadecimal integers (lowercase letters a-f).

printf("Hex: %x\n", 255);

%X

Format and print hexadecimal integers (uppercase letters A-F).

printf("Hex: %X\n", 255);

%o

Format and print octal integers.

printf("Octal: %o\n", 255);

%u

Format and print unsigned decimal integers.

printf("Unsigned: %u\n", 255);

%p

Format and print memory addresses (pointers).

printf("Address: %p\n", &variable);

Data Types

These are the definitions provided by the programming language to tell the compiler or interpreter how you intend to use the data. Data types determine the size and layout of the data storage; the range of values that can be stored within that data type; and the set of operations that can be applied to the data type.

#include <stdio.h>
#include <stdlib.h>


int main ()
{
	int age = 40;
	double gpa = 3.7;
	
	char grade = 'A';
	char prhase[] = "Hola ";
	
	return 0;
}

Type

Purpose

Bytes (Typically)

char

Store single characters or small integers

1

unsigned char

Store single characters or small unsigned integers

1

int

Store whole numbers

4

unsigned int

Store whole numbers (only positive)

4

short int

Store smaller whole numbers

2

unsigned short int

Store smaller whole numbers (only positive)

2

long int

Store larger whole numbers

4 or 8

unsigned long int

Store larger whole numbers (only positive)

4 or 8

long long int

Store very large whole numbers

8

unsigned long long int

Store very large whole numbers (only positive)

8

float

Store single precision floating-point numbers

4

double

Store double precision floating-point numbers

8

long double

Store extended precision floating-point numbers

8 or 16

wchar_t

Store wide characters (for internationalization support)

2 or 4

Arrays

An array is a collection of elements, all of the same type, stored in contiguous memory locations. It serves to organize and manage multiple items using a single identifier, facilitating efficient data manipulation and access.

int main()
{
	int arreglo[4] = {3,4,1,5}; // Array of an integer wich 4 elements
	printf("%i\n",arreglo[2]); //print the parameter in the second position of the array, which is 1, because the 3 is the position 0
}

//Example of storing the array in an int
int sizeA;
printf("tamaño del arreglo\n");
scanf("%i",&sizeA); //The user will select the size of the array
int age[sizeA]; //The size of the array is sizeA, which was introduced by the user
for(int i = 0; i < sizeA;i++) // Do the following until the reaching the size of the array
{
	printf("ingresa el valor %i\n",i+1); //i+1 is selected to make the program start in 1 and not 0
	scanf("%i", &age[i]); 
}
printf("Los valores del arreglo son: \n");
for(int i = 0; i < sizeA; i++)
{
	printf("%i", age[i]); //Print the selected values of the array
}

Printf

#include <stdio.h>
#include <stdlib.h>


int main ()
{
	printf("Hello \"Tom\"");
	
	printf("My favourite %s is %f," "number", 500.45);
	
	int FavNum = 90;
	char myChar = 'i';
	printf("My favourite %c is %d," "number", FavNum);
	
	return 0;
}

Numbers

#include <stdio.h>
#include <stdlib.h>


int main ()
{
printf("%f," 5.0 + 4.5);
printf("%f," 5.0 - 4.5);
printf("%f," 5.0 * 4.5);
printf("%f," 5.0 / 4.5);

int num = 6;
printf("%d," num);

printf("%f," pow(2, 3) ); /* 2^3 */
printf("%f," sqrt(36) ); /* square root */
printf("%f", ceil(36.356) ); /* round up */
printf("%f", floor(36.356) ); /* round down */


return 0;
}

Constants

#include <stdio.h>
#include <stdlib.h>


int main ()
{
/* This will work */
int num = 5;
printf("%d", num);
num = 8;
printf("%d", num)

/* This not, you can't modify a constant variable, people usually put them in mayus*/
const int NUM = 5;
printf("%d", NUM);
num = 8;
printf("%d", NUM);

/* This will also be considered a constant*/
printf("Hello");
printf("%d", 70);

return 0;

Getting User Input

#include <stdio.h>
#include <stdlib.h>

int main ()
{
int age;
printf("Enter your age: ");
scanf("%d", &age);
printf("You are %d years old", age);

double gpa;
printf("Enter your gpa: ");
scanf("%lf", &gpa);
printf("Your gpa is %f", gpa);

char grade;
printf("Enter your grade: ");
scanf("%c", &grade);
printf("Your grade is %c", grade);

/* Just prints until the first space */
char name[20];
printf("Enter your name: ");
scanf("%s", name);
printf("Your name is %s", name);

char name[20];
printf("Enter your name: ");
fgets(name, 20, stdin);
printf("Your name is %s", name);

return 0;
}

Structures

Structures or Structs are user-defined data types that allow the programmer to group related data items of different data types into a single unit. Structs can be used to store data related to a particular object. Structs help organize large amounts of related data in a way that can be easily accessed and manipulated. Each item within a struct is called a "member" or "element".

- Structure Declaration

Structures can be declared with the use of typedef keyword to give a structure an alias.

For example, the structure below is created with the name _STRUCTURE_NAME but typedef adds two other names, STRUCTURE_NAME and *PSTRUCTURE_NAME.

typedef struct _STRUCTURE_NAME {
// structure elements
} STRUCTURE_NAME, *PSTRUCTURE_NAME;

The STRUCTURE_NAME alias refers to the structure name, whereas PSTRUCTURE_NAME represents a pointer to that structure. Microsoft generally uses the P prefix to indicate a pointer type.

- Structure Initialization

Initializing the actual structure type (following the previous example, it would be the same when using _STRUCTURE_NAME or STRUCTURE_NAME):

STRUCTURE_NAME struct1 = { 0 }; // The '{ 0 }' part, is used to initialize all the elements of struct1 to zero
// OR
_STRUCTURE_NAME struct2 = { 0 }; // The '{ 0 }' part, is used to initialize all the elements of struct2 to zero

Initializing the structure pointer, PSTRUCTURE_NAME.

PSTRUCTURE_NAME structpointer = NULL;

- Initializing and Accessing Structures Members

To initialize structure's members directly via the dot operator (.):

typedef struct _STRUCTURE_NAME {
    int ID;
    int Age;
} STRUCTURE_NAME, *PSTRUCTURE_NAME;

STRUCTURE_NAME struct1 = { 0 }; // initialize all elements of struct1 to zero
struct1.ID = 1470; // initialize the ID element
struct1.Age = 34; // initialize the Age element

To initialize structure's members using designated initializer syntax to specify which members of the structure to initialize:

typedef struct _STRUCTURE_NAME {
    int ID;
    int Age;
} STRUCTURE_NAME, *PSTRUCTURE_NAME;

STRUCTURE_NAME struct1 = {
    .ID = 1470,
    .Age = 34
}; // initialize both the ID and the Age elements

To access and initialize a structure through its pointer via the arrow operator (->):

typedef struct _STRUCTURE_NAME {
    int ID;
    int Age;
} STRUCTURE_NAME, *PSTRUCTURE_NAME;

STRUCTURE_NAME struct1 = {
    .ID = 1470,
    .Age = 34
};

PSTRUCTURE_NAME structpointer = &struct1; // structpointer is a pointer to the 'struct1' structure

// Updating the ID member
structpointer->ID = 8765;
printf("The structure's ID member is now: %d\n", structpointer->ID);

structpointer->ID is equivalent to (*structpointer).ID.

Enumeration

The enum or enumeration data type is used to define a set of named constants.

The compiler automatically assigns values to the constants, starting with 0 and increasing by 1 for each subsequent constant. In this course, enums can be seen representing the state of specific data, error codes or return values.

Enum lists cannot be modified or accessed using the dot (.) operator. Instead, each element is accessed directly using its named constant value.

enum Weekdays {
  Monday,         // 0
  Tuesday,        // 1
  Wednesday,      // 2
  Thursday,       // 3
  Friday,         // 4
  Saturday,       // 5
  Sunday          // 6
};

// Defining a "Weekdays" enum variable 
enum Weekdays EnumName = Friday;       // 4

// Check the value of "EnumName"
switch (EnumName){
    case Monday:
      printf("Today Is Monday !\n");
      break;
    case Tuesday:
      printf("Today Is Tuesday !\n");
      break;
    case Wednesday:
      printf("Today Is Wednesday !\n");
      break;
    case Thursday:
      printf("Today Is Thursday !\n");
      break;
    case Friday:
      printf("Today Is Friday !\n");
      break;
    case Saturday:
      printf("Today Is Saturday !\n");
      break;
    case Sunday:
      printf("Today Is Sunday !\n");
      break;
    default:
      break;
}

Union

Union is a data type that permits the storage of various data types in the same memory location. Unions provide an efficient way to use a single memory location for multiple purposes.

To access the members of a union in C, one can use the dot operator.

union ExampleUnion {
   int    IntegerVar;
   char   CharVar;
   float  FloatVar;
};

Bitwise Operators

Bitwise operators are operators that manipulate the individual bits of a binary value, performing operations on each corresponding bit position.

  • Right shift (>>):

The right shift (>>) operator is used to shift the bits of a binary number to the right by a specified number of positions.

For example, 10100111 shifted right by 2, to become 00101001.

  • Left shift (<<):

The left shift (<<) operators is used to shift the bits of a binary number to the left by a specified number of positions.

For example, 10100111 shifted left by 2, to become 10011100.

  • Bitwise OR (|):

Logical operation that involves two binary values at the bit level. It evaluates each bit of the first operand against the corresponding bit of the second operand, generating a new binary value. The new binary value contains a 1 in any bit position where either one or both of the corresponding bits in the original values are 1 (only 0 is the output when both inputs are 0).

  • Bitwise AND (&):

Logical operation that involves two binary values at the bit level. This operation sets the bits of the new binary value to 1 only in the case where the corresponding bits of both input operands are 1.

  • Bitwise XOR (^):

XOR operation (also known as exclusive OR) is a logical operation that involves two binary values at the bit level. If only one of the bits is 1, the result in each position is 1. Conversely, if both bits are 0 or 1, the output is 0.

  • Bitwise NOT (~):

The bitwise NOT operation takes one binary number and flips all its bits. In other words, it changes all 0s to 1s and all 1s to 0s.

- Passing By Value

Method in which the value of the object is copied and the function can only modify its local copy of the object's value, not the original object itself, allowing it to operate on local copies without changing the original variables.

int add(int a, int b)
{
    int result = a + b;
    return result;
}

int main()
{
    int x = 5;
    int y = 10;
    int sum = add(x, y); // x and y are passed by value
    return 0;
}

Passing By Reference

"Passing by reference" means when you give a function something (an argument), you're not giving it a copy of that thing's value. Instead, you're giving it a way to find and interact with the original thing directly. It's like telling someone, "Here's where you can find this thing," rather than giving them a copy of the thing itself.

So, when you pass something by reference in a function, you're sharing its memory address (a way to locate it in the computer's memory) with the function. This allows the function to work with the original thing without making its own copy. This is useful when you want to change the original thing inside the function and have those changes affect the original outside the function.

Dereferencing is the act of accessing the actual value stored at the memory location pointed to by a pointer. "Passing by reference" is more about how you pass arguments to a function, while "dereferencing" is about how you access values through pointers.

Getting the memory address of a variable:

int x = 42;
int* ptr = &x; // 'ptr' now contains the memory address of 'x'

Passing variables by reference to functions:

// Function to add two numbers and store the result in a pointer variable
void add(int *a, int *b, int *result) {
    // Dereferencing 'a' and 'b' pointers to access the values they point to.
    // These are examples of passing by reference.
    int A = *a; // 'A' now holds the value pointed to by 'a', which is 'x' from the 'main' function
    int B = *b; // 'B' now holds the value pointed to by 'b', which is 'y' from the 'main' function

    // Calculate the sum of 'A' and 'B' and store the result at the memory location pointed to by 'result'.
    // This is another example of passing by reference.
    *result = B + A;
}

int main() {
    int x = 5; // Declare and initialize 'x' to 5
    int y = 10; // Declare and initialize 'y' to 10
    int sum = 0; // Declare and initialize 'sum' to 0

    // Call the 'add' function, passing the memory addresses of 'x' and 'y' as references.
    // This means 'add' can modify 'x' and 'y' indirectly through the pointers.
    add(&x, &y, &sum);

    // The 'sum' variable has been updated through the reference passed to 'add'.
    printf("The sum is: %d\n", sum); // Print the result, which is 15 (5 + 10)

    return 0;
}

Header Files

For example, for debugging:

#pragma once
#ifndef DEBUG_H
#define DEBUG_H

#include <stdio.h> // Include for printf

#define okay(msg, ...) printf("[+] " msg "\n", ##__VA_ARGS__)
#define info(msg, ...) printf("[i] " msg "\n", ##__VA_ARGS__)
#define warn(msg, ...) printf("[!] " msg "\n", ##__VA_ARGS__)

To create a function or functions in a separate .c file to be called by another file (i.e. the main):

Hola.c:

#include "Hola.h"

VOID Hola(IN PBYTE pTarget, IN SIZE_T sTargetSize, IN PBYTE bKey, IN SIZE_T sKeySize) {

	hola hola hola
}

Hola.h:

#pragma once

#include <Windows.h>

#ifndef HOLA_H
#define HOLA_H

VOID HOLA(IN PBYTE pTarget, IN SIZE_T sTargetSize, IN PBYTE bKey, IN SIZE_T sKeySize); //Only the structure of tje arguments it will receive, not the function itself

#endif // HOLA_H

Last updated