C语言-指针用法概述

 目录

1.指针基础概念  

2. 指针与数组

3. 指针作为函数参数

4. 动态内存分配

 5. 指针的高级用法

6. 常见错误与注意事项

7. 指针数组 vs. 数组指针     

8.总结与建议


        本文主要作为指针用法的复习,会对指针的大致用法进行举例和概述。


1.指针基础概念  

  • 什么是指针​:指针是一种变量,它存储的是另一个变量的内存地址,而不是数据本身。这允许你通过地址间接访问和操作数据
  • 声明指针​:声明指针时,需要在数据类型后跟一个星号 *。例如 int *p;声明了一个指向整型的指针 p
  • 取地址与解引用​:
    • 使用取地址运算符 &可以获取变量的地址。例如,int a = 10; int *p = &a;使得指针 p指向变量 a的地址
    • 使用解引用运算符 *可以访问指针所指向地址中存储的值。例如,printf("%d", *p);会输出 a的值 10

2. 指针与数组

        C语言中数组名通常可被当作指向其首元素的指针使用。

int arr[3] = {1, 2, 3};
int *p = arr; // p 指向 arr[0]
printf("%d", *(p + 1)); // 输出 arr[1] 的值 2

        指针可以通过算术运算(如 +-++--)来遍历数组,单位是所指向类型的大小(例如 int指针每次移动通常为4字节)。


3. 指针作为函数参数

        通过向函数传递指针(即变量的地址),可以在函数内部修改调用函数中的变量值,实现“引用传递”。

void swap(int *a, int *b) {int temp = *a;*a = *b;*b = temp;
}int main() {int x = 1, y = 2;swap(&x, &y); // 交换后 x=2, y=1
}

        当需要向函数传递数组时,通常传递数组名(即首元素地址)和数组长度。使用 const限定符可以保护指针所指向的数据在函数内不被修改。


4. 动态内存分配

        C语言允许在程序运行时动态地申请和释放内存,这类内存分配在上进行。

  • malloc​:分配指定字节数的内存,​不初始化内存内容,返回 void*
int *p = (int*)malloc(5 * sizeof(int)); // 分配容纳5个整数的空间
if (p == NULL) { /* 处理分配失败 */ }
  • calloc​:分配指定数量、特定类型的内存空间,并初始化为0​。
int *p = (int*)calloc(5, sizeof(int)); // 分配并初始化5个整数为0
  • realloc​:调整之前通过 malloc, callocrealloc分配的内存块的大小。
  • free​:​必须用于释放之前动态分配的内存,否则会导致内存泄漏。释放后最好将指针置为 NULL,以避免“悬空指针”。
    free(p);
    p = NULL;

 5. 指针的高级用法

  • 多级指针​:指向指针的指针,例如 int **pp是指向 int*的指针,常用于动态二维数组或需要修改指针本身值的场景。

  • 函数指针​:指向函数的指针,允许动态调用函数,常用于回调机制。
int add(int a, int b) { return a + b; }
int (*funcPtr)(int, int) = add; // 声明并初始化函数指针
printf("%d", funcPtr(2, 3)); // 通过指针调用函数,输出5

•​const与指针​:

  • const int *pint const *p:指向常量数据的指针,​不能通过 p修改所指数据,但可以改变 p的指向。
  • int *const p:指针本身是常量,​p的指向不能变,但可以通过 p修改所指数据。
  • const int *const p:指针本身和所指数据都不可变。

6. 常见错误与注意事项

  • 未初始化的指针(野指针)​​:声明指针后未赋予有效地址前就使用,可能导致程序崩溃。​务必初始化,例如设为 NULL
  • 悬空指针​:指针指向的内存已被释放,但指针仍在被使用。​释放内存后应将指针置为 NULL
  • 内存泄漏​:分配的内存未被释放且失去对其的引用,导致内存浪费。确保 ​malloc/callocfree成对出现
  • 越界访问​:访问了分配内存范围之外的空间,行为不可预知。​确保访问在合法范围内

7. 指针数组 vs. 数组指针     

对于我们新手来说特别难搞清楚他们的区别,理解它们的区别很重要:

类型

声明示例

含义

指针数组

int *arr[10]

一个数组,其每个元素都是指向 int的指针

数组指针

int (*arr)[10]

一个指针,它指向一个包含10个整数的数组

理解指针数组(Array of Pointers)和数组指针(Pointer to Array)的区别确实是C语言学习中的一个重点和难点。下面我将通过一个表格帮你快速梳理它们的核心差异,然后进行详细解释。

特性

指针数组 (Array of Pointers)

数组指针 (Pointer to Array)

本质

数组,其每个元素都是指针

指针,它指向整个数组

声明语法

数据类型 *数组名[数组长度];
(例如:int *ptr_arr[5];

数据类型 (*指针名)[数组长度];
(例如:int (*arr_ptr)[5];

内存布局

连续存储多个指针(每个指针占4或8字节)

存储一个指针变量(指向数组首地址),本身通常占4或8字节

步长

指针运算以单个指针的大小为单位(如+1移动4或8字节)

指针运算以整个数组的大小为单位(如+1移动sizeof(类型[长度])字节)

典型用途

管理多个字符串、存储动态分配的不同长度内存块的地址、命令行参数argv

操作多维数组(尤其是二维数组)、向函数传递固定长度的数组

深入理解两者

1. 指针数组(Array of Pointers)

指针数组的本质是一个数组,但这个数组里的每个元素都不是普通的数据,而是一个指针变量,这些指针可以指向相同或不同类型的地址。

  • 声明与初始化

    语法格式为:数据类型 *数组名[数组长度];

    #include <stdio.h>int main() {int a = 10, b = 20, c = 30;// 声明并初始化一个指针数组,元素是int指针int *ptr_arr[3] = {&a, &b, &c}; // 遍历指针数组,通过解引用访问所指的值for (int i = 0; i < 3; i++) {printf("ptr_arr[%d] = %d\n", i, *ptr_arr[i]);}return 0;
    }

    输出:

    ptr_arr[0] = 10
    ptr_arr[1] = 20
    ptr_arr[2] = 30
  • 常见应用场景

    • 管理多个字符串​:这是指针数组非常常见的用途,可以高效地处理一堆长度不一的字符串。

      char *str_arr[] = {"Hello", "World", "C", "Programming"}; // 初始化字符串指针数组
      for (int i = 0; i < 4; i++) {printf("%s ", str_arr[i]);
      }
      // 输出: Hello World C Programming
    • 动态内存管理​:当需要动态分配多个独立的内存块时,可以用指针数组来存储这些内存块的地址。

    • 命令行参数​:C语言中的main函数参数char *argv[]就是一个典型的指针数组,用于存储命令行输入的字符串参数。

2. 数组指针(Pointer to Array)

数组指针的本质是一个指针,但它不是指向单个变量,而是指向整个数组

  • 声明与初始化

    语法格式为:数据类型 (*指针名)[数组长度];注意括号是必须的,它保证了*先与指针名结合。

    #include <stdio.h>int main() {int arr[5] = {1, 2, 3, 4, 5};// 声明一个数组指针,指向包含5个int的数组int (*arr_ptr)[5] = &arr; // 取数组的地址赋给数组指针// 通过数组指针访问数组元素for (int i = 0; i < 5; i++) {printf("%d ", (*arr_ptr)[i]); // 先解引用arr_ptr得到数组,再通过下标访问}return 0;
    }

    输出:

    1 2 3 4 5
  • 常见应用场景

    • 处理二维数组​:数组指针在遍历和理解二维数组时特别有用,它可以表示二维数组中的一行。

      int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
      // 数组指针,指向一个包含3个int的数组(即一行)
      int (*row_ptr)[3] = matrix; // matrix是首行地址,可直接赋值for (int i = 0; i < 2; i++) {for (int j = 0; j < 3; j++) {printf("%d ", row_ptr[i][j]); // 像二维数组一样访问// 等价于 *(*(row_ptr + i) + j)}printf("\n");
      }

      输出:

      1 2 3 
      4 5 6
    • 向函数传递二维数组​:当函数需要接收一个二维数组时,使用数组指针可以明确列数。

 如何快速区分声明?

记住一点:​​“看括号和优先级”​

  • int *p[5];[]的优先级高于*,所以p先与[5]结合,说明p是一个数组,里面存放的是int*类型。这是指针数组

  • int (*p)[5];:括号()改变了优先级,*先与p结合,说明p是一个指针,它指向一个int [5]类型的数组。这是数组指针

注意事项

  1. 匹配类型和长度​:对于数组指针,其指向的数组类型和长度必须严格匹配。例如,int (*p)[5]只能指向包含5个整数的数组。

  2. 初始化​:使用指针数组时,务必确保其中的每个指针都指向有效的内存地址,避免野指针。

  3. 内存管理​:如果指针数组的元素指向动态分配的内存,记得在使用完毕后释放这些内存,防止内存泄漏。

 小总结

