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

c语言指针学习

好的,我们来系统地、详细地讲解 C 语言中指针的用法。指针是 C 语言的精髓,也是最强大且最容易出错的部分。

一、指针是什么?

指针的本质是一个变量,但它存储的不是普通的数据,而是另一个变量的内存地址

你可以把它想象成一张“地址纸条”。假设变量 a 住在一栋大楼(内存)的 101 房间,那么指针 p 就是一张写着 “101” 的纸条。通过这张纸条,你就能找到 a

  • & (取地址运算符): 用于获取一个变量的内存地址。
    • 例如:&a 表示获取变量 a 的地址。
  • * (解引用运算符): 用于获取指针所指向的内存地址中存储的值。
    • 例如:*p 表示获取指针 p 所指向地址的值。

二、指针的声明与初始化

// 1. 声明一个整型变量 a,并赋值为 10
int a = 10;// 2. 声明一个指向整型的指针 p
//    语法:目标数据类型 *指针变量名;
int *p;// 3. 将变量 a 的地址赋值给指针 p (初始化)
//    现在 p 存放着 a 的地址,我们说 “p 指向 a”
p = &a;// 也可以在声明的同时初始化
// int *p = &a;

理解 * 的两种角色:

  • int *p; 中的 *: 用于声明一个指针变量,告诉编译器 p 是一个指向 int 的指针。
  • printf("%d", *p); 中的 *: 用于解引用,获取 p 指向的值。

三、指针的核心操作与用法

1. 基本操作:通过指针修改变量的值
#include <stdio.h>int main() {int a = 10;int *p = &a; // p 指向 aprintf("a 的值: %d\n", a);     // 输出: 10printf("a 的地址: %p\n", &a);  // 输出: 类似 0x7ffd42a1a23cprintf("p 的值(即a的地址): %p\n", p); // 输出: 和上一行相同printf("*p 的值(即a的值): %d\n", *p); // 输出: 10// 通过指针修改变量 a 的值*p = 20; // 找到 p 指向的地址,向里面写入 20printf("修改后 a 的值: %d\n", a); // 输出: 20printf("修改后 *p 的值: %d\n", *p); // 输出: 20return 0;
}
2. 指针与数组

数组名本质上就是一个指向数组第一个元素的常量指针

#include <stdio.h>int main() {int arr[5] = {1, 2, 3, 4, 5};int *p = arr; // 等价于 int *p = &arr[0];// 通过指针访问数组元素// 指针加法: p + i 表示向后移动 i 个“单位”(单位大小取决于指针类型,int 是 4 字节)printf("第一个元素: arr[0]=%d, *p=%d\n", arr[0], *p);printf("第二个元素: arr[1]=%d, *(p+1)=%d\n", arr[1], *(p + 1));printf("第三个元素: arr[2]=%d, *(p+2)=%d\n", arr[2], *(p + 2));// 通过指针遍历数组printf("遍历数组: ");for (int i = 0; i < 5; i++) {printf("%d ", *(p + i));// 另一种常见写法: printf("%d ", p[i]); // 下标和指针可以混用}printf("\n");// 指针也可以自增来遍历p = arr; // 重新指向开头printf("指针自增遍历: ");for (int i = 0; i < 5; i++) {printf("%d ", *p);p++; // p 指向下一个元素}printf("\n");return 0;
}
3. 指针与函数(重点)
a) 传址调用 (Call by Reference)

C 语言是“传值调用”,函数内部无法修改外部实参的值。通过指针,我们可以实现“传址调用”,在函数内部修改外部变量的值。

#include <stdio.h>// 错误的交换函数:只交换了形参的值,不影响实参
void swap_wrong(int x, int y) {int temp = x;x = y;y = temp;
}// 正确的交换函数:通过指针操作实参所在的内存
void swap_correct(int *x, int *y) {int temp = *x; // 取出 x 指针指向的值*x = *y;       // 把 y 指针指向的值,放入 x 指针指向的地址*y = temp;     // 把 temp 的值,放入 y 指针指向的地址
}int main() {int a = 10, b = 20;printf("交换前: a=%d, b=%d\n", a, b);swap_wrong(a, b);printf("错误交换后: a=%d, b=%d\n", a, b); // 输出: a=10, b=20 (未改变)swap_correct(&a, &b); // 传入变量的地址printf("正确交换后: a=%d, b=%d\n", a, b); // 输出: a=20, b=10 (成功交换)return 0;
}
b) 返回多个值

函数只能返回一个值,但如果需要返回多个值,可以通过指针参数来实现。

void getMinMax(int arr[], int len, int *min, int *max) {*min = arr[0];*max = arr[0];for (int i = 1; i < len; i++) {if (arr[i] < *min) *min = arr[i];if (arr[i] > *max) *max = arr[i];}
}int main() {int array[] = {21, 54, 11, 8, 92, 67};int min_val, max_val;getMinMax(array, 6, &min_val, &max_val);printf("最小值: %d, 最大值: %d\n", min_val, max_val);return 0;
}
4. 动态内存分配

