C语言指针进阶——指针运算、数组与const(笔记续)
2026/7/29 3:48:58 网站建设 项目流程

一、指针的运算

指针本质上是一个地址值,但它的运算并不是简单的整数加减,而是以基类型大小为步长的。

1. 指针的加减运算

int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int *p = a; // p 指向 a[0]
表达式含义
p + 1向后偏移1个基类型大小(即跳过一个int),指向a[1]
p - N向前偏移 N 个基类型大小
p++指针后移一个基类型大小
p--指针前移一个基类型大小

关键:p + 1不是地址 +1 字节,而是地址 +sizeof(基类型)字节。比如int *p,则p + 1实际地址增加了 4 字节(假设int为 4 字节)。

2. 指针相减

int *p = &a[2]; int *q = &a[7]; int diff = p - q; // 结果为 -5
  • 前提:两个指针必须是同类型指针
  • 结果含义:两个地址之间差了几个基类型(而不是差几个字节)

3. 指针的关系运算

支持><>=<===!=

if (p > q) { // p 所指向的地址在 q 之后 }

二、指针操作整型一维数组

数组名就是首元素地址

int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  • a(数组名)⇔&a[0](首元素地址),两者等价
  • 首元素a[0]的数据类型是int,所以&a[0]的地址类型是int *
int *p = a; // 等价于 int *p = &a[0]; 说 p 指向了数组 a

用指针访问数组元素

表达式等价形式含义
*pa[0]首元素
*(p + 1)a[1]第二个元素
*(p + i)a[i]第 i+1 个元素

数组的下标运算,本质上就是指针运算。a[i]等价于*(a + i)

数组的特性与指针的关系

  1. 1.连续性—— 只需知道起始地址,就能通过p + i定位到任意元素
  2. 2.有序性—— 元素在内存中按顺序排列
  3. 3.单一性—— 每个元素类型相同,大小一致

三、指针实战练习

练习1:找出数组最大值

int findMax(int *p, int len) { int max = *p; for (int i = 1; i < len; i++) { if (*(p + i) > max) { max = *(p + i); } } return max; }

练习2:数组逆序

void reverse(int *p, int len) { int *left = p; int *right = p + len - 1; while (left < right) { int temp = *left; *left = *right; *right = temp; left++; right--; } }

练习3:选择排序(迭代方式)

核心思想:外层循环确定当前位置begin,内层循环从剩余元素中找到最小值,然后交换。

void selectSort(int *p, int len) { for (int i = 0; i < len - 1; i++) { int minIdx = i; for (int j = i + 1; j < len; j++) { if (*(p + j) < *(p + minIdx)) { minIdx = j; } } if (minIdx != i) { int temp = *(p + i); *(p + i) = *(p + minIdx); *(p + minIdx) = temp; } } }

选择排序(begin/end 指针风格)

void selectSortP(int *begin, int *end) { int *i = NULL, *j = NULL; for (i = begin; i < end; ++i) { int *min = i; for (j = i + 1; j <= end; ++j) { if (*j < *min) min = j; } if (min != i) swap(i, min); } }

冒泡排序(外层--end,内层++p

void bubbleSortP(int *begin, int *end) { int *p = NULL; for (; end > begin; --end) { for (p = begin; p < end; ++p) { if (*p > *(p + 1)) swap(p, p + 1); } } }

插入排序(外层++i,内层--j

void insertSortP(int *begin, int *end) { int *i = NULL; for (i = begin + 1; i <= end; ++i) { int temp = *i; int *j = i - 1; while (j >= begin && *j > temp) { *(j + 1) = *j; --j; } *(j + 1) = temp; } }

快速排序(--right找小值,++left找大值)

int *partitionP(int *begin, int *end) { int pivot = *begin; int *left = begin; int *right = end; while (left < right) { while (left < right && *right >= pivot) --right; *left = *right; while (left < right && *left <= pivot) ++left; *right = *left; } *left = pivot; return left; } void quickSortP(int *begin, int *end) { if (begin >= end) return; int *pivotPos = partitionP(begin, end); quickSortP(begin, pivotPos - 1); quickSortP(pivotPos + 1, end); }

练习4:二分查找(返回指针)

int *binaryFind(int *begin, int *end, int target) { while (begin <= end) { int *mid = begin + (end - begin) / 2; if (*mid == target) return mid; // 返回找到元素的地址 else if (*mid < target) begin = mid + 1; else end = mid - 1; } return NULL; // 没找到返回 NULL }

四、指针操作字符型一维数组(字符串)

字符串在 C 语言中本质是字符数组,同样可以用指针操作:

char s[] = "hello"; char *p = s; // p 指向 s[0],即 'h' *(p + i) // 访问第 i+1 个字符

标准库函数的典型签名:

int puts(const char *s); // 参数为 const char *

五、const 与指针

const和指针搭配时,位置不同,含义完全不同。核心原则:const离谁近,就限定谁。

四种组合

const char *s; // const 限定 *s(基类型)→ 不能通过 *s 修改所指向的内容 char const *s; // 同上,写法不同,效果相同 char *const s; // const 限定 s(指针变量本身)→ s 不能再指向其他地址 const char *const s; // 两者都限定 → 既不能改指向,也不能改内容

示例对比

const char *s = "hello"; // *s = 'c'; ✗ 编译报错,不能通过指针修改内容 s = "world"; ✓ 可以改变指向
char buf[] = "hello"; char *const s = buf; *s = 'c'; ✓ 可以修改内容 s = NULL; ✗ 编译报错,不能改变指向

为什么字符串函数推荐用const char *

char *strcpy(char *dest, const char *src); // ^^^^^

好处有两点:

  1. 1.提前发现逻辑错误—— 防止意外修改源字符串
  2. 2.参数适用性更强—— 可以接受:
    • 数组名(char s[] = "hello"
    • 字符串常量("hello"
    • const char *变量

在编写字符串相关函数时,形参能写成const char *的,尽可能写成const char *

小结

知识点核心要点
指针加减sizeof(基类型)为步长
指针相减同类型指针,结果为基类型个数的差
数组名等价于首元素地址&a[0]
a[i]本质是*(a + i),即指针运算
const char *不能通过*s修改内容,推荐用于函数形参
char *const ss 不能改指向,但可改内容

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询