简单来说:

  • 指针数组​:是一个仓库(数组)​,里面放着很多把钥匙(指针)​,每把钥匙可以打开不同的房间。

  • 数组指针​:是一把特殊的钥匙(指针)​,这把钥匙对应着一整排连续的仓库(整个数组)​

希望这些解释和例子能帮助你清晰地区分指针数组和数组指针。


8.总结与建议

        指针是C语言的精髓,提供了直接操作内存的强大能力和灵活性,常用于动态内存管理、数组操作、函数参数传递以及构建复杂数据结构(如链表、树)。同时,指针的使用也伴随着风险,需要谨慎处理内存管理和避免常见陷阱。

最佳实践​:

  • 始终初始化指针,若暂时不知指向何处,可初始化为 NULL
  • 检查动态内存分配​(如 malloc, calloc)的返回值是否为 NULL,以防分配失败。
  • 确保分配的内存及时释放,并在释放后将指针置为 NULL​。
  • 使用 const限定符保护不应被修改的数据,增强代码健壮性。
  • 利用工具​(如 Valgrind)检查内存泄漏和非法访问。

希望以上梳理能帮助你更好地理解C语言中的指针。

请大家点点关注和点赞,后面我一定会分享更多实用的文章的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/96271.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/96271.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java调用Whisper和Vosk语音识别(ASR)模型,实现高效实时语音识别(附源码)

简介 语音识别&#xff08;Automatic Speech Recognition, ASR&#xff09;是将人类的语音信号自动转换为对应文字的技术&#xff0c;它使计算机能够“听懂”人说的话&#xff0c;是人机语音交互的核心技术&#xff0c;广泛应用于智能助手、语音输入、客服系统等场景。 现在我…

第3周 机器学习课堂记录

1.学习问题的分类有监督的学习分类回归无监督学习聚类密度估计&#xff1a;确定输入空间中的数据的分布可视化&#xff1a;把高位空间中的数据投影到二维或三维空间强化学习不给定最优输出的示例&#xff0c;而是通过试错发现最优输出2.泛化versus过度拟合背景引入&#xff1a;…

消息队列(MQ)高级特性深度剖析:详解RabbitMQ与Kafka

一、引言&#xff1a;为什么需要关注高级特性&#xff1f; 在现代分布式系统架构中&#xff0c;消息队列&#xff08;Message Queue&#xff09;已成为不可或缺的核心组件。初级使用消息队列可能只需几行代码就能实现基本功能&#xff0c;但要真正发挥其在大规模生产环境中的威…

【GPT入门】第65课 vllm指定其他卡运行的方法,解决单卡CUDA不足的问题

【GPT入门】第65课 vllm指定其他卡运行的方法&#xff0c;解决单卡CUDA不足的问题&#xff11;.原理说明&#xff1a;&#xff12;.实践&#xff11;.原理 要将 vllm 部署在第二张 GPU 卡上&#xff08;设备编号为 1&#xff09;&#xff0c;只需在命令前添加 CUDA_VISIBLE_DE…

Spring Boot Actuator自定义指标与监控实践指南

Spring Boot Actuator自定义指标与监控实践指南 本篇文章以生产环境实战经验为主线&#xff0c;结合某电商系统的业务场景&#xff0c;讲解如何在Spring Boot Actuator中添加并暴露自定义指标&#xff0c;并使用Prometheus和Grafana进行完整的监控与告警配置。 一、业务场景描述…

Vue报错<template v-for=“option in cardOptions“ :key=“option.value“>

在Vue项目中遇到报错&#xff0c;原因是模板中使用了<template>标签内的v-for指令&#xff0c;而当前Vue版本不支持此用法。解决方案是移除<template>标签&#xff0c;直接在<el-option>上使用v-for。同时优化计算属性cardOptions&#xff0c;使其能够兼容历…

人工智能学习:Transformer结构中的规范化层(层归一化)

Transformer结构中的规范化层(层归一化) 一、规范化层(层归一化)介绍 概念 层归一化(Layer Normalization) 是一种用于提高深度神经网络训练稳定性和加速收敛的技术,广泛应用于现代深度学习模型中,尤其是在Transformer等序列建模网络中。它通过对每一层的输出进行归一化…

盼之代售 最新版 decode__1174

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 逆向分析 部分python代码 cp1 execj…

Transformer系列 | Pytorch复现Transformer

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、Transformer和Seq2Seq 在之前的博客中我们学习了Seq2Seq(深度学习系列 | Seq2Seq端到端翻译模型)&#xff0c;知晓了Attention为RNN带来的优点。那么有没有…

【MySQL】常用SQL语句

