Skip to content

Instantly share code, notes, and snippets.

@littleblacklb
Last active November 14, 2022 12:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save littleblacklb/ef27515ad9aeae13f956d6c5f302a4d5 to your computer and use it in GitHub Desktop.
Save littleblacklb/ef27515ad9aeae13f956d6c5f302a4d5 to your computer and use it in GitHub Desktop.

C Pointer

https://www.bilibili.com/video/BV1bo4y1Z7xf

https://gist.github.com/LittleBlacklb/ef27515ad9aeae13f956d6c5f302a4d5

There must be some grammatical errors here, please forgive me :(

Initialization

<data type> *<variable name>;: The pointer to the <data type> of <variable name>

& (ampersand)

&<variable>: Return the address of the variable

* (asterisk)

*<variable>: Return the value at variable's address To access the address of the variable in format

The asterisk meaning between int* p and *p are not the same.

int* is a type that a pointer point to a data of int type.

*p is to dereference the address which stored in variable p.

Pointer arithmetic

int a = 1024;
int *p;
p = &a;  // Assume variable `a` 's address is 2077 
cout << p << endl;  // 2077 in decimal
cout << p + 1 << endl; // 2081 in decimal
int a = 1025;
int* p;
p = &a;
printf("Address=%d; value=%d\n", p, *p);

char* p0;  // 1 char: 1 byte = 8 bit
p0 = (char*)&a;  // Cast int* to char*

// 1025 in memory (small-endian order):
// ... 00000000     00000000    00000100    00000001 ...
// ...(p + 3) ^    (p + 2) ^   (p + 1) ^   (p + 0) ^ ...
// '^' means toward bit
printf("%d\n", (int)*p0);  // 0000100 => 1
printf("%d\n", (int)*(p0 + 1)); // 00000001 => 4

Void Pointer - Generic Pointer

void* <variable name>;

It can't be used to print the value that it points.

It can be used to print itself address.

Char Array

char c[] = "ABC";: It will be stored in the space for array (Stack).

const char* c = "Hello";: It will be stored as compile time constant, so it cannot be modified (Like: c[0] = 'a')

Array & Pointer

The relationship between Array & Pointer.

arr[x] <==> *(arr+x)

(You need to know that arr is a const variable, so it cannot be assigned (Like: arr = var)

It just the same as the relationship between Reference & Pointer

int &a = var <==> int* const a = &var

return a <==> return *a

The array in function parameters

// ...
int get_size(int arr[])
{
    return sizeof(arr) / sizeof(arr[0])       
}

Most of the time, return result is 1 (Depend on the platfrom)

Because the declaration of int arr[] is actually a int pointer which points to the first address of the array.

Compiler translates int arr[] => int* arr

So the variable of arr 's size is the size of pointer.

The purpose is to reduce the time and space cost.(It don't need to copy the array)

mulit-dimensional array

2-dimensional array:

a[i][j] => *(a[i]+j) => *(*(a+i)+j)

a's type: int (*)[]

a[i]'s type: int*

3-dimensional array:

a[i][j][k] => *(a[i][j] + k) => *(*(a[i] + j ) + k ) => *(*(*(a + i) + j ) + k

a's type: int(*)[][]

a[i]'s type: int(*)[]

a[i][j]'s type: int*

Diffence between array type and pointer type

Yes! They are not the same type. You can't consider them as the same type!

int arr[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int (*p)[3] = arr;
printf("%d\n", p);
printf("%d\n", *p);

This circumstance only happens in static creation!

p and *p's output are both equal

Differences:

p: It's a one-dimensional pointer;

*p: It's a int* pointer.

Explanation

type of array inequals to pointer

In the other word, array is a special pointer.

What is the point of defining this?

  1. It's a fake-pointer (pointer's address = the address it's point to) Because the static array is continuous. But dynamic multi-dimension array is not continuous from row to row.

  2. It needs each row's capacity. For example: sizeof(int[3]) = 12 As we all know, sizeof(int) * 3 = 12 bytes So when you do operator like +, C can know plus 1 equal to how many bytes

Much deeper understanding

Static:

int arr[3][3];
for (int y = 0; y < 3; y++)
{
    for (int x = 0; x < 3; x++)
    {
        // These following three are all equivalent
        *(arr[y]+x) = 3 * y + x;
        *(*(arr + y) + x) = 3 * y + x;
        *((*arr + 3 * y) + x) = 3 * y + x;  // Important!
      }
}
int (*p)[3] = arr;
// All equal
printf("%d\n", p);
printf("%d\n", *p);

pic

int b[3][1];
int (*p)[1] = b;
// int **p = b;  // Syntax Error

Dynamic:

int **arr = (int **)calloc(3, sizeof(int *));
for (int y = 0; y < 3; y++)
{
    *(arr + y) = (int *)calloc(3, sizeof(int));
    for (int x = 0; x < 3; x++)
    {
      // ...
    }
}
int** p = arr;
// Not equal
printf("%d\n", p);
printf("%d\n", *p);

It doesn't need [] type for helping.

Because it's structure is different from static array.

It's discontinuous, and is similar to deque in STL:

pic

一段一段的连续空间's beginning address are different.

StackVsHeap

Data in memory

High Bit & Low Bit

注意高字节在左边,低字节在右边

    1           1           1           1

High Bit                          Low Bit

Byte Order(Endian): Big Endian(大端) & Small Endian(小端)

0b1001111

Big Endian

Low address in mem -------------> High address in mem

1   0   0   1   1   1   1

High Bit <----------------------- Low Bit

BigEndian

Small Endian

Low address in mem -------------> High address in mem
1   1   1   1   0   0   1
Low Bit <------------------------ High Bit

SmallEndian

LSB (Least Significant Bit): 最低有效位

0b10010101

The bold underlined bit above called Least Significant Bit In Big-Endian order, LSB refers to the rightest bit.

Heap & Stack

  • 代码区:存放函数体的二进制代码,由操作系统进行管理的

  • 全局区:存放 全局变量静态变量 以及常量

  • 栈区:由编译器自动分配释放, 存放函数的参数值,局部变量

  • 堆区:由**程序员分配和释放**,若程序员不释放,程序结束时由操作系统回收

Method of using dynamic memory (heap segment)

C:

Functions:

  • void *malloc(size_t size)
  • void *calloc(size_t nitems, size_t size)`
  • void *realloc(void *ptr, size_t size)
  • void free(void *ptr)

C++:

Key Words:

  • new <variable>
  • delete <variable>
  • delete[] <variable>

malloc

Signature:

void *malloc(size_t size)

size_t: Positive number. The exact data type depend on the platform (unsigned int, etc.)

Usage:

int* p = (int*)malloc(4 * sizeof(int))
  • Replacing sizeof(int) with 4 is not recommanded. Because the space of a data type depends on the compiler(platform)

  • Malloc function return a generic pointer (void*), so we need to do a type casting.

calloc

Signature:

void *calloc(size_t nitems, size_t size)`

size_t nitems: Numbers of elements of specific data type

Difference:

  • It can be more convenient to allocate a continuous memory.

  • It will initialize the allocated memory with 0.

realloc

Signature:

void *realloc(void *ptr, size_t size)

Application Scenarios:

  • The new memory block may bigger than the original one.
    • The function will mallocate a new memory block & copy the original data to there.
    • If the old memory block's adjacency is available to use, the function will expand the old memory block.
  • The new memory block may less than the original one.
    • The function will free the unnecessary memory block.
//...
int n;
printf("Enter the size of array:\n");
scanf("%d", &n);
int *arr = (int*)calloc(n, sizeof(int));  // Dynamically allocated array
// arr = {0, 0, 0, ...} (n>3)
// ...
free(arr);

Some machine or compiler may allows program to r/w memory block that freed. But sometimes may get crash.

// ...
realloc(NULL, n*sizeof(int)); // equivalent to malloc;
realloc(arr, 0); // equivalent to free;

Dynamic Memory Allocation

Allocate dynamic memory

In C or C++:

int* p;
p = (int*)alloc(sizeof(int));
*p = 0x400;
free(p)

In C++:

int* p;
p = new int;
*p = 0x400;
delete p;

Allocate dynamic array

UsingHeap

When you alloc a continuous space, you can use the space as a array.

In C or C++:

// ...
int* p;
const int SIZE = 4;
p = (int*)alloc(SIZE * sizeof(int))  // Allocate a 16 byte continuous space
for (int i=0; i<4; i++, p++)
{
    // p[i] = i;
    *p = i;
}
free(p)

In C++:

int* p;
const int SIZE = 4;
p = new int[4];  // Allocate a 16 byte continuous space
for (int i=0; i<4; i++, p++)
{
    // p[i] = i;
    *p = i;
}
delete[] p;

Memory Leak

Normal:

//...
void func()
{
  printf("void func(): called")
  char chars = "1145141919810";  // 13 bytes
}

int main()
{
  for (int i = 0; i < 100; i++) func();
  return 0;
}

The program's usage of the memory won't be raised up after finish calling the func()

Memory Leak:

//...
void func()
{
  printf("void func(): called")
  char* chars = (char*)calloc(1145, sizeof(char));  // 1145 bytes
  // char* chars = new char[1145];
}

int main()
{
  for (int i = 0; i < 100; i++) func();
  return 0;
}

The program's usage of the memory will be raised up after finish calling the func()

Reason: The allocated memory block in heap isn't deallocated after use.

Solution: Add a free(chars) or delete[] chars at the end line of the func()

In Java, C#, etc. The problem can't be happened, because they have their own garbage collection mechanism.

Pointer return from function

//...
int f1(int a) {/* ... */}
int f2(int* a) {/* ... */}
int main() {f1();f2();return 0}

Concept:

  • int main();: In C/C++ called Calling function

  • int f1(int a); and int f2(int* a): Call Called function

  • int f1(var);: Call Call by value

  • int f1(*var);: Call Call by reference

The correct operation of returning pointer from function:

int* rtnAPointer()
{
  int* p = (int*)malloc(sizeof(int));
  *p = 0x1bf52;
  return p;
}

int main()
{
  int* p = rtnAPointer();
  *p += 1919810;
  printf("%d\n", p);
  free(p);
  return 0;
}

Function Pointer

A pointer which stores a function entry address.

Initialization syntax: <return type> (*<pointer name>)(<type>, ...);

Example:

void (*fp)(int, char);

Counterexample:

int *fp();

This declare a function that return int*, not a function pointer.


Call syntax: *(<pointer name>)(<type>, ...) or <pointer name>(<type>, ...)

Example:

*(f)(0x1bf52, 33);

f(0x1bf52, 33);

Why these two syntax are both correct?

You can compare this with array's syntax:

int arr[] <==> int* arr

*(f)() <==> f()

Application scenario

Callbacks

// ...
void f0() {printf("f0() called");}

void f1(void (*func)()) {func();}

int main() {
  f1(f0);
  return 0;
}

What's the meaning of the callbacks?

In C library function, qsort use it.

void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))
  • base − This is the pointer to the first element of the array to be sorted.

  • nitems − This is the number of elements in the array pointed by base.

  • size − This is the size in bytes of each element in the array.

  • compar − This is the function that compares two elements.

The reference example from:

https://www.runoob.com/cprogramming/c-function-qsort.html

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

int values[] = { 88, 56, 100, 2, 25 };

int cmpfunc (const void * a, const void * b)
{
   return ( *(int*)a - *(int*)b );
}

int main()
{
   int n;

   printf("排序之前的列表:\n");
   for( n = 0 ; n < 5; n++ ) {
      printf("%d ", values[n]);
   }

   qsort(values, 5, sizeof(int), cmpfunc);

   printf("\n排序之后的列表:\n");
   for( n = 0 ; n < 5; n++ ) {
      printf("%d ", values[n]);
   }
 
  return 0;
}

Sucked definition of pointer

https://mp.weixin.qq.com/s/g80HxtEfmS4nasxUOS79_w

Extra: Pointer and its application scanerios (Base on ARM Cortex-M)

Memory Address

  • Memory is byte addressable
    • Memory is an array of bytes
    • Each byte has a memory address
    • Smallest data that can be addressed is a byte
      • The reason that why boolean data type is 1 byte.
    • Each address has 32 bits (ARM Cortex-M)

Pointer

  • The value of a pointer is simply the memory address of some variable
    • If a variable occupies multiple bytes in memory, its address is the lowest address of all bytes it occupies.

AddressOfVariable

The pink address is this integer's This is small-endian order

TEST

/*
 * @Date: 2022-07-21 22:39:45
 * @LastEditors: littleblackLB
 * @LastEditTime: 2022-07-22 00:16:55
 * @FilePath: \Cpp\src\Test.cpp
 */

#include <iostream>

using namespace std;

// 一个指针数组,拥有三个元素,每个元素指向一个函数原型为 void func()
typedef void (*(ARR[3]))();
// 一个函数指针返回一个数组,这是一个指针数组,容量为10,
// 每个元素指向一个函数原型为 void func()
typedef ARR *(*FUNC)(); // 等价于 typedef void(*(*(*FUNC)())[3])();

void func2()
{
    printf("%d\n", 888);
}

void func3()
{
    printf("%d\n", 889);
}

void func4()
{
    printf("%d\n", 890);
}

ARR *func1()
{
    ARR *rtn = new ARR[3];
    // 解引用rtn指针到数组头指针,解数组头指针,并赋值为func2
    **rtn = func2;  // 等价于*rtn[0] = func2;
    *rtn[1] = func3;
    *rtn[2] = func4;
    return rtn;
}

int main(int argc, char const *argv[])
{
    system("chcp 65001");
    FUNC f = func1;
    ARR *fs = (*f)();  // 等价于ARR *fs = f();
    for (int i = 0; i < 3; i++)
    {
        (*fs[i])();  // 等价于fs[i]();
    }
    delete[] fs;
    system("pause");
    return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment