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个元素,越界了}
}
总结
多写代码,多画内存图,在纸上画出变量、指针和它们地址之间的关系,是理解指针最有效的方法。从简单的例子开始,逐步尝试更复杂的应用,如动态数组、字符串处理、函数指针和链表等。
