C/C++
static
静态局部变量(函数内)
使局部变量的生命周期延长至整个程序运行期间,但作用域仍限于函数内
变量只初始化一次,下次调用函数时保留上次的值
void counter() { static int count = 0; // 只初始化一次 count++; printf("Count: %d\n", count); } int main() { counter(); // 输出 "Count: 1" counter(); // 输出 "Count: 2" return 0; }
限制全局变量或函数的作用域为当前文件,避免与其他文件的同名符号冲突 // file1.c static int hidden_var = 42; // 仅当前文件可见 static void hidden_func() { // 仅当前文件可调用 printf("Secret function\n"); } // file2.c extern int hidden_var; // 编译错误!无法访问其他文件的static变量
类的静态成员变量被所有对象共享,内存仅分配一次 class Widget { public: static int count; // 声明(不分配内存) Widget() { count++; } }; int Widget::count = 0; // 必须定义初始化 int main() { Widget w1, w2; cout << Widget::count; // 输出 2 }
静态成员函数不依赖对象实例,可直接通过类名调用 class MathUtils { public: static int square(int x) { return x * x; } // 静态函数 }; int main() { cout << MathUtils::square(5); // 输出 25,无需创建对象 }
class Singleton { private: static Singleton* instance; Singleton() {} public: static Singleton* getInstance() { if (!instance) instance = new Singleton(); return instance; } }; Singleton* Singleton::instance = nullptr;
strlen 和sizeof
strlen计算C风格字符串的长度
const char*,以 \0 结尾的字符数组
char str[100]; // 没有初始化 size_t len = strlen(str); // ❌ 未定义行为(没有 '\0',会一直找直到崩溃)
std::string str; // 空字符串 std::cout << str.length(); // ✅ 输出0,不崩
char str[] = "hello"; size_t len = strlen(str); // 返回5 std::string str = "hello"; size_t len = str.length(); // 也是返回5 std::cout << sizeof(str) << std::endl; // 返回是指针str大小,64位下是str
char str[] = "Hello"; // sizeof:计算数组总大小(包括'\0') printf("%zu\n", sizeof(str)); // 输出6(5字符 + 1个'\0') // strlen:计算字符串长度(不含'\0') printf("%zu\n", strlen(str)); // 输出5 char *ptr = "Hello"; // sizeof:返回指针本身的大小(通常4或8字节) printf("%zu\n", sizeof(ptr)); // 输出8(64位系统) // strlen:仍计算字符串长度 printf("%zu\n", strlen(ptr)); // 输出5
char str[100] = "abc"; printf("%zu %zu\n", sizeof(str), strlen(str)); // sizeof(str) 输出 100(数组总大小) // strlen(str) 输出 3(实际字符串长度)
字节序概述
- 字节序是数据在计算机内存储中的顺序方式
- 计算机将数据按字节8位存储,不同的系统可能会有不同的字节存储顺序
- 大端序:数据高位字节存储低地址处,低位字节存储在高地址处
- 小端序:数据低位字节存储在低地址处,高位字节存储在高地址处
- 网络字节序是大端序
字节序转换
- htonl(): 主机到网络字节序
- ntohl():网络到主机字节序
- 编译器默认生成函数
- 默认构造函数
- 析构函数
- 拷贝构造函数
- 拷贝赋值函数
- 移动构造函数
- 移动赋值运算符
- const与#define的区别
const
:定义类型安全的常量,占用内存,支持调试符号- #define:宏替换,无类型检查,不占内存,易引发副作用
数组与指针
数组:连续内存块的名称,存储相同类型的元素。数组名代表整个内存块的首地址,但在sizeof和&操作时表现为整个数组的属性
指针:变量,存储内存地址,可指向任意数据类型或内存位置
int arr[5]; // 数组,sizeof(arr) = 5*sizeof(int) int *ptr = arr; // 指针,sizeof(ptr) = 4或8(取决于系统)
大多数表达式中,数组名会隐式转换为指向其首元素的指针(如传递给函数、参与指针运算)
void func(int *ptr) {} int main() { int arr[3]; func(arr); // 数组名退化为int*,指向arr[0] printf("%zu\n", sizeof(arr)); // 输出12(假设int为4字节) }
指针加法以元素大小为步长(如ptr + 1指向下一个元素)
int arr[3] = {1, 2, 3}; int *ptr = arr; printf("%d\n", *(ptr + 1)); // 输出2(等价于arr[1])
动态分配数组
// 一维数组 int *arr1D = malloc(5 * sizeof(int)); // 二维数组(非连续) int **arr2D = malloc(3 * sizeof(int*)); for (int i = 0; i < 3; i++) arr2D[i] = malloc(4 * sizeof(int)); // 二维数组(连续内存) int (*arr2D_cont)[4] = malloc(3 * sizeof(int[4]));
多维数组与指针数组的区别
多维数组:内存连续,每行元素紧密排列 指针数组:每个元素为指针,指向独立分配的内存块 int arr[3][4] int *arr[3] +---+---+---+---+ +---+ +---+---+---+---+ | 0 | 1 | 2 | 3 | | · | --> | 0 | 1 | 2 | 3 | +---+---+---+---+ +---+ +---+---+---+---+ | 4 | 5 | 6 | 7 | | · | --> | 4 | 5 | 6 | 7 | +---+---+---+---+ +---+ +---+---+---+---+ | 8 | 9 |10 |11 | | · | --> | 8 | 9 |10 |11 | +---+---+---+---+ +---+ +---+---+---+---+
字符串的两种表达方式
char str[] = "hello"和char *str = "hello" 1. 字符数组:可修改,存储于栈或全局区 字符指针:指向常量字符串,不可修改内容(可能存储于只读段) 2. char arr[] = "hello"; arr[0] = 'H'; // 合法 char *ptr = "hello"; ptr[0] = 'H'; // 运行时错误(未定义行为)
指针与数组的常见错误
越界访问:arr[5]访问超出数组长度 未初始化指针:int *ptr; *ptr = 5;(野指针) 错误的内存释放:free非malloc分配的内存或重复释放
函数参数传递数组
数组作为参数传递时退化为指针,需额外传递长度: void printArray(int arr[], int size) { // 等价于int *arr for (int i = 0; i < size; i++) printf("%d ", arr[i]); }
指针与数组的性能差异
数组:内存连续,利于缓存命中,访问速度快 指针:灵活性高,但可能因间接寻址增加开销
C++
new/delete 与 malloc/free 的区别
new
/delete
:调用构造函数/析构函数,类型安全,可重载malloc
/free
:仅分配/释放内存,不调用构造/析构
智能指针
unique_ptr
:独占所有权,不可拷贝,可移动shared_ptr
:共享所有权,引用计数weak_ptr
:解决shared_ptr
循环引用问题
函数模板与类模板
// 函数模板 template <typename T> T max(T a, T b) { return a > b ? a : b; } // 类模板 template <typename T> class Stack { private: std::vector<T> elements; public: void push(const T& elem) { elements.push_back(elem); } };
模板特化与偏特化
全特化:针对特定类型完全重写模板
偏特化:部分限制模板参数(如指针类型)
// 全特化 template <> class Stack<int> { /* 针对int的特殊实现 */ }; // 偏特化(指针类型) template <typename T> class Stack<T*> { /* 处理指针类型 */ };
typename
与class
的区别- 在模板参数声明中二者等价
typename
用于声明嵌套依赖类型(如typename T::iterator
)
浅拷贝
class ShallowCopy { public: int* data; ShallowCopy(int val) { data = new int(val); } ~ShallowCopy() { delete data; // 析构时释放内存 } }; int main() { ShallowCopy obj1(42); ShallowCopy obj2 = obj1; // 浅拷贝:obj2.data 与 obj1.data 指向同一内存 // obj1和obj2析构时会导致重复释放(崩溃)! return 0; }
深拷贝
class DeepCopy { public: int* data; DeepCopy(int val) { data = new int(val); } // 自定义拷贝构造函数(深拷贝) DeepCopy(const DeepCopy& other) { data = new int(*other.data); // 分配新内存并复制值 } // 自定义赋值运算符(深拷贝) DeepCopy& operator=(const DeepCopy& other) { if (this != &other) { // 防止自赋值 delete data; // 释放旧内存 data = new int(*other.data); // 分配新内存并复制值 } return *this; } ~DeepCopy() { delete data; } }; int main() { DeepCopy obj1(42); DeepCopy obj2 = obj1; // 深拷贝:obj2.data 指向新内存 // obj1和obj2析构时各自释放内存,无冲突 return 0; }
简述strcpy,sprintf和memcpy函数的区别
#include <string.h> char src[] = "Hello"; char dest[10]; strcpy(dest, src); // dest 变为 "Hello"(含\0)
#include <stdio.h> char buffer[50]; int num = 42; sprintf(buffer, "Value: %d", num); // buffer 变为 "Value: 42\0"
#include <string.h> int src[5] = {1, 2, 3, 4, 5}; int dest[5]; memcpy(dest, src, 5 * sizeof(int)); // 复制整个数组(二进制拷贝)
C语言字符串
字符串的基本概念
C语言中,字符串是以空字符
'\0'
结尾的字符数组字符数组:显式分配内存,可修改
char str[] = "Hello"; // 可修改,存储在栈上 str[0] = 'h'; // 合法
// 字符串常量:存储在只读内存段(如.rodata),不可修改 char *ptr = "Hello"; // ptr指向常量区 ptr[0] = 'h'; // 运行时错误(未定义行为)
字符串的输入与输出
- scanf(“%s”, str):读取到空格/换行停止,不检查缓冲区大小
fgets(str, sizeof(str), stdin)
:安全读取,保留换行符,自动截断- printf(“%s”, str):输出到 ‘\0’ 终止
puts(str)
:自动添加换行符
字符串函数
strlen(str) //返回字符串长度(不含 '\0') strcpy(dest, src) // 复制字符串(含 '\0') strncpy(dest, src, n) // 复制最多 n 个字符 strcat(dest, src) // 安全追加,最多 n 字符 strcmp(s1, s2) // 比较字符串 strstr(haystack, needle) // 查找子串首次出现位置
char buf[5]; strcpy(buf, "HelloWorld"); // 缓冲区溢出,导致未定义行为
char *str = malloc(20 * sizeof(char)); // 动态分配20字节 if (str != NULL) { strcpy(str, "Dynamic String"); // 使用... free(str); // 必须手动释放 }
char *p = "Hello"; while (*p != '\0') { printf("%c", *p); p++; }
// 指针和数组区别 char arr[] = "Hello"; // 数组名是常量指针 char *ptr = "Hello"; // 指针可重新赋值 ptr = arr; // 合法 // arr = ptr; // 非法(数组名不可修改)
// 实现安全的字符串拷贝 void safe_strcpy(char *dest, const char *src, size_t dest_size) { if (dest_size == 0) return; size_t i; for (i = 0; i < dest_size-1 && src[i] != '\0'; i++) { dest[i] = src[i]; } dest[i] = '\0'; }