1. 指针

1.1 数组和指针

数组指针是指针,指向数组的第一个元素;

#include <stdio.h>

int main() {
    int arr[3] = {1, 2, 3};
    int (*ptr)[3] = &arr;  // ptr 是一个指向包含3个整数的数组的指针

    // 通过指针访问数组元素
    for (int i = 0; i < 3; i++) {
        printf("%d ", (*ptr)[i]);
    }
    printf("\n");

    return 0;
}

指针数组是数组,数组的每个对象都是一个指针;

#include <stdio.h>

int main() {
    int a = 10, b = 20, c = 30;
    int *ptr_arr[3];  // ptr_arr 是一个包含3个整数指针的数组

    ptr_arr[0] = &a;
    ptr_arr[1] = &b;
    ptr_arr[2] = &c;

    // 通过指针数组访问整数变量
    for (int i = 0; i < 3; i++) {
        printf("%d ", *ptr_arr[i]);
    }
    printf("\n");

    return 0;
}

1.2 函数指针

函数指针是一个指针,它指向一个函数的地址。函数指针允许我们通过指针允许我们通过指针来调用函数,这在实现回调函数调用时非常有用,虚函数表也就时一个函数指针数组。

#include <stdio.h>

// 定义一个函数类型
typedef int (*FuncPtr)(int, int);

// 定义一个函数
int add(int a, int b) {
    return a + b;
}

// 另一个函数
int subtract(int a, int b) {
    return a - b;
}

// 使用函数指针的函数
void useFunctionPointer(FuncPtr fp, int x, int y) {
    int result = fp(x, y);
    printf("Result: %d\n", result);
}

int main() {
    FuncPtr fp;

    // 指向 add 函数
    fp = add;
    useFunctionPointer(fp, 5, 3);  // 输出: Result: 8

    // 指向 subtract 函数
    fp = subtract;
    useFunctionPointer(fp, 5, 3);  // 输出: Result: 2

    return 0;
}

指针函数是一个返回指针的函数,函数的返回类型是指针。

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

// 函数返回一个指向整数的指针
int* createArray(int size) {
    int* array = (int*)malloc(size * sizeof(int)); // 动态分配内存
    if (array == NULL) {
        printf("Memory allocation failed\n");
        exit(1);
    }
    // 初始化数组
    for (int i = 0; i < size; i++) {
        array[i] = i + 1;
    }
    return array;
}

int main() {
    int size = 5;
    int* arr = createArray(size);

    // 打印数组内容
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    // 释放分配的内存
    free(arr);

    return 0;
}

1.3 const 和 指针、static、#define、typedef

  • const:定义变量不能修改,必须在定义时初始化;在 * 前是常量指针(不能通过该指针修改),在*后是指针常量(不能再指向其它地址)修饰函数的参数(函数体内不能修改这个值)
  • #define: C语言中定义语法,是预处理指令(简单的替换)、没有作用域
  • typedef:有类型检查功能,在定义很长类型的时候方便的多、有作用域
  • #defineconst都能定义常量,define可替换常量、表达式(代码段中:不分配内存空间);const常量在(数据段中)分配空间
#include <stdio.h>
// 加法宏函数
#define ADD(x, y) ((x) + (y))
// 减法宏函数
#define SUBTRACT(x, y) ((x) - (y))
int main() {
    int a = 5;
    int b = 3;
    printf("Addition: %d + %d = %d\n", a, b, ADD(a, b));        // 输出: 5 + 3 = 8
    printf("Subtraction: %d - %d = %d\n", a, b, SUBTRACT(a, b)); // 输出: 5 - 3 = 2
    return 0;
}

1.4 指针和引用的异同

  • 相同:都是地址概念;
  • 区别:指针是实体、引用是别名;指针++是内存地址自增、引用时值自增;引用无需解引用(*)、指针需要解引用、引用只能在定义的时候被初始化、引用不能为空。

1.5 sizeof与strlen

https://qhd666.blog.csdn.net/article/details/132915168

  • sizeof 用于计算数据类型或变量的大小(以字节为单位),它可以用于基本数据类型、数组、结构体、联合体以及其他类型。
  • strlen 计算以null终止字符的长度(不包括null终止符),它在 string.h 头文件中定义。
  • 数组所占空间:sizeof(数组名); 数组大小:sizeof(数组名)/sizeof(数据类型);

2. 库函数及其模拟实现

