docs

a slatepencil documentail site

View on GitHub

C basic

Typical Build Process

Data types

basic type

size type format specifier
1 byte char %c
2 bytes short %d
2 or 4 bytes int %d or %i
4 or 8 bytes long %ld
4 bytes float %f 6-7 precision
8 bytes double %lf 15-16 precision
#include <stdbool.h>

bool isProgrammingFun = true;
printf("%d", isProgrammingFun);   // Returns 1 (true)

// Arrays
int myNumbers[] = {25, 50, 75, 100};
int matrix[2][3] = { {1, 4, 2}, {3, 6, 8} };

char greetings[] = "Hello World!";
printf("%s", greetings);

d_type (man readdir)

/* Standard file descriptors.  */
#define STDIN_FILENO    0       /* Standard input.  */
#define STDOUT_FILENO   1       /* Standard output.  */
#define STDERR_FILENO   2       /* Standard error output.  */

struct dirent {
  ino_t          d_ino;       /* Inode number */
  off_t          d_off;       /* Not an offset; see below */
  unsigned short d_reclen;    /* Length of this record */
  unsigned char  d_type;      /* Type of file; not supported by all filesystem types */
  char           d_name[256]; /* Null-terminated filename */
};

IEEE 754 floating point format

Float VS Double

Float
Double
#include <stdio.h>

int main(void)
{
    float fnum = 78.55674545678;
    double dnum = 78.5567676767676;

    printf("Number is = %0.9f\n", fnum);
    // Number is 78.556747437
    printf("Number is = %0.14lf\n", dnum);
    // Number is 78.55676767676761
    return 0;
}

Enumerations

Enums are an enumerated type builed into C-programming: Integer data type

