文章目录
- [1. 指针](#1. 指针)
-
- [1.1 数组和指针](#1.1 数组和指针)
- [1.2 函数指针](#1.2 函数指针)
- 1.3 const 和 指针、static、#define、typedef
- [1.4 指针和引用的异同](#1.4 指针和引用的异同)
- [1.5 sizeof与strlen](#1.5 sizeof与strlen)
- [2. 库函数及其模拟实现](#2. 库函数及其模拟实现)
- [3. 自定义类型](#3. 自定义类型)
- [4. 数据存储](#4. 数据存储)
- [5. 编译链接过程](#5. 编译链接过程)
- [6. C++入门基础](#6. C++入门基础)
-
- [6.1 函数重载](#6.1 函数重载)
- [6.2 引用和指针](#6.2 引用和指针)
- [6.3 建议使用const、inline、enum去替代宏](#6.3 建议使用const、inline、enum去替代宏)
- [6.4 nullptr的意义是什么?](#6.4 nullptr的意义是什么?)
- [7. 类和对象](#7. 类和对象)
-
- [7.1 面向对象和面向过程的区别?](#7.1 面向对象和面向过程的区别?)
- [7.2 类大小的计算](#7.2 类大小的计算)
- [7.3 class和struct区别](#7.3 class和struct区别)
- [7.4 C++11智能指针](#7.4 C++11智能指针)
- [7.5 this指针](#7.5 this指针)
- [7.6 八个默认成员函数](#7.6 八个默认成员函数)
- [7.7 运算符重载](#7.7 运算符重载)
- [7.8 友元](#7.8 友元)
- [7.9 static 成员](#7.9 static 成员)
- [8. 内存管理](#8. 内存管理)
1. 指针
1.1 数组和指针
数组指针是指针,指向数组的第一个元素;
c
#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;
}
指针数组是数组,数组的每个对象都是一个指针;
cpp
#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 函数指针
函数指针是一个指针,它指向一个函数的地址。函数指针允许我们通过指针允许我们通过指针来调用函数,这在实现回调函数调用时非常有用,虚函数表也就时一个函数指针数组。
cpp
#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;
}
指针函数是一个返回指针的函数,函数的返回类型是指针。
cpp
#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
常量在(数据段中)分配空间
cpp
#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
- 内存对齐的规则:
- 第一个成员在与结构体变量偏移量为0的位置
- 其他成员变量要对齐到对齐数的整数倍位置
- 对齐数 = min( 编译器默认的对齐数 , 该成员变量大小 ) , VS下默认对齐数为8
- 结构体总大小为每一个成员变量的最大对齐数的整数倍
- 修改默认对齐数的方法: #pragma pack(8) , 设置默认对齐数为8
- 为什么要有内存对齐
- 为了平台的可移植:不是所有的硬件都能访问任意地址上的任意数据, 可能只能在4的倍数的地址处取数据。
- 为了性能:若内存不对齐, 原本只需要访问一次的数据现在可能要访问两次才能拿全
- 整型的存储:
- 原码:直接表示整数的符号和大小,最高位为符号位。
- 反码:正数与原码相同,负数的表示是将原码的每一位取反。
- 补码:正数与原码相同,负数的表示是将反码加1,主要用于简化计算和处理负数。
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
- 指针和引用的异同:
相同 :都是地址概念;
区别:指针是实体、引用是别名、指针++是内存地址自增、引用时值自增、引用无需解引用(*)、指针需要解引用、引用只能在定义的时候被初始化一次、引用不能为空。
- 引用价值:做参数和做返回值
引用可以作为参数传递给函数以避免复制开销,也可以作为返回值以允许函数返回对对象的直接引用。
例如:
cpp
int& getElement(std::vector<int>& vec, size_t index) {
return vec[index]; // 返回对 vec 中元素的引用
}
- 引用也不是绝对安全的:
悬挂引用:如果引用的对象在引用被使用之前已经被销毁或超出作用域(例如局部变量),那么引用将变成悬挂引用,导致未定义行为。
cpp
int* unsafeFunc() {
int local = 42;
return &local; // local 在函数返回后被销毁
}
// 访问 unsafeFunc() 返回的指针将是未定义行为
6.3 建议使用const、inline、enum去替代宏
- 宏的缺点:
- 调试困难:宏展开后,调试时无法直接查看宏的原始代码。
- 类型安全差:宏不进行类型检查,容易导致类型错误。
- 作用域问题:宏没有作用域限制,可能影响其他代码。
- 代码重复:宏可能导致重复代码,增加维护难度。
- 可能导致错误:宏展开中的括号问题可能引发难以发现的错误。
- inline要求:频繁调用短小函数合适(给编译器提建议)
6.4 nullptr的意义是什么?
在C++中 #define NULL 0
NULL定义存在缺陷
nullptr
是 C++11 引入的类型安全关键字,用于表示空指针,而NULL
是一个宏定义,通常与0
等价,可能在类型安全和可移植性方面不如nullptr
。
7. 类和对象
7.1 面向对象和面向过程的区别?
面向对象侧重于对象的抽象和封装,而面向过程则着重于步骤和函数的顺序执行。
7.2 类大小的计算
-
内存对齐
-
空类
7.3 class和struct区别
7.4 C++11智能指针
https://qhd666.blog.csdn.net/article/details/139183793
unique_ptr
禁止拷贝、shared_ptr
使用引用计数:解决多次释放问题、每一个资源配一个引用计数
7.5 this指针
是每个对象的专属指针,访问自己的数据成员和方法。
this
指针存在哪里?
this
指针在类的成员函数中,指向当前对象的内存地址。它通常存储在栈上或寄存器中。this
指针可以为空吗?
this 指针不会为空,因为它总是指向当前对象。
7.6 八个默认成员函数
-
构造和析构
-
拷贝构造和赋值
-
移动构造和移动赋值
-
初始化列表
- 特性是什么?
以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。- 哪些成员必须在初始化列表初始化?
引用成员变量
const成员变量
自定义类型成员(且该类没有默认构造函数时)- 初始化顺序是按声明顺序
- 什么情况下要自己写?
一般构造都要自己写:
深拷贝的类,一般都要自己写析构、拷贝、构造、赋值。
深拷贝的类,也需要实现移动构造和移动赋值 - 默认生成的这些函数行为是什么?
- 默认构造函数:初始化对象的成员。
- 复制构造函数:逐个复制对象的成员。
- 赋值运算符:将右侧对象的成员赋值给左侧对象。
- 析构函数:释放对象占用的资源。
- 移动构造函数:移动资源,清空右侧对象。
- 移动赋值运算符:移动资源并清空右侧对象。
这些行为适用于所有类,除非你自定义了这些函数。
- 什么是默认构造?(不传参的都可以是默认构造)
我们不写,编译器默认生成的是默认构造。
全缺省的是默认构造 。
没有参数的是默认构造。
7.7 运算符重载
- 哪些运算符不能重载?
- 和函数重载的区别?
- 他的意义是什么?
7.8 友元
- 友元函数
- 友元类
7.9 static 成员
static
成员就是全局成员不占类的大小,仅仅只是收到类域限制而已
8. 内存管理
-------------------------------------------未完待续----------------------------------------------------