
一、位运算位运算仅支持整型和字符型数据类型。位运算符是直接对二进制数进行操作的运算符。运算符名称运算规则双目/单目典型应用场景注意事项按位与两位都为1时结果为1否则为0双目清零指定位某位与0相与判断某位是否为1常与掩码配合使用|按位或两位中至少有一位为1时结果为1否则为0双目置1指定位某位与1相或合并多个标志位常与掩码配合使用^按位异或两位不同相异时结果为1相同则为0双目不借助临时变量交换两数数据简单校验如奇偶校验特定位取反与1异或相同数异或结果为0与0异或保持不变~按位取反将操作数的每一位翻转0变11变0单目生成全1掩码如 ~0配合实现位清零注意操作数的有符号性取反后符号位会改变可能导致负数右移将二进制位整体右移低位溢出舍弃双目高效除以2的幂仅对正数或逻辑右移提取高低字节逻辑右移无符号高位补0算术右移有符号高位补符号位负数补1正数补0结果是否向下取整取决于编译器和数据类型左操作数是需要被操作的数右操作数是移动的位数左移将二进制位整体左移高位溢出舍弃低位补0双目高效乘以2的幂组合构造数据如将字节放入高位有符号数左移可能改变符号位导致未定义行为溢出左移位数不应大于或等于操作数的位宽左操作数是需要被操作的数右操作数是移动的位数#include stdio.h int main(int argc, char **argv) { //两数进行交换 只能是整形变量 int a 10; int b 20; a ^ b; b ^ a; a ^ b; printf(a is %d ,b is %d\n, a, b); int testa 0xa5; // a5 的bit6,3 置1 // 1 6--00000001 - 01000000 testa testa | (1 6) | (1 3); printf(testa is %d\n, testa); // a5 的bit3 清零 0 testa 0xa5; testa testa ~(1 3); //等价于 testa ~(1 3) return 0; }二、内存管理2.1 内存空间介绍当./a.out运行后操作系统会为其分配一个虚拟地址空间从低地址到高地址通常分为以下几个主要区内存区域存储内容分配与释放生命周期关键特性代码段.text存放编译后的机器指令C语言代码系统自动进程开始到结束只读防止程序意外修改自身指令数据段.data / .bss / .rodata细分如下• .data已初始化的全局变量和static变量• .bss未初始化的全局变量和static变量系统自动清零• .rodata字符串常量、const修饰的全局变量等系统自动进程开始到结束.bss 段在可执行文件中不占磁盘空间加载时由系统分配并清零.rodata 段只读数据段堆区Heap动态申请的内存如malloc()/calloc()/realloc()手动申请malloc 族函数手动释放free()从申请到主动释放或进程结束由程序员决定生命周期• 空间较大通常可达 2.9GB 左右• 可动态增长• 若忘记释放会产生内存泄漏共享库映射区Memory Mapping Segment动态链接库如libc.so中的printf、scanf等函数实现也可用于mmap映射文件系统自动加载/卸载动态库被加载到进程结束可通过ldd ./a.out查看程序依赖的共享库栈区Stack局部变量、函数参数、返回地址、函数调用上下文系统自动申请和释放作用域结束函数返回即释放• 空间固定通常默认 8MB• 先进后出LIFO• 由编译器管理效率高但空间有限注意① 地址增长方向栈→ 向低地址增长由高地址向低地址扩展堆→ 向高地址增长由低地址向高地址扩展两者相向而行中间是共享库区域最大限度利用虚拟地址空间② 内存管理权归属栈编译器自动管理无需程序员干预堆程序员手动管理申请/释放更灵活但也更容易出错可以在程序运行时需要内存的时候在堆中申请。2.2 动态内存函数① void *malloc(size_t size)malloc是动态分配堆空间内存 size 参数是需要分配的内存大小字节。 返回值是分配到堆内存空间的首地址值。地址值需要保留好直到调用free释放。 释放堆内存空间 参数如要传入当时malloc的返回值。这个值要和但是分配空间的地址值一致。② void free(void *ptr)free是释放动态分配的堆内存ptr是要释放的内存地址必须是malloc返回的地址释放完后应立即将指针置为NULLptr NULL 不然ptr 是野指针。 也不要试图去访问原来的堆空间数据。 从逻辑上认为堆空间数据生命周期结束。③ 注意free()释放内存后数据是否保留完全取决于后续内存的使用情况若系统内存空间使用紧张则free后的空间立马被使用数据被覆盖。若空间使用不紧张则数据保留不被覆盖#include stdio.h #include stdlib.h int main(int argc, char **argv) { int a 20; // 局部变量 int* p a; // 指针指向了一个局部变量 printf(栈空间 p is %p\n,p); // q 接收堆空间后指针本身就不应在发生变化。 指针指向的内容任意修改 // int* const q malloc(); int * q (int*)malloc(sizeof(int)); // void* 0x1000在堆区申请4字节空间 *q *p; //q 指向堆内存p 指向栈内存两者独立 printf(堆空间 q is %p\n,q); printf(*q is %d ,*p is %d\n,*q,*p); free(q); // 归还空间 , q中依然存储堆空间的地址值 q为野指针 .释放后的空间不要在去访问 q NULL; // 空指针。将q置为NULL防止野指针 // q a;// 错误1 丢失了堆空间的地址。 // free(q); // free(): invalid pointer 释放了栈空间 //q; //指针偏移指向1004 //free(q); // free(): invalid pointer //free() 只能释放 malloc 返回的原始地址 //释放非原始地址会导致堆结构破坏程序崩溃 return 0; }#include stdio.h #include string.h #include stdlib.h void fun(char** p) // p 是二级指针 { *p (char*)malloc(100); // 修改 main 中的 p } int main() { char* p NULL; // 把一个指针当作参数传递并且需要在被函数中修改指针本身就需要传递本指针的地址也就是二级指针。 fun(p); // 传递 p 的地址即二级指针 strcpy(p, hello); printf(p is %s\n, p); free(p); // 释放堆内存 p NULL; // 防止野指针 return 0; }2.3 内存泄漏Memory Leak① 内存泄漏程序在动态申请内存堆区后没有及时释放或失去了对这块内存的控制导致该内存无法被再次使用直到程序结束。② 仅申请堆内存空间而不释放会导致系统内存耗尽最终引发程序异常终止。void func() { int *p (int*)malloc(100 * sizeof(int)); // 使用 p ... // ❌ 忘记调用 free(p); } // 函数结束p 被销毁但堆内存仍被占用 说明反复调用该函数每次泄漏 100 个 int内存耗尽最终系统无可用内存 后续 malloc 返回 NULL程序崩溃或行为异常 最终系统因频繁缺页中断而变慢性能下降③ 申请堆空间后若保存堆地址的指针意外被覆盖将导致原有的堆空间无法访问从而造成内存泄漏。int *p (int*)malloc(100 * sizeof(int)); // 使用 p ... p (int*)malloc(200 * sizeof(int)); // ❌ 重新赋值覆盖了原地址 // 第一块 100 个 int 的内存永远丢失了无人指向无法访问无法释放注意程序进程结束后系统OS 会回收所有内存但长期运行的程序服务器必须避免泄漏。