enum { = , // assigned constant values = , , // auto assigned values , , /* more enumerations */ }

enum month {
    JAUNARY     = 1,
    FEBRUARY    = 2,
    MARCH       = 3,
    APRIL       = 4,
    MAY         = 5,
    JUNE        = 6,
    JULY        = 7,
    AUGUST      = 8,
    SEPTEMBER   = 9,
    OCTOBER     = 10,
    NOVEMBER    = 11,
    DECEMBER    = 12
};

enum month m = APRIL;

typedef enum {
    NO_ERROR = 0,
    SYNC_ERROR = 1,
    POWER_ERROR = 2,
    BUS_ERROR = 3,
    /* More enums */
} Error_e;

Error_e error = NO_ERROR;

Unions

Derived data type containing multiple data members that occupy the same address

union { ; ; /* more members */ }

typedef union {
    uint8_t x;
    uint8_t y;
    uint8_t z;
} ex1;

sizeof(ex1) // 1Byte

ex1 point;
point.x = 0x15;

typedef union {
    uint8_t x;
    uint16_t y;
    uint32_t z;
    uin8_t array[16];
} ex;

sizeof(ex) // 16 bytes

&ex = &ex.x = &ex.y = &ex.z = &ex.array[0];

typedef struct {
    uint8_t x;
    uint8_t y;
    uint8_t z;
} Str1_t;
typedef struct {
    uint32_t x;
    uint32_t y;
    uint32_t z;
} Str2_t;
typedef union {
    Str1_t s1;
    Str2_t s2;
} ex3;

sizeof(ex3) // 12-16 bytes

ex3 ex;
ex.s1.x = 0x12;
ex.s2.z = 0x12345678;

Format specifier

%[flags][width][.precision][length]specifier

flags description
- Left-justify within the given field width; Right justification is the default (see width sub-specifier).
+ Forces to preceed the result with a plus or minus sign (+ or -) even for positive numbers.
  By default, only negative numbers are preceded with a - sign.
(space) If no sign is going to be written, a blank space is inserted before the value.
# Used with o, x or X specifiers the value is preceeded with 0, 0x or 0X respectively for values different than zero.
  Used with a, A, e, E, f, F, g or G it forces the written output to contain a decimal point even if no
  more digits follow. By default, if no digits follow, no decimal point is written.
0 Left-pads the number with zeroes (0) instead of spaces when padding is specified (see width sub-specifier).
width description
(number) Minimum number of characters to be printed. If the value to be
  printed is shorter than this number, the result is padded with blank spaces.
  The value is not truncated even if the result is larger.
* The width is not specified in the format string, but as an additional
  integer value argument preceding the argument that has to be formatted.
specifier Output Example
d or i Signed decimal integer 392
u Unsigned decimal integer 7235
o Unsigned octal 610
x Unsigned hexadecimal integer 7fa
X Unsigned hexadecimal integer (uppercase) 7FA
f Decimal floating point, lowercase 392.65
F Decimal floating point, uppercase 392.65
e Scientific notation (mantissa/exponent), lowercase 3.9265e+2
E Scientific notation (mantissa/exponent), uppercase 3.9265E+2
g Use the shortest representation: %e or %f 392.65
G Use the shortest representation: %E or %F 392.65
a Hexadecimal floating point, lowercase -0xc.90fep-2
A Hexadecimal floating point, uppercase -0XC.90FEP-20
c Character a
s String of characters sample
p Pointer address b8000000
n Nothing printed.  
  The corresponding argument must be a pointer to a signed int.  
  The number of characters written so far is stored in the pointed location.  
% A % followed by another % character will write a single % to the stream. %
#include <stdio.h>

int main()
{
   printf ("Characters: %c %c \n", 'a', 65);
   // Characters: a A
   printf ("Decimals: %d %ld\n", 1977, 650000L);
   // Decimals: 1977 650000
   printf ("Preceding with blanks: %10d \n", 1977);
   // Preceding with blanks:       1977
   printf ("Preceding with zeros: %010d \n", 1977);
   // Preceding with zeros: 0000001977
   printf ("Some different radices: %d %x %o %#x %#o \n", 100, 100, 100, 100, 100);
   // Some different radices: 100 64 144 0x64 0144
   printf ("floats: %4.2f %+.0e %E \n", 3.1416, 3.1416, 3.1416);
   // floats: 3.14 +3e+000 3.141600E+000
   printf ("Width trick: %*d \n", 5, 10);
   // Width trick:    10
   printf ("%s \n", "A string");
   // A string
   return 0;
}

Bitwise operation

#include <stdio.h>

int main() {
    unsigned int a = 60;  // 0011 1100 in binary
    unsigned int b = 13;  // 0000 1101 in binary
    unsigned int result;

    // Bitwise AND
    result = a & b;  // 0000 1100
    printf("a & b = %d\n", result);

    // Bitwise OR
    result = a | b;  // 0011 1101
    printf("a | b = %d\n", result);

    // Bitwise XOR
    result = a ^ b;  // 0011 0001
    printf("a ^ b = %d\n", result);

    // Bitwise NOT
    result = ~a;  // 1100 0011
    printf("~a = %d\n", result);

    // Left Shift
    result = a << 2;  // 1111 0000
    printf("a << 2 = %d\n", result);

    // Right Shift
    result = a >> 2;  // 0000 1111
    printf("a >> 2 = %d\n", result);

    return 0;
}

Bit Masks

Bit Masks are constant expressions used to set, clear, or toggle a specific set of bits

// Macros get replaced into the code by their corresponding values during pre processing and do not take up space in code memory
#define MASK1 (0x30) // 0011 0000
#define MASK2 (0xC0) // 1100 0000
#define MASK3 (0x0E) // 0000 1110

uint8_t foo = 0x0C; // 0000 1100

foo |= MASK1; // 0011 1100 // 0x3C
foo &= MASK2; // 0000 0000 // 0x00
foo ^= MASK3; // 0000 0010 // 0x02

construt type

// this is a comment
/*
 * this is a multi-line comment
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void stringDisplay()
{
    char color[20];
    char pluralNoun[20];
    char celebrityF[20];
    char celebrityL[20];

    printf("Enter a color: ");
    scanf("%s", color);

    printf("Enter a plural noun: ");
    scanf("%s", pluralNoun);

    printf("Enter a celebrity: ");
    scanf("%s%s", celebrityF, celebrityL);

    printf("Roses are %s\n", color);
    printf("%s are blue\n", pluralNoun);
    printf("I love %s %s\n", celebrityF, celebrityL);
}

typedef enum Color {
    COLOR_BLUE = 0,
    COLOR_RED = 1,
    COLOR_GREEN = 2,
} Color_t;

typedef struct Data {
    int32_t temperature;
    uint32_t date;
    uint32_t time;
} Data_t;

void arrayDisplay()
{
    int luckyNumbers[] = {1, 3, 5, 7, 11, 17, 19, 23};
    // luckyNumbers[1] = 200;
    printf("%d\n", luckyNumbers[1]);
}

void invokeFunc()
{
    printf("Hello World!\n");
}

double cube(double num)
{
    double result = num * num * num;
    return result;
}

void calculate()
{
    double num1;
    double num2;
    char op;

    printf("Enter a number: ");
    scanf("%lf", &num1);
    printf("Enter operator: ");
    scanf(" %c", &op);
    printf("Enter a number: ");
    scanf("%lf", &num2);

    if (op == '+')
    {
        printf("%f", num1 + num2);
    }
    else if (op == '-')
    {
        printf("%f", num1 - num2);
    }
    else if (op == '*')
    {
        printf("%f", num1 * num2);
    }
    else if (op == '/')
    {
        printf("%f", num1 / num2);
    }
    else
    {
        printf("Invalid operator!\n");
    }

    printf("\n");
}

void switchCondition()
{
    char grade = 'A';
    switch (grade)
    {
    case 'A':
        printf("You did great!");
        break;
    case 'B':
        printf("You did alright!");
        break;
    case 'C':
        printf("You did poorly!");
        break;
    case 'D':
        printf("You did very bad");
        break;
    case 'F':
        printf("You Failed!");
        break;
    default:
        printf("Invalid grade");
    }
    printf("\n");
}

struct Student
{
    char name[50];
    char major[50];
    int age;
    double gpa;
};

void useStruct()
{
    struct Student student1;
    student1.age = 22;
    student1.gpa = 3.2;
    strcpy(student1.name, "Johnson");
    strcpy(student1.major, "eecs");
    printf("%s\n", student1.name);
    printf("%s\n", student1.major);

    struct Student student2;
    student2.age = 33;
    student2.gpa = 5.6;
    strcpy(student2.name, "Carlson");
    strcpy(student2.major, "IOT");

    printf("%s\n", student2.name);
    printf("%s\n", student2.major);
}

void whileLoop()
{
    int index = 1;
    while (index <= 5)
    {
        printf("%d \n", index);
        index++;
    }
}

void doWhileLoop()
{
    int index = 0;
    do
    {
        printf("%d\n", index);
        index++;
    } while (index <= 5);
}

void loop()
{
    int secretNumber = 5;
    int guess;
    int guessCount = 0;
    int guessLimit = 3;
    int outOfGuesses = 0;

    while (guess != secretNumber && outOfGuesses == 0)
    {
        if (guessCount < guessLimit)
        {
            printf("Enter a number: ");
            scanf("%d", &guess);
            guessCount++;
        }
        else
        {
            outOfGuesses = 1;
        }
    }
    if (outOfGuesses == 0)
    {
        printf("You Win!");
    }
    else
    {
        printf("You lose!");
    }
}

void forLoop()
{
    int luckyNumbers[] = {1, 3, 5, 7, 11};
    int i;
    printf("%d\n", sizeof(luckyNumbers));
    for (i = 0; i < 5; i++)
    {
        printf("%d\n", luckyNumbers[i]);
    }

    /* loop forever */
    for (;;);
}

