6.2.1指针运算#include stdio.h int main(void) { char ac[] {0,1,2,3,4,5,6,7,8,9,};// 声明长度为10的字符数组 char *p ac; // p指向ac数组的第一个元素ac[0] printf(p%p\np) // 打印ac[0]的地址例如0x7ffd09 printf(p1%p\np1);// 地址1移动sizeof(char)1字节例如0x7ffd0a printf(*(p1)%d\n*(p1));// 解引用p1 等价于ac[1]的值输出1 *(pn)-ac[n]// *(pn) ac[n] 是指针运算核心特征 int ai[] {0,1,2,3,4,5,6,7,8,9,};// 声明长度为10的整型数组 int *q ai; // q指向ai[0] printf(q%p\n, q); // 打印ai[0]的地址例如0x7ffd30 printf(q1 %p\n, q1); // 地址4移动sizeof(int)4字节例如0x7ffd34 printf(*(q1)%d\n, *(q1)); // 解引用q1 ai[1]的值输出1 return 0; }指针基本运算1.基本算术运算指针±整数本质根据指针类型自动计算步长int arr[5] {10,20,30,40,50}; int *p arr; // p指向arr[0] p p 3; // 移动3个int单位实际地址12字节 // 现在p指向arr[3]值40特殊规律char*每次±1字节int*每次±4字节32位系统double*每次±8字节2.指针自增/自减/--典型应用遍历数组char str[] hello; char *c str; while(*c ! \0) { printf(%c, *c); c; // 自动移动sizeof(char)1字节 } 等效于 for(int i0; str[i]!\0; i) { printf(%c, str[i]); }3.指针相减仅限同类型指针作用计算元素间隔数int arr[10] {0}; int *p1 arr[2]; // 指向第3个元素 int *p2 arr[5]; // 指向第6个元素 int gap p2 - p1; // 结果是3不是字节差 // 实际计算(地址差)/sizeof(int)4.关系运算 用途判断指针位置关系int *p arr; int *end arr 10; while(p end) { // 检查是否越界 *p 0; // 清零 p; } 注意不同类型指针比较是未定义行为5.指针与数组的等价关系黄金法则arr[i] ≡ *(arr i) arr[i] ≡ arr i 示例 int arr[3] {1,2,3}; printf(%d, *(arr1)); // 输出2等同于arr[1]6.特殊运算void*指针特点可存储任意类型地址但不能直接运算int num 10; void *vp num; // 必须强制类型转换后才能运算 int *p (int*)vp 1; // 合法易错点警报野指针运算未初始化的指针操作会崩溃int *p; // 未初始化 p; // 危险2.越界访问int arr[5]; int *p arr 10; // 越界3.不同类型指针运算char *cp; int *ip; if(cp ip) { /* 编译器会警告 */ }*p的运作原理*p相当于「先取指针当前指向的值再把指针往后挪一步」。⚠️ 注意是移动指针本身不是修改指针指向的值1.取出p所指的那个数据来完事之后顺便把p移到下一个位置去2.*的优先级虽然高但是没有高常用于数组类的连续空间操作3.在某些CPU上这可以直接被翻译成一条汇编指令指针比较,,,!都可以对指针做比较它们在内存中的地址数组中的单元的地址肯定是线性递增的0地址0地址是计算机内存中编号为0的存储单元相当于内存世界的原点坐标。但在现代操作系统中它如同现实中的皇宫禁区——物理存在但禁止随意访问。指针没有被真正初始化先初始化为0NULL是一个预定定义的符号表示0地址有的编译器不愿意你用0来表示0地址指针的类型无论指向什么类型所有的指针的大小都是一样的因为都是地址但是指向不同类型的指针是不能直接互相赋值的这是为了避免用错指针指针的类型转换void*表示不知道指向什么东西的指针计算时与char*相同但不相通指针也可以转换类型int *p i; void*q (void*)p;这并没有改变p所指的变量的类型而是让后人用不同的眼光通过p看它所指的变量我不再当你是int啦我认为你就是个void6.2.2动态内存分配mallocmalloc是 C 语言中“memory allocation”内存分配的缩写它的作用是在程序运行时向系统申请一块指定大小的内存空间。你可以把它想象成“向操作系统借一块空地”用来存放你的数据比如整数、数组、结构体等。malloc 就是“按需借内存用完必须还借之前先看有没有借到检查 NULL还了之后别再用置 NULL”。与普通变量如int a;在编译时就确定好内存不同malloc 让你可以在程序运行过程中根据需要临时决定要多大内存这叫做“动态内存分配”。比如你写一个程序用户输入一个数字 N你想创建一个长度为 N 的数组用int arr[N];在早期的 C 标准中是不允许的因为数组大小必须是常量但用 malloc 就可以轻松搞定malloc(N * sizeof(int))。关键点头文件#include stdlib.h或#include malloc.h函数原型void *malloc(size_t size);返回值成功 → 返回一个void*类型的指针指向分配好的内存块的首地址失败 → 返回NULL分配的内存不会自动初始化里面的值是随机的垃圾值用法四步法1️⃣ 包含头文件#include stdlib.h // malloc 和 free 都在这里2️⃣ 调用 malloc 分配内存并检查是否成功int *p (int *)malloc(sizeof(int) * 10); // 申请 10 个整数的空间 if (p NULL) { // 分配失败处理错误 printf(内存不够了\n); return 1; }为什么加(int *)malloc 返回的是void*无类型指针在 C 中必须强制转换在 C 中虽然不是必须但建议转换方便移植。为什么检查 NULL如果系统内存不足malloc 会返回 NULL不检查就直接使用会导致程序崩溃空指针解引用。3️⃣ 使用分配的内存for (int i 0; i 10; i) { p[i] i * 2; // 像普通数组一样使用 }4️⃣ 用完后释放内存并置空指针free(p); // 把内存“还”给系统 p NULL; // 避免 p 变成“野指针”释放内存非常重要否则会导致“内存泄漏”——程序占用的内存越来越多最终系统资源耗尽。释放后把指针置空可以防止后面误用这个指针比如再次 free 或者解引用。#include stdio.h #include stdlib.h int main(void) { int number; int* a;inti; printf(输入数量); scanf(%dnumber);// int a[number]; a (int*)malloc(number*sizeof(int)); fori0;inumberi){ scanf(%da[i]); } for(inumber-1i0i--){ printf(%d ,a[i]); } return 0 }常见坑与最佳实践1️⃣ 内存泄漏只分配不释放就像借了东西不还。每个 malloc 必须对应一个 free否则程序长时间运行后会耗尽内存。2️⃣ 野指针 / 悬挂指针释放内存后指针仍然指向那块地址但系统已经收回。如果继续使用行为是未定义的可能崩溃或数据错乱。释放后立即置 NULL。3️⃣ 忘记检查返回值如果 malloc 返回 NULL比如内存不足直接使用指针会导致程序崩溃。永远要检查 if (ptr NULL)。4️⃣ 分配大小错误比如malloc(1)却想存一个int4字节会导致越界破坏其他数据。用sizeof(类型)计算正确大小。5️⃣ 多次释放同一块内存第一次 free 后指针没有置 NULL再次 free 会导致未定义行为。将指针置 NULL 后free(NULL) 是安全的。freefree是 C 语言中用来释放动态内存的函数与malloc、calloc、realloc配对使用。你可以把它想象成“把借来的内存还回去”——之前用malloc向系统“借”了一块内存用完之后用free“还”给系统让这块内存可以被其他程序或后续代码再次使用。free就是“借了要还还了别再用还之前检查是不是借的还之后把借条销毁置 NULL”。关键点头文件#include stdlib.h函数原型void free(void *ptr);参数指向之前由malloc/calloc/realloc分配的内存块的指针。返回值无void。核心原则谁申请谁释放成对出现。用法1. 基本语法int *p (int *)malloc(10 * sizeof(int)); // 申请内存 // 使用 p... free(p); // 释放内存 p NULL; // 将指针置空防止野指针2. 使用步骤第1步确认指针指向的是动态分配的内存free只能释放通过malloc、calloc或realloc在堆区开辟的内存绝对不能用于释放栈区变量如数组或全局变量。// ✅ 正确释放堆区内存 int *p (int *)malloc(40); free(p); // ❌ 错误释放栈区数组 int arr[10] {0}; free(arr); // 未定义行为第2步释放前检查指针是否为空如果指针是NULLfree什么都不会做这是安全的。但如果指针未初始化指向随机地址直接free会导致崩溃。int *p NULL; free(p); // ✅ 安全什么也不做第3步释放后将指针置为 NULL这是最关键的一步释放后指针仍然指向原来的地址但该地址已不属于程序这叫做悬空指针Dangling Pointer。如果不置空后续误用这个指针会造成非法访问可能导致程序崩溃。free(p); p NULL; // 彻底切断联系注意为什么要用 free—— 避免内存泄漏内存泄漏是指程序在运行过程中只申请内存却不释放导致系统可用内存越来越少最终程序可能因内存不足而崩溃。举个生活例子你向朋友借了一本书看完后不还朋友书架上的书就会越来越少。如果每个借书的人都不还最后谁都借不到书了。free就是“还书”这个动作。关键原则每个malloc必须对应一个free否则程序长期运行后内存会逐渐被耗尽。
郑州网站建设
网页设计
企业官网