当前位置: 首页 > news >正文

C 语言第 17 天学习笔记:从二级指针到内存布局的进阶指南

C语言进阶:从二级指针到内存布局的深度解析

一、指针进阶:二级指针的应用与特性

1.1 二级指针的定义与基本用法

二级指针(多重指针)用于存储一级指针的地址,需要两次次解引用才能访问原始数据。在实际开发中,二级指针是最常见的多重指针类型。

int a = 10; // 普通变量,原始数据
int *p = &a; // 一级指针,指向a,一次解引用可获取a的值
printf("%d\n", *p); // 输出:10int **w = &p; // 二级指针,指向p,两次解引用可获取a的值
printf("%d\n", **w); // 输出:10int ***k = &w; // 三级指针,指向w,三次解引用可获取a的值
printf("%d\n", ***k); // 输出:10

1.2 二级指针的语法与特点

语法格式

数据类型 **指针变量名 = 指针数组的数组名 | 一级指针的地址

核心特点

  1. 与指针数组的等效性
    二级指针与指针数组在某些场景下可以等效使用,但与二维数组不等效。

    // 指针数组示例
    int arr[] = {11, 22, 33};
    int *arr_[] = {&arr[0], &arr[1], &arr[2]};// 二级指针接收指针数组
    char *str[3] = {"abc", "aaa034", "12a12"};
    char **p = str; // p存储数组首地址,*p访问列地址,**p访问列元素
    
  2. 与二维数组的差异
    二维数组名是数组指针类型(如int (*)[3]),直接赋值给二级指针会导致类型不匹配:

    int arr[2][3] = {{1, 3, 5}, {11, 33, 55}};
    int (*p)[3] = arr; // 正确:数组指针指向二维数组
    int **k = arr; // 错误:类型不兼容(int(*)[3] 与 int**)
    

1.3 二级指针的解引用操作

字符型二级指针可直接遍历字符串数组,操作类似一维数组:

#include <stdio.h>void fun2() {char *arr[] = {"orange", "apple", "grape", "banana", "kiwi"};int len = sizeof(arr) / sizeof(arr[0]);char **p = arr; // 二级指针等价于指针数组for (int i = 0; i < len; i++) {printf("%s\n", *(p + i)); // 指针法访问}
}

其他类型二级指针需要两次解引用访问数据,常用于操作指针数组:

#include <stdio.h>int main() {int arr1[] = {11, 22, 33, 44, 55, 66};int *arr[] = {&arr1[0], &arr1[1], &arr1[2], &arr1[3], &arr1[4], &arr1[5]};int **p = arr; // 二级指针接收指针数组for(int i = 0; i < sizeof(arr)/sizeof(arr[0]); i++){printf("%-6d", **(p+i)); // 指针法访问}
}

二、main函数原型解析

main函数作为程序入口,有多种定义格式,其中标准写法为:

int main(int argc, char *argv[]) { ... }
// 或等价形式
int main(int argc, char **argv) { ... }

2.1 参数含义与用法

-** argc :存储命令行参数的个数,默认值为1(程序名本身)
-
argv **:存储所有参数的字符串形式,是一个字符型指针数组

示例代码

#include <stdio.h>int main(int argc, char **argv) {printf("参数个数: %d\n", argc);for(int i = 0; i < argc; i++){printf("参数%d: %s\n", i, argv[i]);}return 0;
}

三、常量指针与指针常量

3.1 常量指针(指向常量的指针)

定义:指向常量数据的指针,指针指向的数据不可修改,但指针本身的指向可以改变。

语法

const 数据类型 *变量名;
// 或
const 数据类型* 变量名;

示例

int a = 10, b = 20;
const int *p = &a;
// *p = 30; // 错误:不能修改指向的数据
p = &b; // 正确:可以改变指向

3.2 指针常量(指针本身是常量)

定义:指针本身是常量,指向固定地址,但指向的数据可以修改。

语法

数据类型* const 变量名;
// 或
数据类型 *const 变量名;

示例

int a = 10, b = 20;
int* const p = &a;
*p = 30; // 正确:可以修改指向的数据
// p = &b; // 错误:不能改变指向

3.3 常量指针常量

定义:指针指向和指向的数据都不可改变。

语法

const 数据类型* const 变量名;

示例

int a = 10, b = 20;
const int* const p = &a;
// *p = 30; // 错误
// p = &b; // 错误

3.4 总结对比

类型语法指向可变数据可变
常量指针const int *p✔️
指针常量int *const p✔️
常量指针常量const int *const p

四、野指针、空指针与空悬指针

4.1 野指针

定义:指向无效内存区域的指针(未初始化、已释放或越界访问)。

产生场景

  1. 指针未初始化

    int *p; // 野指针
    printf("%d\n", *p); // 危险操作
    
  2. 指针指向已释放的内存

    int *p = malloc(sizeof(int));
    free(p);
    printf("%d\n", *p); // 危险操作
    
  3. 返回局部变量的地址

    int* fun() {int sum = 0;return &sum; // 危险:返回局部变量地址
    }
    