long long factorialSum(int num)
{
	long long result = 0;
	for (int i = 1; i <= num; i++)
	{
		long long pow = 1;
		for (int j = 1; j <= i; j++)
		{
			pow *= i;
		}
		result += pow;
		printf("%d %lld\t %lld\n", i, pow, result);
	}
	return result;
}

void gotoPractice()
{
	for (int i = 1; i <= 3; i++)
	{
		for (int j = 1; j <= 5; j++)
		{
			printf("inner loop executing %d\n", j);
			// break; // exit inner loop
			goto a;
		}
		printf("inner loop done\n");
	}

	// 标号
	a: printf("outer loop done\n");
}

void array()
{
    int nums[4][2] = {
        {1, 2},
        {3, 4},
        {5, 6},
        {7, 8},
    };

    int i, j;
    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 2; j++)
        {

            printf("nums[%d][%d]: %d\n", i, j, nums[i][j]);
        }
    }
}

void address()
{
    int age = 30;
    double gpa = 3.4;
    char grade = 'A';
    printf("age: %p\ngpa: %p\ngrade: %p\n", &age, &gpa, &grade);
}

void pointerAddress()
{
    int age = 30;
    int *pAge = &age;
    double gpa = 3.4;
    double *pGpa = &gpa;
    char grade = 'A';
    char *pGrade = &grade;

    // printf("age's memory address: %p \n", pAge);
    // dereferencing a pointer variable
    printf("pointer's value: %d \n", *pAge);
    // getting the physical address and then dereferencing it
    printf("%d", *&age);
}

void writeFile()
{
    // w => write
    // a => append
    FILE *fpointer = fopen("employees.txt", "a");
    // write info to a file
    fprintf(fpointer, "OVERRIDDEN!\nJim, Salesman\nPam, Receptionist\nOscar, Accounting\n");

    fclose(fpointer);
}

void readFile()
{
    char line[255];
    // r => read
    FILE *fp = fopen("employees.txt", "r");

    // read file line by line
    fgets(line, 255, fp);
    fgets(line, 255, fp);
    printf("%s", line);
    fclose(fp);
}

void helloWorld()
{
    printf("Hello World!\n");
}

void testChar()
{
    printf("%c\n", 'a');
    printf("%d\n", 'a');
    printf("%c\n", '\0');
}

void testVariable()
{
    // Student data
    int studentID = 15;
    int studentAge = 23;
    float studentFee = 75.25;
    char studentGrade = 'B';

    // Print variables
    printf("Student id: %d\n", studentID);
    printf("Student age: %d\n", studentAge);
    printf("Student fee: %f\n", studentFee);
    printf("Student grade: %c\n", studentGrade);

    int num1, num2;

    printf("Please input two int: \n");
    scanf("%d %d", &num1, &num2);
    printf("You've entered: %d %d\n", num1, num2);

    float sum = (float) 5 / 2;
    printf("%f.1\n", sum);

    const float PI = 3.14;
}

void testSign()
{
    signed int num;
    printf("%d output singed int\n", +32);
    printf("%hd output singed short\n", -2);
    printf("%ld output singed long\n", -6553589102);

    unsigned int num2;
    printf("%u ouput unsigned int\n", 32);
    printf("%hu output unsinged short\n", 2);
    printf("%lu output unsigned long\n", 6553589102);
}

void testNumber()
{
    // Octal 八进制
    // hexadecimal 十六进制
    // true form 原码
    // base minus one's complement 反码
    // Complement Code 补码
    // negative number are saved in Complement Code
    // non-negative number are saved in true form
    // octal in true form
    // hexadecimal in true form
    char ch1 = -10;
    printf("ch1=%#x\n", ch1);
    // ch1=0xfffffff6

    char ch2 = 6;
    printf("ch2=%#x\n", ch2);
    // ch2=0x6

    unsigned char ch = -10;
    printf("%#x\n", ch); // 0xf6
    printf("%d\n", ch); // 246
    printf("%u\n", ch); // 246

    // read-only variable initialize
    const int num = 100;

    // register variable
    // cannot use &freq
    register int freq = 10;

    // volatile
    // 1.强制访问内存
    // 2.防止编译器优化
    // 3.sizeof测量类型大小
    volatile int force = 10;

}

void testSizeOf()
{
    int num = 0;
    printf("%lu\n", sizeof(num)); // 4
    printf("%lu\n", sizeof(int)); // 4
    printf("%lu\n", sizeof(short)); // 2
    printf("%lu\n", sizeof(long)); // 8

    char ch = 'a';
    printf("%lu\n", sizeof(ch)); // 1
    printf("%lu\n", sizeof('a')); // 4 (ASCII)
}

    // typedef
    // 给已有类型重新取别名
    // 1.不能创建新类型
    // 2.旧类型任然可用