介绍常用的DDL语句、DML语句基本语法分号结尾使用空格和缩进不区分大小写--或#注释单行内容 /*注释多行内容*/DDL数据定义语句&#xff1a;定义数据库、表、字段一、操作库-- 创建库create database db1;-- 创建库是否存在&#xff0c;不存在则创建create database if not exi…

云手机就是虚拟机吗?

云手机并非等同于虚拟机&#xff0c;尽管二者存在一定相似性&#xff0c;但有着诸多区别&#xff0c;以下从多个方面来分析&#xff1a;云手机是一种基于云计算技术&#xff0c;将云端服务器虚拟化为手机设备&#xff0c;用户能通过网络远程操控的虚拟手机服务&#xff0c;它从…

准确--Nginx 1.28.0 安装与配置流程

Nginx 1.28.0 安装与配置流程 1. 下载与解压 cd ~ wget http://nginx.org/download/nginx-1.28.0.tar.gz tar -zxvf nginx-1.28.0.tar.gz cd nginx-1.28.02. 配置编译参数 ./configure \--prefix/home/ynnewweb/nginx \--with-http_ssl_module \--with-http_gzip_static_module…

无标记点动捕新范式:Xsens系统助力人形机器人实现毫米级动作复刻

Xsen搭载Manus数据手套在机器人操作与机器学习中的应用当前&#xff0c;人形机器人正加速向工业装配、家庭陪护、仓储物流等场景渗透&#xff0c;而 “如何让机器人的动作既符合人类运动规律&#xff0c;又能实现高精度执行” 成为制约其落地的核心瓶颈。Xsens 高精度全身动捕系…

mysql57超管root忘记密码怎么办

目录 背景 1.首先停止数据库 2.使用免密模式启动 3.修改密码 3.1刷新权限配置 3.2修改密码 4.杀掉mysql 5.重新正常启动mysql 6.查看mysql状态 7.验证 7.1首先服务器本地验证 7.2远程验证 背景 数据库密码忘记了,急的抓耳挠腮,怎么也想不起来,于是就开始重置吧 1.…

RESTful API:@RequestParam与@PathVariable实战对比

RequestParam vs PathVariable 在删除和查找操作中的使用差异 在项目实战中&#xff0c;选择使用 RequestParam 还是 PathVariable 来接收ID参数&#xff0c;通常基于以下几个考虑因素&#xff1a; 1. RESTful API 设计原则 查找操作使用 PathVariable GetMapping("/depts…

剧本杀小程序系统开发:开启沉浸式社交娱乐新纪元

在当今数字化浪潮席卷的时代&#xff0c;社交娱乐方式正经历着前所未有的变革。剧本杀&#xff0c;这一融合了角色扮演、推理悬疑与社交互动的线下娱乐项目&#xff0c;近年来迅速风靡全国&#xff0c;成为年轻人热衷的社交新宠。而随着移动互联网的蓬勃发展&#xff0c;剧本杀…

中线安防保护器,也叫终端电气综合治理保护设备为现代生活筑起安全防线

中线安防保护器&#xff08;Neutral Line Protection Device&#xff0c;简称NLPD&#xff09;是一种专门用于监测和保护电力系统中性线的安全装置。中线安防保护器的基本原理为:通过电流检测环节采集系统中性线上过电流信息&#xff0c; 经控制器快速计算并提取各次谐波电流的…

Spring Cloud Alibaba快速入门02-Nacos配置中心(下)

文章目录前言配置中心 - 数据隔离示例1.先创建命名空间2.创建配置3.克隆配置4.动态切换环境5.yml多文档模式spring.profiles.activedevspring.profiles.activetest总结前言 上一章简单了解了Nacos配置中心的基本用法&#xff0c;这一章将开始Nacos配置中心的实战案例。 配置中…

基于结构光相移法的三维重建

基于结构光相移法的三维重建程序 1. 介绍 结构光相移法是一种常用的三维重建技术&#xff0c;通过投射条纹图案并捕捉其变形来计算物体的三维形状。相移法通过多次投射不同相位的条纹图案&#xff0c;利用相位信息来提取物体表面的深度信息。 2. MATLAB实现 2.1 生成条纹图案 首…

机器学习10——降维与度量学习(K近邻、多维缩放、主成分分析)

上一章&#xff1a;机器学习09——聚类 下一章&#xff1a;机器学习11——特征选择与稀疏学习 机器学习实战项目&#xff1a;【从 0 到 1 落地】机器学习实操项目目录&#xff1a;覆盖入门到进阶&#xff0c;大学生就业 / 竞赛必备 文章目录一、k近邻学习&#xff08;kNN&#…