https://qhd666.blog.csdn.net/article/details/133102559
memcpy 用于不重叠的内存区域快速复制数据,而 memmove 用于处理可能重叠的内存区域,确保数据正确移动。
字符串拷贝:strcpystrncpy
字符创连结:strcatstrncat
字符串比较:memcmpstrcmpstrncmp
字符串查找:strstr
计算长度:strlen

3. 自定义类型

内存对齐:自定义类型的内存对齐是确保数据在内存中按照特定字节边界排列,以提高访问效率和满足硬件要求。
https://qhd666.blog.csdn.net/article/details/134456752?spm=1001.2014.3001.5502

https://qhd666.blog.csdn.net/article/details/132009604?spm=1001.2014.3001.5502

  • 内存对齐的规则:
  • 为什么要有内存对齐
  • 整型的存储:

4. 数据存储

https://qhd666.blog.csdn.net/article/details/132009604
静态存储区(全局变量、静态变量)、栈区(局部变量、函数内参数)。

大端:高地址存低字节、低地址存高字节
小端:高地址存高字节、低地址存低字节

5. 编译链接过程

编译:将源代码翻译成目标代码(机器语言),生成一个或多个目标文件。
汇编:将汇编代码转换成目标代码(如果需要)。
链接:将多个目标文件和库文件合并,解决符号引用,生成最终的可执行文件。

6. C++入门基础

6.1 函数重载

https://qhd666.blog.csdn.net/article/details/140560876
函数重载是指在同一作用域内,多个函数名相同但参数列表不同的特性。

6.2 引用和指针

https://qhd666.blog.csdn.net/article/details/140904531?spm=1001.2014.3001.5502

  • 指针和引用的异同:
  • 引用价值:做参数和做返回值

例如:

int& getElement(std::vector<int>& vec, size_t index) {
    return vec[index]; // 返回对 vec 中元素的引用
}
  • 引用也不是绝对安全的:
int* unsafeFunc() {
    int local = 42;
    return &local; // local 在函数返回后被销毁
}
// 访问 unsafeFunc() 返回的指针将是未定义行为

6.3 建议使用const、inline、enum去替代宏

  • 宏的缺点:
  • inline要求:频繁调用短小函数合适(给编译器提建议)

6.4 nullptr的意义是什么?

在C++中 #define NULL 0
NULL定义存在缺陷

7. 类和对象

7.1 面向对象和面向过程的区别?

面向对象侧重于对象的抽象和封装,而面向过程则着重于步骤和函数的顺序执行。

7.2 类大小的计算

  • 内存对齐

  • 空类

7.3 class和struct区别

在C++中,class的成员默认是私有的,而struct的成员默认是公有的。

7.4 C++11智能指针

https://qhd666.blog.csdn.net/article/details/139183793
unique_ptr 禁止拷贝、shared_ptr使用引用计数:解决多次释放问题、每一个资源配一个引用计数

7.5 this指针

是每个对象的专属指针,访问自己的数据成员和方法。

7.6 八个默认成员函数

  • 构造和析构

  • 拷贝构造和赋值

  • 移动构造和移动赋值

  • 初始化列表
  1. 什么情况下要自己写?
    一般构造都要自己写:
    深拷贝的类,一般都要自己写析构、拷贝、构造、赋值。
    深拷贝的类,也需要实现移动构造和移动赋值
  2. 默认生成的这些函数行为是什么?
  1. 什么是默认构造?(不传参的都可以是默认构造)

7.7 运算符重载

  1. 哪些运算符不能重载?
  1. 和函数重载的区别?
  1. 它的意义是什么?

7.8 友元

  1. 友元函数
  2. 友元类

7.9 static 成员

static成员就是全局成员不占类的大小,仅仅只是受到类域限制而已,生命周期也是全局的。

7.10 对象拷贝时,编译器的优化

在对象拷贝时,编译器的优化通常包括使用移动语义来减少不必要的复制,以及应用拷贝省略(copy elision)来避免临时对象的拷贝。

8. 内存管理

malloccallocrealloc 的区别:

  • malloc:分配指定字节数的内存块,但不初始化其内容。返回指向该内存块的指针。
void* malloc(size_t size);
  • calloc:分配内存并初始化为零。需要指定要分配的块数和每块的字节数。
void* calloc(size_t num, size_t size);
  • realloc:重新调整已分配内存块的大小。如果需要,可以将内容复制到新位置,并释放原内存块
void* realloc(void* ptr, size_t size);

new和new[] 的底层实现原理:operator new + 构造函数

-------------------------------------------未完待续----------------------------------------------------

08-13 18:23