void testTypeDef()
{
    typedef int MY_ARRAY[5];
    MY_ARRAY arr = {1, 2, 3, 4, 5};
    int i = 0;

    for (i = 0; i<5; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

/**
* type auto change: ensure accuracy
* low => high
* char,short => signed int => unsigned int => long => double
* float => double
*/
void testTypeChange()
{
    int data1 = -10;
    unsigned int data2 = 6;
    if (data1 + data2 > 0)
    {
        printf(">0");
    }
    else
    {
        printf("<0");
    }
    // >0
}

int main(int argc, char *argv[])
{
    helloWorld();
    testChar();
    testVariable();
    testSign();
    testNumber();
    testSizeOf();
    testTypeDef();
    testTypeChange();

    // stringDisplay();
    // arrayDisplay();
    // invokeFunc();
    // printf("Answer: %f\n", cube(3.0));
    // calculate();
    // switchCondition();
    // useStruct();
    // whileLoop();
    // doWhileLoop();
    // loop();
    // forLoop();
    // array();
    // address();
    // pointerAddress();
    // writeFile();
    readFile();
    return 0;
    return 0;
}

Strutctures 2

// memory alignment
struct stuct_name {
    int8_t var1;
    int8_t var2;
    int32_t var3;
} __attribute__((packed));

// Create a structure called myStructure
struct myStructure {
  char myLetter;
  char myString[30]; // String
  int myNum;
};

struct Car {
  char brand[50];
  char model[50];
  int year;
};

int main(int argc, char* argv[]) {
  // Create a structure variable of myStructure called s1
  struct myStructure s1;

  // Assign values to members of s1
  s1.myNum = 13;
  s1.myLetter = 'B';
  // Assign a value to the string using the strcpy function
  strcpy(s1.myString, "Some text");

  // Print values
  printf("My number: %d\n", s1.myNum);
  printf("My letter: %c\n", s1.myLetter);
  printf("My string: %s", s1.myString);

  struct Car car1 = {"BMW", "X5", 1999};
  struct Car car2 = {"Ford", "Mustang", 1969};
  printf("%s %s %d\n", car1.brand, car1.model, car1.year);

  return 0;
}

Escape Characters

escape character meaning
\n New line
\t Tab
\0 Null
\0 ASCII 0
\r to line start
\a alert

each d range 0~7, max 3 octal digit in total

each h range 0~9, a~f, max 2 hex digit in total

#include <string.h>

char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
printf("%d", strlen(alphabet));   // 26
printf("%d", sizeof(alphabet));   // 27
// sizeof  also includes the `\0` character when counting

char alphabet[50] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
printf("%d", strlen(alphabet));   // 26
printf("%d", sizeof(alphabet));   // 50
// sizeof will always return the memory size (in bytes), and not the actual string length

char str1[20] = "Hello ";
char str2[] = "World!";
strcat(str1, str2);
// Print str1
printf("%s", str1);

char str1[20] = "Hello World!";
char str2[20];
// Copy str1 to str2
strcpy(str2, str1);
printf("%d\n", strcmp(str1, str2));  // Returns 0 (the strings are equal)

User input

// Create an integer variable that will store the number we get from the user
int myNum;

// Ask the user to type a number
printf("Type a number: \n");

// Get and save the number the user types
scanf("%d", &myNum);

char firstName[30];
scanf("%s", firstName);
// you don't have to specify the reference operator (&) when working with strings in scanf()

// Output the number the user typed
printf("Your number is: %d", myNum);

char fullName[30];
printf("Type your full name: \n");
fgets(fullName, sizeof(fullName), stdin); // read a line of text

Memory Address VS Pointers

A pointer is a variable that stores the memory address of another variable. Instead of holding a data value directly, a pointer holds the address where the data is stored. This allows for indirect access and manipulation of the variable’s value.

All Pointers are the same length

uint8_t * ptr1 = (uint8_t *) 0x00;
uint16_t * ptr2 = (uint16_t *) 0x04;
uint32_t * ptr3 = (uint32_t *) 0x08;
float * ptr4 = (uint8_t *) 0x0C;

// 32-bits!!
sizeof(uint8_t*) = sizeof(uint16_t*) = sizeof(uint32_t*) = sizeof(float*)
// 32-bits!!
sizeof(ptr1) = sizeof(ptr2) = sizeof(ptr3) = sizeof(ptr4)

sizeof(*ptr1) // 1 Byte
sizeof(*ptr2) // 2 Bytes
sizeof(*ptr3) // 4 Bytes
sizeof(*ptr4) // 4 Bytes

data_type * pointer_name

* means:

int myAge = 43;
printf("%p", &myAge); // Outputs 0x7ffe5367e044
// A pointer variable, with the name ptr, that stores the address of myAge
int* ptr = &myAge;
// Output the memory address of myAge with the pointer (0x7ffe5367e044)
printf("%p\n", ptr);
// Dereference: Output the value of myAge with the pointer (43)
printf("%d\n", *ptr);

// to assign a direct address to a pointer
// casts integer to address
// does not change the value of the address
// tells the compiler to interrupt this number as an address
uint16_t * ptr = (uint16_t *) 0x480C0000;

Pointers

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

void demo()
{
	int a = 10;
	int* p = &a;

	printf("%d\n", *p);

	*p = 200;

	printf("%d\n", *p);
	printf("%d\n", a);

	char c = 'a';
	char* p1 = &c;

	long long n = 100;
	long long* p2 = &n;

	printf("%zu\n", sizeof(p1)); // 8
	printf("%zu\n", sizeof(p2)); // 8
}

void swap(int* p1, int* p2)
{
	int temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}

void demo2()
{
	int a = 10;
	int b = 20;
	printf("Before swap: %d %d\n", a, b);
	swap(&a, &b);
	printf("After swap: %d %d\n", a, b);
}

void calcMaxAndMin(int arr[], int len, int* max, int* min)
{
	for (int i = 1; i < len; i++)
	{
		if (arr[i] > *max)
		{
			*max = arr[i];
		}
		if (arr[i] < *min)
		{
			*min = arr[i];
		}
	}
}

void demo3()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int len = sizeof(arr) / sizeof(int);
	int max = arr[0];
	int min = arr[0];
	calcMaxAndMin(arr, len, &max, &min);
	printf("Max: %d Min: %d\n", max, min);
}

int getRemainder(int a, int b, int* res)
{
	if (b == 0)
	{
		return 1;
	}
	*res = a % b;
	return 0;
}

void demo4()
{
	int a = 10, b = 3;
	int res = 0;
	int flag = getRemainder(a, b, &res);
	if (!flag)
	{
		printf("Remainder: %d\n", res);
	}
}

void demo5()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

	int* p = &arr[0];

	printf("%d\n", *p);
	printf("%d\n", *(p + 1));

	int* p2 = &arr[5];
	printf("%d\n", p2 - p);
	printf("%p\n", p);
	printf("%p\n", p2);
}