这是指针至关重要的用途。使用 malloc, calloc, realloc堆 (Heap) 上申请内存,并使用 free 释放。

#include <stdio.h>
#include <stdlib.h> // 包含动态内存管理函数int main() {int n;int *dynamic_arr;printf("请输入数组大小: ");scanf("%d", &n);// 在堆上申请 n 个 int 大小的连续内存,并将首地址赋给 dynamic_arrdynamic_arr = (int *)malloc(n * sizeof(int));if (dynamic_arr == NULL) {printf("内存申请失败!\n");exit(1); // 退出程序}// 使用这块内存for (int i = 0; i < n; i++) {dynamic_arr[i] = i * 10;}printf("动态数组元素: ");for (int i = 0; i < n; i++) {printf("%d ", dynamic_arr[i]);}printf("\n");// 非常重要:使用完毕后必须释放内存,防止内存泄漏free(dynamic_arr);// 良好习惯:释放后将指针置为NULL,防止“悬空指针”dynamic_arr = NULL;return 0;
}

四、指针的进阶概念(了解)

  1. **指针的指针 (int pp): 指向指针的指针。
  2. 函数指针: 指向函数的指针,可用于回调函数等高级功能。
    int (*funcPtr)(int, int); // 声明一个函数指针,该指针指向一个接受两个int参数并返回int的函数
    funcPtr = &add; // 指向一个名为 add 的函数
    result = (*funcPtr)(3, 4); // 通过指针调用函数
    
  3. const 与指针
    • const int *p: 指针指向的值是常量,不可修改 (*p = 20; 错误)。
    • int *const p: 指针本身是常量,不可再指向别处 (p = &b; 错误)。
    • const int *const p: 指针本身和指向的值都是常量。

五、常见错误与陷阱

  1. 未初始化的指针(野指针)

    int *p; // 未初始化,p 指向一个随机地址
    *p = 10; // 向未知地址写入数据,可能导致程序崩溃!严重错误!
    

    解决方法: 声明时初始化为 NULL

  2. 访问已释放的内存(悬空指针)

    int *p = (int*)malloc(sizeof(int));
    free(p);   // 释放 p 指向的内存
    *p = 10;   // 错误!p 现在是一个悬空指针,指向的内存已无效。
    

    解决方法free(p); 后立刻 p = NULL;

  3. 内存泄漏
    申请了内存 (malloc),但忘记释放 (free),导致程序持续占用内存,最终可能耗尽系统资源。

总结

用途描述关键点
基本操作通过指针间接访问和修改变量&, *
与数组高效地遍历和操作数组数组名即指针,指针算术运算
与函数实现传址调用,修改实参,返回多个值函数参数定义为指针
动态内存在运行时申请任意大小的内存malloc, calloc, realloc, free

掌握指针需要大量的练习和实践。先从简单的例子开始,理解地址和值的区别,然后再逐步深入到数组、函数和动态内存管理中。

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

相关文章:

  • C文件编译
  • IQC、IPQC、PQC、FQC、OQC在ERP/MES/WMS中的系统协同
  • 【SBP】Unity 打包构建管线原理解析于对比
  • 什么是服装企业管理软件?
  • 【Canvas与旗帜】金波浪圈法兰西国旗
  • 广告业务连续四季度双位数增长,B站做了什么?
  • DAY 51 复习日+退款开始
  • 数据挖掘 4.8 评估泛化能力
  • 【DeepResearch调研】基于知识图谱与数据合成的大语言模型幻觉缓解研究前沿
  • C++ Core Guidelines: 最佳实践与深入解析
  • 服务器硬件电路设计之 SPI 问答(五):服务器场景下的ESD防护策略与通信故障诊断指南
  • Flink元空间异常深度解析:从原理到实战调优指南
  • LLM实践系列:利用LLM重构数据科学流程07 - 工程化实践与挑战
  • 计算机网络基础(三) --- TCP/IP网络结构(运输层)
  • 实时操作系统FreeRTOS移植到STM32VGT6
  • Axure RP 9的安装
  • 2025年渗透测试面试题总结-31(题目+回答)
  • leetcode 1504. 统计全 1 子矩形 中等
  • `malloc` 内存分配函数
  • fastdds:topic instance
  • 【嵌入式】【搜集】状态机、状态迁移图及状态模式材料
  • 【线性代数】常见矩阵类型
  • 【Nginx系列】查看 Nginx 的日志
  • Building Systems with the ChatGPT API 使用 ChatGPT API 搭建系统(第八章学习笔记及总结)
  • Hibernate详解
  • GaussDB 数据库架构师修炼(十八) SQL引擎-分布式计划
  • 保姆级Maven安装与配置教程(Windows版)
  • SpringCloud Alibaba核心知识点
  • MIT 6.5840 (Spring, 2024) 通关指南——入门篇
  • 项目学习总结(4)