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

C语言基础开发入门系列(八)C语言指针的理解与实战

文章目录

  • 前言
  • 一、C语言指针介绍
    • 1.指针的声明
    • 2.指针的初始化
    • 4.指针数据的访问
    • 5.指针的运算
    • 6.函数指针
    • 7.通过数组求累加和学习C语言指针编程
    • 8.指针常见错误和注意事项
  • 总结

前言

这章我们主要介绍C语言中指针的概念。C语言指针是C语言中一种非常重要的数据类型,它存储的是内存地址。通过指针,我们可以直接操作内存,这使得C语言在系统编程、硬件操作等方面非常强大。

在这里插入图片描述

一、C语言指针介绍

指针是一个变量,其值为另一个变量的内存地址,简单来说,指针就是地址的变量化。指针是C语言中一个非常重要且强大的特性,它允许直接操作内存地址。理解指针是掌握C语言的关键。下面将从指针的基本概念、声明、初始化、运算、指针与数组等方面进行详细讲解。

在这里插入图片描述

1.指针的声明

指针在使用前,必须先声明,指针的声明需要指定指针所指向的数据类型,指针的定义格式如下:
*数据类型 指针变量名;
C语言指针的声明写法如下:

int *p;      // 指向整型的指针
char *c;     // 指向字符型的指针
float *f;    // 指向浮点型的指针

2.指针的初始化

指针变量在使用之前必须初始化,否则它可能指向一个随机的内存地址,导致未定义行为。指针的初始化通常是取另一个变量的地址。示例如下:

int var = 10;//定义整型变量
int *p;//申明指针
p= &var;   // p 指向变量 var 的地址,&是取地址

4.指针数据的访问

通过解引用运算符:*,实现指针访问或修改其指向的内存地址中存储的值。
指针访问举例:

int var = 10;
int *p = &var;
printf("%d", *p);   // 输出 10
*p = 20;            // 通过指针修改变量 var 的值
printf("%d", var);  // 输出 20

5.指针的运算

指针支持有限的算术运算:递增、递减、加整数、减整数和指针相减。在C语言中,数组名在大多数情况下表示数组首元素的地址。

int arr[] = {10, 20, 30};
int *p = arr;   // p 指向数组的首元素地址
printf("%d", *p);   // 输出 10
p++;                // 指向下一个整型元素,即 arr[1]
printf("%d", *p);   // 输出 20
int arr[] = {10, 20, 30, 40};
int *p = arr;     // 等价于 int *p;p = &arr[0];
p = p + 2;        // 现在 p 指向 arr[2]
printf("%d", *p); // 输出 30
int arr[] = {1, 2, 3};
int *p = arr;   // 等价于 int *p;p = &arr[0];
// 通过指针访问数组元素
*(p + 1) = 10;  // 等价于 arr[1] = 10;

通过指针,可以在函数内部修改外部变量的值。

//实现a,b两个地址的数值交换
void swap(int *a, int *b) 
{int temp = *a;//temp为a指针的内容*a = *b;//a指针指向的内容改成b指针指向的值*b = temp;//b指针指向的值改成temp的内容
}int main(void) 
{int x = 10, y = 20;swap(&x, &y);//交换x,y的值printf("x=%d, y=%d", x, y); // 输出 x=20, y=10return 0;
}

6.函数指针

函数指针是指向函数的指针变量,它存储函数的地址,通过这个指针可以调用相应的函数。基本语法返回类型 (*指针变量名)(参数列表);。


#include <stdio.h>
int add(int a, int b) //定义两个参数相加的函数
{return a + b;
}
int (*operation)(int, int);  // 声明函数指针
int main(void) 
{operation = add; // 指针指向add函数的地址printf("10 + 5 = %d\n", operation(10, 5));//执行函数指针调用,实现两个数值相加return 0;
}

7.通过数组求累加和学习C语言指针编程

这个简单例程完整展示了一维数组指针的核心概念,是理解C语言指针基础的最佳起点。通过这个例子,你可以清楚地看到指针如何与数组交互,以及如何使用指针来操作数组数据。