int* method()
{
	int a = 100;
	return &a;
}

void demo6()
{
	int a = 10;
	int* p = &a;

	printf("%p\n", p);
	printf("%d\n", *p);

	// wild pointer;

	int* p2 = p + 10;
	printf("%p\n", p2);
	printf("%d\n", *p2);

	// empty pointer
	int* p3 = method();
	printf("take some other task and time spent\n");
	printf("%d\n", *p3);
}

void generalSwap(void* p1, void* p2, int len)
{
	char* pc1 = p1;
	char* pc2 = p2;

	char temp = 0;

	for (int i = 0; i < len; i++)
	{
		temp = *pc1;
		*pc1 = *pc2;
		*pc2 = temp;
		pc1++;
		pc2++;
	}
}

void demo7()
{
	int a = 10;
	short b = 20;

	int* p1 = &a;
	short* p2 = &b;

	printf("%d\n", *p1);
	printf("%d\n", *p2);

	// void* p4 = p1;
	// void* p5 = p2;
	int* c = 100;
	int* d = 200;

	generalSwap(&c, &d, sizeof(int));
	printf("c = %d, d = %d\n", c, d);
}

void demo8()
{
	int a = 10;
	int b = 20;
	int* p = &a;

	int** pp = &p;
	*pp = &b;

	printf("%p\n", &a);
	printf("%p\n", &b);
	printf("%p\n", p);

	printf("%d\n", **pp);
}

/**
    Arrays and pointers are closely related in C. 
    The name of an array is a constant pointer to its first element. 
 */
void demo9()
{
	// get array pointer address

	int arr[] = { 10, 20, 30, 40, 50 };
	int len = sizeof(arr) / sizeof(int);

	int* p1 = arr;
	int* p2 = &arr[0];
	int* p3 = &arr;
	printf("%zu\n", sizeof(arr)); // 20;

	printf("%p\n", p1); // 000000C05E0FF8B8
	printf("%p\n", p2); // 000000C05E0FF8B8
	printf("%p\n", p3); // 000000C05E0FF8B8

	printf("%p\n", arr + 1);
	printf("%p\n", &arr + 1);

    // Here, ptr is assigned the address of the first element of arr. 
    // You can use pointer arithmetic to traverse the array:
	for (int i = 0; i < len; i++)
	{
		printf("%d\n", *p1++);
        // printf("%d ", *(p1 + i));
	}
}

void demo10()
{
	int arr[3][5] =
	{
		{1, 2, 3, 4, 5},
		{11, 22, 33, 44, 55},
		{111, 222, 333, 444, 555},
	};
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			printf("%d\t", arr[i][j]);
		}
		printf("\n");
	}
	printf("---------------\n");

	int arr1[3] = { 1, 2, 3 };
	int arr2[5] = { 1, 2, 3, 4, 5 };
	int arr3[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	int* arra[3] = {arr1, arr2, arr3};

	int len1 = sizeof(arr1) / sizeof(int);
	int len2 = sizeof(arr2) / sizeof(int);
	int len3 = sizeof(arr3) / sizeof(int);
	int lenArr[3] = { len1, len2, len3};
	for (int i = 0; i < 3; i++)
	{
		// int len = sizeof(arra[i]) / sizeof(int); // 2
		for (int j = 0; j < lenArr[i]; j++)
		{
			printf("%d\t", arra[i][j]);
		}
		printf("\n");
	}
}

void demo11a()
{
	int arr[3][5] =
	{
		{1, 2, 3, 4, 5},
		{11, 22, 33, 44, 55},
		{111, 222, 333, 444, 555},
	};

	printf("%p\n", arr);
	printf("%p\n", arr + 1);

	int(*p)[5] = arr;

	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			printf("%d\t", *(*p + j));
		}
		printf("\n");
		p++;
	}
}

void demo11b()
{
	int arr1[5] = { 1, 2, 3, 4, 5 };
	int arr2[5] = { 11, 22, 33, 44, 55 };
	int arr3[5] = { 111, 222, 333, 444, 555 };
	int* arr[3] = { arr1, arr2, arr3 };

	int** p = arr;

	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			printf("%d\t", *(*p + j));
		}
		printf("\n");
		p++;
	}
}

void methodA();
int methodB(int a, int b);

/**
* function pointer
*/
void demo12()
{
	void (*a)() = methodA;
	int (*b)(int, int) = methodB;

	a();
	int res = b(3, 4);
	printf("%d\n", res);
}

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

int subtract(int a, int b)
{
	return a - b;
}

int multiply(int a, int b)
{
	return a * b;
}

int divide(int a, int b)
{
	return a / b;
}

/**
 * function pointer array
 */
void demo13()
{
	int (*p[4])(int, int) = { add, subtract, multiply, divide };

	int choose = 1;
	printf("Please input two number: \n");

	int num1;
	int num2;
	scanf("%d %d", &num1, &num2);

	printf("%d\n", num1);
	printf("%d\n", num2);
	printf("Please input a number to operate(1~4): \n");
	scanf("%d", &choose);

	int res = (p[choose - 1])(num1, num2);
	printf("Result: %d\n", res);
}