避免方法

  • 初始化指针为NULL
  • 释放内存后立即置为NULL
  • 避免返回局部变量地址
  • 使用前检查指针有效性

4.2 空指针

定义:值为NULL的指针,指向地址0x00000000(系统保留,不可访问)。

作用:明确表示指针当前不指向有效内存,用于指针初始化。

int *p = NULL; // 初始化为空指针

4.3 空悬指针

定义:指针指向的内存已被释放,但未重新赋值,是野指针的一种特例。

char *p = malloc(100);
free(p); // 释放内存后,p成为空悬指针

五、void与void*的区别

5.1 void类型

表示"无类型/空类型",用于函数返回类型或参数:

void func(void); // 无返回值,无参数

5.2 void*类型(通用指针)

可指向任意类型数据,但需要强制类型转换后才能解引用:

void* ptr = malloc(4); // 分配4字节内存// 存储int类型
int *p = (int*)ptr;
*p = 10;// 存储float类型
float* p1 = (float*)ptr;
*p1 = 12.5f;

注意void*只能与具体类型指针(int*double*等)之间进行转换。

六、C语言内存管理

6.1 进程内存布局

每个C语言进程拥有结构相同的虚拟内存,包含以下区域:

-** 栈(stack):存储环境变量、命令行参数、局部变量
-
堆(heap):动态内存区域,可由开发者管理
-
数据段 :包含.bss段(未初始化静态数据)、.data段(已初始化静态数据)、.rodata段(常量数据)
-
代码段 **:包含.text段(用户代码)、.init段(系统初始化代码)

6.2 各内存区域特性

栈内存

  • 空间有限,自动分配和释放
  • 函数调用时栈向下增长,函数退出时栈向上缩减

静态数据

  • 包括全局变量和static修饰的局部变量
  • 程序启动时分配,程序退出时释放
  • 未初始化时自动初始化为0

堆内存

  • 大小受限于物理内存
  • 从下往上增长
  • 需要开发者手动申请和释放
  • 相关API:malloc()calloc()realloc()free()

示例

// 堆内存操作示例
int *p = malloc(sizeof(int)); // 申请内存
*p = 100; // 存储数据
free(p); // 释放内存
p = NULL; // 避免空悬指针// 连续内存申请
double *k = calloc(3, sizeof(double)); // 已清零
k[0] = 0.618;
free(k);

七、总结

本文深入探讨了C语言中的二级指针、main函数原型、常量指针与指针常量、各类特殊指针以及内存布局等进阶知识。掌握这些概念对于理解C语言的内存管理机制、编写高效安全的代码具有重要意义。特别是内存管理部分,需要开发者手动管理堆内存,这既是C语言的灵活性所在,也是容易出现bug的地方,需要格外注意。

http://www.dtcms.com/a/319264.html

相关文章:

  • 力控汽车零部件冲压MES系统方案
  • 2025最新国内服务器可用docker源仓库地址大全(2025年8月更新) · DockerHub镜像加速全面指南
  • STM32学习笔记4-OLED外部中断和中断系统
  • nlp-句法分析
  • 虚幻GAS底层原理解剖八 (自定义子类)
  • nohup 学习笔记
  • AWS RDS自定义终端节点深度分析工具:Python脚本详解
  • PyTorch 核心三件套:Tensor、Module、Autograd
  • 旅游mcp配置(1)
  • cookie和session之间区别
  • 从BaseMapper到LambdaWrapper:MyBatis-Plus的封神之路
  • 组件安全漏洞
  • 站在Vue的角度,对比鸿蒙开发中的状态管理
  • 机器学习工程化 3.0:从“实验科学”到“持续交付”的 7 个关卡
  • 淘宝商品价格数据采集||淘宝SKU价格数据采集API
  • 从配置到远程访问:如何用群晖NAS FTP+ Cpolar搭建稳定文件传输通道
  • Charles中文版抓包工具使用指南 提高API调试和网络优化效率
  • 通信中间件 Fast DDS(一) :编译、安装和测试
  • rk3588s vscode索引失败的问题
  • Numpy科学计算与数据分析:Numpy随机数生成入门
  • Numpy科学计算与数据分析:Numpy数据分析基础之统计函数应用
  • 【线性代数】5特征值和特征向量
  • Android 原生与 Flutter 通信完整实现 (Kotlin 版)
  • C++基础:继承
  • qt系统--事件
  • 设计模式中的行为模式
  • sqli-labs-master/Less-41~Less-50
  • 论文Review 激光实时动态物体剔除 DUFOMap | KTH出品!RAL2024!| 不上感知,激光的动态物体在线剔除还能有什么方法?
  • DrissionPage自动化:高效Web操作新选择
  • 【人工智能99问】NLP(自然语言处理)大模型有哪些?(20/99)