首页 / 资讯中心 / 文章详情

C/C++语言基础知识

C/C++语言基础知识 ★ FEATURED ARTICLE
static成员变量什么时候分配内存static成员变量在程序运行时进行分配static成员变量只是声明在类定义中而他们的定义必须在类外定义。例如class myClass { public: static int score;//声明 };需要在类外提供这个成员变量的定义int myClass::score100;//初始化定义内存对齐的原理和作用一、什么是内存对齐CPU 访问内存时不会按字节任意读取而是以固定块2/4/8 字节为单位读取这个块叫访问粒度。 编译器会自动给结构体、类成员之间填充空白字节padding让每个成员的起始地址满足特定规则这个机制就是内存对齐。二、对齐规则成员自身对齐每个成员的起始地址必须是「该成员自身大小」的整数倍。整体对齐结构体最大成员对齐整个结构体总大小必须是内部最大基础类型的整数倍不足则末尾补 padding。三、内存对齐底层原理硬件限制CPU 访存只能按块读取32 位 CPU 一次读 4 字节64 位一次读 8 字节。 如果变量未对齐一个变量跨两个内存块对齐情况一次内存读取就能拿到完整数据未对齐情况CPU 要读两次内存再拼接数据性能大幅下降。四、内存对齐两大作用1. 提升 CPU 读取性能最主要对齐后数据落在单次访存块内减少内存 IO 次数程序运行更快。2. 硬件兼容性很多嵌入式、移动端 CPU 不支持非对齐内存访问不对齐会引发程序异常、内存报错。sizeof和strlen的区别sizeof求整个数据类型 / 变量占用的内存字节大小包含填充、末尾\0strlen求有效字符串长度只统计可见字符不包含末尾结束符\0strcpy和memcpy的区别strcpy头文件string.h用途只拷贝字符串停止规则遇到\0就停止拷贝结束符\0会一并复制过去memcpy头文件string.h用途任意二进制内存块拷贝char、int、结构体、数组都能拷停止规则完全依靠第三个参数size_t n固定拷贝 n 个字节不会识别\0static和const的区别两个关键字本质完全不同const修饰只读限制变量不能被修改只改访问权限不改变存储位置、作用域、生命周期。static修饰存储位置 / 作用域 / 生命周期和读写权限无关static 变量照样能改。const关键字和define的区别#define 宏预处理指令预处理阶段直接文本替换不做类型检查没有作用域概念。#define MAX 100预处理时代码里所有MAX直接替换成字面量100。const 常量修饰变量编译期生效是有类型、有作用域、存在内存的只读变量。const int max 100;本质是只读变量分配内存有数据类型 int。作用域#define作用域从定义处到文件末尾不受大括号、函数限制全局生效头文件重复引入会重复定义冲突。const遵循正常变量作用域局部 const 仅函数内有效全局 const 仅限当前文件。是否分配内存#define纯文本替换不分配内存只是字面量。const会分配内存全局const存只读常量段.rodata函数内const存栈上static关键字的用法static 核心两大特性改变存储位置、限制作用域分全局、局部、函数、修饰全局变量四类场景。一、修饰全局变量写在函数外面static int g_num 10;作用域限制仅当前.c文件内可见其他文件用extern也无法访问 不加 static 的全局变量是整个工程可见。存储位置静态数据段.data程序运行全程存在。初始化main 执行前初始化一次默认值 0。用途模块私有全局变量避免多文件命名冲突。二、修饰局部变量函数内部void test() { static int num 0; num; printf(%d , num); }存储改变不再存栈存入静态区函数结束不销毁。初始化规则只在第一次调用函数时初始化 1 次后续调用保留上次值。生命周期整个程序运行期间存在。对比普通局部变量普通局部变量在栈每次调用重新创建、初始化为随机值。 调用两次 test () 输出1 2。三、修饰普通函数static void func() { }作用域限制该函数只能在当前.c文件调用外部文件不可见。不加 static 的函数默认可跨文件调用。用途模块内部工具函数对外隐藏接口降低命名污染。四、拓展static const 组合常用常量static const int MAX 100;static仅限本文件使用const值只读不可修改 常用于头文件定义私有常量避免多文件重复定义报错。关键特性总结所有 static 变量生命周期 程序整个运行周期static 不会改变读写权限变量默认可修改搭配 const 才只读初始化时机全局 static 加载时初始化局部 static 第一次进函数初始化内存位置统一存静态区不在栈、堆核心记忆口诀修饰全局变量 / 函数限定为本文件私有修饰局部变量延长生命周期只初始化一次。指针是什么一、一句话理解指针指针就是存储内存地址的变量。 计算机每一块内存字节都有唯一编号这个编号叫内存地址专门用来存放地址的变量就是指针变量简称指针。举个生活化例子房间 内存空间房间门牌号 内存地址一张写着门牌号的纸条 指针 纸条本身不装房间里的东西只记录东西在哪。二、两个核心运算符取地址符作用取出变量的内存地址int a 10; a; // 获取变量a的地址*解引用符间接访问作用根据地址找到对应内存里存的数据int a 10; int *p a; // p保存a的地址 *p 20; // 通过地址修改a的值此时a变成20三、基础指针代码示例#include stdio.h int main() { int num 100; // 定义int类型指针p存放int变量的地址 int *p num; printf(num的值%d\n, num); printf(num的地址%p\n, num); printf(指针p存的地址%p\n, p); printf(*p 通过地址取到的值%d\n, *p); *p 200; printf(修改后 num %d, num); // num变成200 return 0; }四、关键知识点指针有类型int *、char *、double *指针类型决定两件事该地址对应内存解读成什么数据指针加减运算时一次偏移多少字节。char*指针 1向后跳 1 字节int*指针 1向后跳 4 字节double*指针 1向后跳 8 字节指针本身也占内存64 位系统下所有类型指针统一占 8 字节只存地址和指向的数据大小无关。空指针 NULLNULL 代表地址 0表示指针不指向任何有效内存防止野指针访问崩溃。野指针指针存了随机、非法地址没有初始化、没有正确赋值直接*p访问会程序崩溃。总结变量存数值指针存内存地址变量拿地址*指针顺着地址找到数据引用和指针如何选择核心原则能使用引用就优先用引用必须用指针的时候才用指针如果确定变量一定有效、不需要改变指向优先选择引用。引用语法简洁不存在空的情况更安全。如果场景允许对象为空或者运行时需要更换指向的对象、使用动态内存则使用指针。nullptr和NULL有何区别NULL 是宏等价于整数 0没有指针类型在函数重载时会产生歧义nullptr 是 C11 引入的关键字属于专门的空指针类型 std::nullptr_t只能转为各类指针不会和整型混淆。C 代码推荐优先使用 nullptr 表示空指针。RAII的核心机制是什么RAII 的核心是把资源生命周期绑定到对象生命周期构造时获取资源析构时释放资源即使异常发生栈展开也会自动执行析构new/delete与malloc/free有何区别malloc/free 是 C 库函数只负责内存分配释放不调用构造、析构函数new/delete 是 C 运算符分配内存同时调用构造函数释放前调用析构函数。malloc 返回 void*需要强制转换new 直接返回对应类型指针。malloc 失败返回 NULLnew 默认抛异常nothrow 版本返回空指针。new/delete 支持重载malloc/free 不可重载数组 new [] 要配套 delete []。栈和堆有何区别栈内存由系统自动分配和释放空间小、分配速度快用于存放局部变量、函数参数超出容量会栈溢出。 堆内存由开发者通过 new/malloc 手动申请delete/free 手动释放空间大分配较慢频繁使用容易产生内存碎片若忘记释放会造成内存泄漏。全局、局部、静态局部对象何时初始化和销毁全局main 前构造main 结束后析构普通局部进作用域构造出作用域析构static 局部首次执行到定义处才构造main 结束后析构类成员初始化顺序由什么决定只由【成员在类里面声明的先后顺序】决定和初始化列表的书写顺序无关先初始化父类成员继承的话先构造父类再按类内声明顺序初始化普通成员变量不是初始化列表顺序最后执行构造函数函数体{}里面代码构造中途抛异常已构造成员如何释放已经成功构造完成的成员、基类子对象会自动调用析构函数释放还没构造完的不会析构。什么是移动语义把一个对象管理的堆资源内存、句柄等直接 “偷” 给新对象不拷贝底层数据只是转移资源所有权原来对象变成合法但不可用的 “空壳”。std::move仅仅是类型转换把入参无条件强制转换成右值引用T。真正的资源转移偷指针、转移内存是移动构造函数 / 移动赋值运算符里面的代码完成的。拷贝构造、拷贝赋值、移动构造、移动赋值有何区别构造 新建对象赋值 对象已存在。 拷贝 复制一份移动 偷资源转移所有权。虚函数如何实现动态多态动态多态父类指针 / 引用指向子类对象调用虚函数时在运行时根据对象真实类型执行子类版本的函数而不是指针的静态类型版本。依靠虚函数表 虚指针实现。class Base{ public: virtual void func(){coutBaseendl;} }; class Derive:public Base{ public: void func() override {coutDeriveendl;} }; int main(){ Base* p new Derive(); //父类指针指向子类对象 p-func(); //输出 Derive动态多态 }函数重载和重写有何区别C中的智能指针STL中有哪些容器序列容器元素有序内存存储不一样vector动态数组连续内存。随机访问 O (1)尾部增删快中间插入删除慢。deque双端队列分段连续内存。首尾增删 O (1)支持随机访问。list双向链表非连续内存。任意位置插入删除 O (1)不支持随机访问。forward_list单向链表只支持向后遍历内存开销更小。array固定大小数组编译期确定长度连续内存不能扩容。2. 关联容器底层红黑树自动有序key 唯一set集合元素唯一自动升序multiset允许元素重复map键值对key-valuekey 唯一按 key 排序multimapkey 允许重复3. 无序关联容器底层哈希表查找极快无序unordered_set无序集合元素唯一unordered_multiset无序、可重复unordered_map无序键值对key 唯一开发最常用unordered_multimapkey 可重复4. 容器适配器不是独立容器包装上面容器实现stack栈默认底层 deque后进先出 LIFOqueue队列默认底层 deque先进先出 FIFOpriority_queue优先队列堆默认大顶堆底层 vector小结需要随机访问优先vector频繁头尾增删deque频繁中间插入删除不需要随机访问list有序查找map/set快速查找、不要求有序unordered_map/unordered_set进程和线程各自独占、共享哪些资源进程是资源分配的基本单位线程是调度执行的基本单位。同一个进程内的线程共享进程资源每个线程有自己少量私有资源。进程独立程序实例✅进程独占私有独立地址空间虚拟内存文件描述符表、堆、代码段、全局变量进程 ID、PCB 进程控制块信号处理方式✅进程之间默认不共享资源想要共享需要 IPC管道、消息队列、共享内存、socket 等线程进程内执行流轻量级同一个进程下线程之间共享进程虚拟地址空间代码段、全局变量、堆文件描述符、打开的文件信号处理函数进程 ID每个线程独占私有线程栈局部变量非常重点局部变量在线程栈不共享线程寄存器、程序计数器 PC线程 ID、TCB 线程控制块信号屏蔽字
阅读完成 · 觉得有帮助?
咨询建站