void userInput(float * num1, float * num2, float* num3, float* num4)
{
    printf("Enter the first number: ");
	fflush(stdout);
	scanf("%f", num1);
	printf("\nEnter the second number: ");
	fflush(stdout);
	scanf("%f", num2);
	printf("\nEnter the third number: ");
	fflush(stdout);
	scanf("%f", num3);
	printf("\nEnter the fourth number: ");
	fflush(stdout);
	scanf("%f", num4);
}

int main(int argc, char* argv[])
{
	// demo();
	// demo2();

	// demo3();
	// demo4();
	// demo5();
	// demo6();
	// demo7();
	// demo8();
	// demo9();
	// demo10();

	// demo11a();
	// demo11b();

	// demo12();
	// demo13();
    float num1, num2, num3, num4;
	float avg;
    userInput(&num1, &num2, &num3, &num4);
    avg = (num1 + num2 + num3 + num4)/4;
	printf("Average is: %f\n", avg);

	printf("Press any key to exit the application");
	getchar();
    getchar();
}

void methodA()
{
	printf("methodA\n");
}

int methodB(int a , int b)
{
	printf("methodB\n");
	return a + b;
}

Null Pointers

At time of pointer declaration, you might not know the address

// this pointer will have garbage data
uint32_t * ptr;

#define NULL((void*)0)
uint32_t * ptr = NULL;

if (ptr == NULL)
{
    /* error */
}
*ptr = 0xABCD1234;

Endianness

/* Switches endinannes of variable pointed by ptr */
void byte_swap32(uint32_t * ptr)
{
    uint8_t i, temp_byte;
    for (i=0; i<2; i++)
    {
        temp_byte = *((uint_t*)ptr + (3-i));
        *((uint8_t*)ptr + (3-i)) = *((uint8_t*)ptr + i)
        *((uint8_t*)ptr + i) = temp_byte;
    }
}

/* Assume little endian */
void main()
{
    uint32_t var = 0xABCD1234;
    uint32_t * ptr = &var;

    byte_swap32(ptr);
    while(1);
}

volatile

Structure overlay

Define a Structure to directly match peripherl region registers

typedef struct {
    __IO uint16_t   CTL;
    __IO uint16_t   CTTL[7];
    __IO uint16_t   R;
    __IO uint16_t   CCR[7];
    __IO uint16_t   EX0;
    uint16_t   RESERVED0[6];
    __I uint16_t   IV;
} Timer_A_Type;

#define __IO(volatile)
#define __I(volatile const)

/* Define the base address of peripheral regions */
#define PERIPH_BASE     ((uint32_t)0x40000000)
#define TIMER_A0_BASE   (PERIPH_BASE + 0x00000000)
#define TIMER_A1_BASE   (PERIPH_BASE + 0x00000400)
#define TIMER_A2_BASE   (PERIPH_BASE + 0x00000800)

/* Multiple Timer modules, different addresses */
#define TIMER_A0    ((Timer_A_Type *)TIMER_A0_BASE)
#define TIMER_A1    ((Timer_A_Type *)TIMER_A1_BASE)
#define TIMER_A2    ((Timer_A_Type *)TIMER_A2_BASE)

// Example use of Structure overlay:
TIMER_A0->CTL = 0x0202;

Macro Functions

Direct substitution in code

#define TA0CTL_ADDR (0x40000000)
/* 8, 16, & 32 Bit Register Access Macros */
#define HWREG8(x) (*((volatile uint8_t *)(x)))
#define HWREG16(x) (*((volatile uint16_t *)(x)))
#define HWREG32(x) (*((volatile uint32_t *)(x)))

#define TA0CTL (HWREG16(0x40000000))

/* example use of access methods: */
HWREG16(TA0CTL_ADDR) = 0x0202;
// SAME AS
(*((volatile uint16_t *)(0x40000000))) = 0x0202;

/* example use of access macro */
TA0CTL = 0x0202;
// SAME AS
volatile uint16_t * ta0_ctl = (uint16_t*)TA0CTL_ADDR;
*ta0_ctl = 0x0202;

MACRO FUNCTION SUBSTITUTION

PORT MACROS

/* Port 1 Register Access Macros */
#define P1IN (HWREG8(0x40004C00))
#define P1OUT (HWREG8(0x40004C02))
#define P1DIR (HWREG8(0x40004C04))
#define P1SEL0 (HWREG8(0x40004C0A))
#define P1SEL1 (HWREG8(0x40004C0C))
#define P1IES (HWREG8(0x40004C18))
#define P1IE (HWREG8(0x40004C1A))

/* set P1.0 to output direction */
P1DIR |= 0x01;

typedef struct {
    __I uint8_t IN;
    uint8_t RESERVED0;
    __IO uint8_t OUT;
    uint8_t RESERVED1;
    __IO uint8_t DIR;
    uint8_t RESERVED2;
    __IO uint8_t REN;
    uint8_t RESERVED3;
    /* ... MORE REGISTERS */
} DIO_PORT_TYPE;

/* Define the PORT Constants and Types */
#define PIN0(0x0)
#define DIO_PORT1_ADDR ((uint32_t)0x40004C00)
#define P1((DIO_PORT_TYPE*)(DIO_PORT1_ADDR))

#define SET_PORT_PIN_DIR (port, pin) ((port)->DIR |= (1<<pin))
/* set P1.0 to output direction */
SET_PORT_PIN_DIR(P1, PIN0)

Bit Band Macros

BIT Band Macros