#include <stdio.h>
#define ITM_PORT8(n)         (*(volatile unsigned char *)(0xe0000000 + 4*(n)))//Keil5环境调用printf打印需要的定义
int fputc(int ch, FILE *f)//Keil5环境调用printf打印需要的接口函数
{ITM_PORT8(0) = ch;return ch;
}// 计算数组元素的和,返回的累加和就是1字节的数据,如果超出1字节,依旧是取1字节的数据
unsigned char calculate_sum(unsigned char*arr, unsigned char size) 
{unsigned char sum = 0;for (unsigned char i = 0; i < size; i++) {sum += *(arr + i); // 使用指针访问元素}return sum;
}unsigned char numbers[] = {2,5,8,3,9,1,7,10,15,20};// 定义数组,数组内容是10字节的数据
unsigned char *ptr;//声明指针
int main(void) 
{unsigned char size = sizeof(numbers) / sizeof(numbers[0]);//定义变量size,size是计算数组中元素的个数printf("指针赋值前,指针指向的地址 %d\n", ptr);ptr = numbers;//初始化指针指向数组首地址	printf("指针赋值后指向地址0x%x\n", ptr);printf("数组numbers起始地址0x%x\n", &numbers[0]);	printf("数组numbers内容:\n");//准备打印数组内容for (int i = 0; i < size; i++) {printf("%d ", *(ptr + i));//*(ptr + i) 表示取出指针ptr向后移动i个元素的该位置的值,这个循环就是打印数组内每个元素内容}printf("\n");//打印换行符for (int i = 0; i < size; i++) {*(ptr + i)=i;//修改数组里的每个元素值,每个元素值是跟i相关,第i个,数值就是i}printf("修改后的数组numbers内容:\n");//准备打印数组内容for (int i = 0; i < size; i++) {printf("%d ", *(ptr + i));//*(ptr + i) 表示取出指针ptr向后移动i个元素的该位置的值,这个循环就是打印数组内每个元素内容}printf("\n");//打印换行符unsigned char sum = calculate_sum(numbers, size);//定义变量sum,调用函数计算数据累加和,函数的形参是指针,所以实参numbers需要是地址值printf("修改后的数组元素累加和: %d\n", sum);return 0;
}

调试上面的代码,先了解指针声明后指向的地址与赋值后的指向地址。指针声明后指向的地址是0,指针赋值后指向的地址是0x20000008,数组numbers的起始地址也是0x20000008。ptr = numbers;这句代码就是让指针ptr指向数组的起始地址。
在这里插入图片描述

如下图所示,我们查看内存空间,数组numbers的起始地址是0x20000008,也就是numbers[0]的地址,然后数组内容如下图顺序存储,跟定义的内容一致。
在这里插入图片描述
如下图所示,程序运行完打印指针指向的数组的内容,打印的内容与数组定义的数据一致,所以通过指针读取内容正确。
在这里插入图片描述
如下图所示,程序运行完打印修改后的数组内容,打印的内容与修改后的数据一致,所以通过指针对数组的内容进行修改正确。

在这里插入图片描述

如下图所示,程序运行完打印累加的计算结果内容,打印的内容与数组内所有数值相加的值一致,说明calculate_sum函数调用的没问题,实参传递也没问题。在这里插入图片描述

8.指针常见错误和注意事项

指针是C语言中最强大但也最容易出错的地方,未初始化的指针,数组越界访问等问题。

// 1. 未初始化的指针
int *p;
*p = 10;  // 错误!// 2. 空指针引用
int *p = NULL;
*p = 10;  // 错误!//数组越界访问
void array_bounds_demo() 
{int arr[5] = {1, 2, 3, 4, 5};int *ptr = arr;// 越界访问for (int i = 0; i <= 5; i++) {  // 应该是 i < 5printf("%d ", *(ptr+i));  // ptr[5] 越界!,数组总共只有5个元素,*(ptr+5)是第6个元素,越界了}
}

总结

多写代码,多画内存图,在纸上画出变量、指针和它们地址之间的关系,是理解指针最有效的方法。从简单的例子开始,逐步尝试更复杂的应用,如动态数组、字符串处理、函数指针和链表等。

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

相关文章:

  • 如何绑定网站域名解析电商网站模板html
  • 【机器视觉通用检测框架】基于VS2019 C#+VisionPro9.0开发的视觉框架软件,全套源码,开箱即用
  • 智慧交通管理新范式 基于深度学习的城市交通车型识别AI监控系统 车型识别 停车场车型分类系统 高速路车型识别算法
  • pnpm 安装依赖后 仍然启动报的问题
  • 【河北政务服务网-注册_登录安全分析报告】
  • 深入理解 package.json:前端项目的 “身份证“
  • 【辽宁政务服务网-注册_登录安全分析报告】
  • 免费正能量励志网站建设网站要多久到账
  • 新乡市红旗区建设局网站网页制作素材网有哪些
  • 用车申请车辆管理小程序开发
  • 单片机.RS485
  • 从单模型到多域自由转换:StarGAN的公式与多域图像生成魔法
  • 人工智能在全球多领域的应用潜力及当前技术面临的挑战
  • Python处理json数据
  • go开发规范指引
  • 期权交易中的希腊字母:风险管理的多维指南
  • C++---关键字constexpr
  • 广州购物网站公司地址广州网站建设
  • 手术机器人多传感器数据融合 × 深度学习前沿研究精要(2024-2025)
  • 火山引擎升级AI云原生套件:AgentKit、ServingKit、TrainingKit全链路加速AI应用落地
  • Git命令速查手册
  • 随机链表的复制 (带random的链表深度拷贝)| C语言实现
  • 大仓库推到GitHub大踩坑-Git LFS从安装到使用
  • 宁夏制作网站公司网站仿静态和静态的区别
  • 【App开发】02:Android Studio项目环境设置
  • 初识MYSQL —— 复合查询
  • 有网站可以接设计的单子做吗招投标网站
  • 基于 WPS TOROW 函数实现 VLOOKUP:多行多列转一行
  • 编译SQLite 3.51源码并体验新功能
  • CMP(类Cloudera CDP 7.3 404版华为泰山Kunpeng)和Apache Doris的对比