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
:有类型检查功能,在定义很长类型的时候方便的多、有作用域#define
与const
都能定义常量,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
用于处理可能重叠的内存区域,确保数据正确移动。
字符串拷贝:strcpy
、strncpy
字符创连结:strcat
、strncat
字符串比较:memcmp
、strcmp
、strncmp
字符串查找: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 八个默认成员函数
-
构造和析构
-
拷贝构造和赋值
-
移动构造和移动赋值
- 初始化列表
- 什么情况下要自己写?
一般构造都要自己写:
深拷贝的类,一般都要自己写析构、拷贝、构造、赋值。
深拷贝的类,也需要实现移动构造和移动赋值 - 默认生成的这些函数行为是什么?
- 什么是默认构造?(不传参的都可以是默认构造)
7.7 运算符重载
- 哪些运算符不能重载?
- 和函数重载的区别?
- 它的意义是什么?
7.8 友元
- 友元函数
- 友元类
7.9 static 成员
static
成员就是全局成员不占类的大小,仅仅只是受到类域限制而已,生命周期也是全局的。
7.10 对象拷贝时,编译器的优化
在对象拷贝时,编译器的优化通常包括使用移动语义来减少不必要的复制,以及应用拷贝省略(copy elision)来避免临时对象的拷贝。
8. 内存管理
malloc
、calloc
和 realloc
的区别:
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 + 构造函数
-------------------------------------------未完待续----------------------------------------------------