#define TA0CTL_ADDR(0x40000000)
/* Bit Band Region is offset from Peripheral/SRAM */
#define BB_OFFSET(0x02000000)
/* Macro Function to Read Memory */
#define HWREG32(addr)(*((volatile uint32_t *)(addr)))
/* Bit Band Alias Offset Address */
#define BITBAND_ADDR(addr, bit)((addr & 0xF0000000) + BB_OFFSET + \
                                ((addr & 0xFFFFF) << 5) + (bit << 2))
/* Set bit 1 of TA0CTL Register */
HWREG32(BITBAND_ADDR(TA0CTL_ADDR, 1))

Macro problems

// Directive used for Boolean Compilation Conditions

/* Define feature for the MSP */
#define MSP_PLATFORM

#define TEN     (10)
/* undefine the feature */
#undef TEN

#define COMPILE_CODE
// ...
#ifdef COMPILE_CODE
// Code will be compiled
#endif
#define DO_NOT_COMPILE_CODE
// ...
#undef DO_NOT_COMPILE_CODE
#ifdef DO_NOT_COMPILE_CODE
// Code will NOT be compiled
#endif

Compile Time switch

Condition provided at Compile time to dicate WHAT should be compiled, by use combination of #if-else and #define directives, for the following example: Add extra option to gcc command to deinfe Macro

# -D<MACRO-NAME>
gcc -DMSP_PLATFORM -o main.out main.c
#if defined ( KL25_PLATFROM ) && !defined ( MSP_PLATFORM)
  kl25_init();
#elif defined ( MSP_PLATFORM ) &&  !defined( KL25_PLATFROM )
  msp_init();
#else 
  #error "Plaese specify one platfrom target"
#endif

Specialized C Functions

inline Keyword

Skips calling convention, copies function body into calling code


__attribute__((always_inline)) inline int32_t add(int32_t x, int32_t y)
{
    return (x+y);
}

static keyword

static keyword can apply to functions to create private access

/* core_cm4.h */
__attribute__((always_inline)) static inline void __enable_irq(void)
{
    __ASM volatile ("cpsie i":::"memory");
}

Interface Libray for GPIO

/* IO_MSP432.h */
#include <stdio.h>
#include "msp.h"

__attribute__((always_inline)) static inline void IO_Read(DIO_PORT_TYPE * port, uint8_t pin)
{
    return (((port)->IN) & (1<<pin));
}

__attribute__((always_inline)) static inline void IO_Write(DIO_PORT_TYPE * port, uint8_t pin, uint8_t value)
{
    value ? (((port)->OUT) |= (1<<pin)) : (((port)-> OUT) &= ~(1<<pin));
}
/* Platform.h */
#if defined (KL25Z) && !defined (MSP432)
#inclue "IO_KL25Z.h"
#elif defined(MSP432) && !defined (KL25Z)
#inclue "io_msp432.h"
#else
#error "Platform not properly specified"
#endif
make all PLATFORM=MSP432

Advanced Pointers

Memories of an Embedded System

void * ptr1 = NULL;
void ** ptr2 = &ptr1;
uint32_t * restrict ptr3;
uint32_t ** ptr4;

sizeof (uint8_t *)  = sizeof (void *)
                    = sizeof (void **)
                    = sizeof (uint32_t **)
                    = sizeof (uint32_t * restrict)
                    // = 32 bits

sizeof(ptr1)        = sizeof(ptr2)
                    = sizeof(ptr3)
                    = sizeof(ptr4)
                    // = 32 bits

Void Pointer

Void pointers are Generic Pointers, they point to a memory address

#define NULL(void *(0))
void *ptr1 = NULL;

void * ptr1 = (void*)0x40000000;
*((uint16_t*)ptr1) = 0x0202;
// equivalent to:
TA0CTL = 0x0202;

char * ptr;
ptr = (char *)malloc(8*sizeof(char));

if (ptr == NULL)
{
    /* Allocation Failed!! */
    /* ...Handle Failure */
}
/* Other code */
free((void*)ptr);

Double Pointer

Double pointers are a pointer to a pointer, This is useful for multidimensional arrays or dynamic memory management.

uint32_t var = 0x1234ABCD;
uint32_t * ptr3 = &var;
uint32_t ** ptr4 = &ptr3;

sizeof (float **)   = sizeof(uint8_t**)
                    = sizeof(void **)
                    = sizeof(uint32_t **)
                    // = 32 bits

typedef enum {
    RSP_TYPE_1 = 0,
    RSP_TYPE_2 = 1,
} RSP_e;

typedef struct {
    RSP_e rsp_type;
    uint8_t data[4];
} rsp1;

int8_t create_rsp1 (rsp1 ** r_p)
{
    *r_p = (rsp1 *)malloc (sizeof(rsp1));

    if (*r_p == NULL)
    {
        /* allocation failed */
        return -1;
    }
    (*r_p)-> rsp_type = RSP_TYPE_1;
    return 0;
}
int value = 10;
int *ptr = &value;
int **ptr_to_ptr = &ptr;
// Here, ptr_to_ptr holds the address of ptr, which in turn holds the address of value.

Dynamic Memory Allocation

Pointers are essential for dynamic memory allocation, which allows you to allocate memory at runtime. The standard library functions malloc(), calloc(), realloc(), and free() are used for this purpose.

int *arr = (int *)malloc(5 * sizeof(int));
if (arr == NULL) {
    // Handle memory allocation failure
}
for (int i = 0; i < 5; i++) {
    arr[i] = i + 1;
}
free(arr);
// This code allocates memory for an array of 5 integers, initializes the array, and then deallocates the memory.

Restrict Qualified Pointer

Restrict type qualifier helps compiler to optimize memory interactions

uint32_t * restrict ptr4;


sizeof (float *)   = sizeof(uint8_t*)
                    = sizeof(void *)
                    = sizeof(uint32_t * restrict)
                    // = 32 bits

Function Pointers

Pointers can also point to functions. This allows you to pass functions as arguments to other functions or store them in data structures. Function pointers are declared using the function’s signature.

int add(int a, int b) {
    return a + b;
}
int (*func_ptr)(int, int) = &add;
printf("Result: %d\n", func_ptr(2, 3));
// Here, func_ptr is a pointer to the add function, and it is used to call the function.

/* A function pointer variable declaration that returns a int8_t type */
int8_t (*foo)();
/* A function declaration that return a int8_t pointer type */
int8_t *foo();

// more function pointer examples
int8_t void (*bar)(int8_t a, int8_t *b);
uint32_t (* func)(uint8_t param);

sizeof (void(*))    = sizeof(void *)
                    = sizeof(uint32_t *)
                    // = 32 bits

// call
(* foo)();
// or
foo();

/* Function bar prototype */
int8_t bar();
/* function pointer */
int8_t (*foo)() = &bar;

// function pointer array
typedef void (* FuncPtr_t[2])();

FuncPtr_t example =
{
    foo,
    bar
};

// alternatively
typedef void (* FuncPtr_t());
FuncPtr_t example[2] =
{
    foo,
    bar
}

typedef enum
{
    FP_FOO = 0,
    FP_BAR = 1,
} FP_e;

// example calls:
example[FP_FOO]();
example[FP_BAR]();

Interrupt vector table

Interrupts are special events that request the CPU to perform a specific operation

Common Pitfalls and Best Practices

Using pointers comes with certain risks and challenges. Here are some common pitfalls and best practices to keep in mind:

  1. Uninitialized Pointers: Always initialize pointers before using them. Uninitialized pointers contain garbage values and can lead to undefined behavior.
  2. Dangling Pointers: Avoid using pointers that point to memory that has been deallocated. This can lead to crashes or corrupt data.
  3. Null Pointers: Check if a pointer is NULL before dereferencing it. This prevents accessing invalid memory.
  4. Pointer Arithmetic: Be cautious with pointer arithmetic. Ensure that you do not access memory outside the bounds of the allocated memory.
  5. Memory Leaks: Always deallocate dynamically allocated memory using free() to prevent memory leaks.
  6. Type Safety: Ensure that pointers point to the correct data type to prevent type mismatches and unexpected behavior.

Advanced Pointer Topics

  1. Pointer Arrays: Arrays of pointers are used for dynamic lists, such as an array of strings (array of char*).
  2. Pointer Casting: You can cast pointers from one type to another, but do so with caution to avoid undefined behavior.
  3. Function Pointers in Structures: Function pointers can be included in structures to implement callback mechanisms or function tables.
  4. Memory Mapping: Pointers are used in systems programming for memory-mapped I/O, where hardware registers are accessed through specific memory addresses.

The stdint.h header file defines fixed-width integer types such as int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, and uint64_t. These types ensure that integers have a specific width (in bits) across different platforms, which is crucial for portability and reliability in embedded systems.

Operator

Operator Precedence

Bitwise operation

Bitwise operators perform operations on the binary representations of data. Unlike arithmetic operators that work on entire values, bitwise operators manipulate individual bits of the operands. The primary bitwise operators in C are:

applications cases are:

#include <stdio.h>

int main(void)
{
    int num;
    printf("Please enter a number");
    scanf("%d", &num);
    if (num & 1)
        printf("%d is odd number\n", num);
    else
        printf("%d is even number\n", num);

    int x = 0x7b;
    printf("%#x", x);
    // 0x7b
    
    x ^= (1 << 5); // toggle the fifth bit of a variable x
    printf("%#X", x);
    // 0X5B
    
    x &= ~(1 << 4); // clear the fourth bit of a variable x
    printf("%#X", x);
    // 0X4B

    int a = 12;  // Binary: 1100
    int b = 10;  // Binary: 1010
    int result = a & b;  // Binary: 1000, Decimal: 8
    int result = a | b;  // Binary: 1110, Decimal: 14
    int result = a ^ b;  // Binary: 0110, Decimal: 6

    int a = 12;  // Binary: 00001100
    int result = ~a;  // Binary: 11110011, Decimal: -13 (in 2's complement form)

    int a = 12;  // Binary: 1100
    int result = a << 2;  // Binary: 110000, Decimal: 48

    int a = 12;  // Binary: 1100
    int result = a >> 2;  // Binary: 0011, Decimal: 3

    uint8_t * ptr = (uint8_t*)0x1000;
    // set 4th bit without changing other bits:
    *ptr |= 0x10

    // clear 4th bit without changing other bits:
    *ptr &= ~(0x10)

    // toggle 4th bit
    *ptr ^= 0x10
    return 0;
}

Setting a Bit: To set a specific bit to 1, use the OR operator with a bitmask.

int num = 10;  // Binary: 1010
int bit_to_set = 1;  // Set the second bit (from the right)
num |= (1 << bit_to_set);  // Result: 1010 | 0010 = 1110 (Decimal: 14)

Clearing a Bit: To clear a specific bit to 0, use the AND operator with a bitmask.

int num = 14;  // Binary: 1110
int bit_to_clear = 1;  // Clear the second bit (from the right)
num &= ~(1 << bit_to_clear);  // Result: 1110 & 1101 = 1100 (Decimal: 12)

Toggling a Bit: To toggle a specific bit, use the XOR operator with a bitmask.

int num = 10;  // Binary: 1010
int bit_to_toggle = 1;  // Toggle the second bit (from the right)
num ^= (1 << bit_to_toggle);  // Result: 1010 ^ 0010 = 1000 (Decimal: 8)