一、指针数组 (Array of Pointers)
一维数组
cpp
int main()
{
int arr[5] = { 1, 2, 3, 4, 5 };
int (*ptr)[5] = &arr;
int* p = arr;
// 访问元素方式
printf("%d\n", p[2]); //输出3
printf("%d\n", (*ptr)[2]); // 输出3
printf("%d\n", ptr[0][2]); // 同上,输出3
/*
p 是一个 int* 类型的指针,指向数组的第一个元素
p[2] 等价于 *(p + 2),通过指针算术访问第三个元素
这是最常用的数组访问方式
ptr 是一个指向整个数组的指针,类型是 int(*)[5]
*ptr 解引用得到数组本身(arr)
(*ptr)[2] 等价于 arr[2]
ptr[0] 等价于 *(ptr + 0),即解引用得到数组本身
ptr[0][2] 相当于先取数组,再取数组的第三个元素
这种写法暗示 ptr 可以看作是指向二维数组的行指针
*/
return 0;
}
二维数组
cpp
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 数组指针指向包含4个int的数组
int (*ptr)[4] = matrix;
// 访问元素
printf("%d\n", ptr[1][2]); // 输出7
printf("%d\n", (*(ptr + 1))[2]); // 同上,输出7
sizeof
操作符与指针、数组的关系 (64位下)
cpp
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;
printf("sizeof(arr): %zu\n", sizeof(arr)); // 数组总大小
printf("sizeof(ptr): %zu\n", sizeof(ptr)); // 指针大小
printf("sizeof(&arr): %zu\n", sizeof(&arr)); // 指针大小
printf("sizeof(*ptr): %zu\n", sizeof(*ptr)); // int大小
printf("sizeof(arr[0]): %zu\n", sizeof(arr[0])); // 单个元素大小
return 0;
}
cpp
sizeof(arr): 20 // 5个int × 4字节 = 20字节
sizeof(ptr): 8 // 指针在64位系统占8字节
sizeof(&arr): 8 // 数组指针也是指针
sizeof(*ptr): 4 // int类型大小
sizeof(arr[0]): 4 // int类型大小
二、函数指针
返回值类型 (*指针变量名)(参数类型列表);
cpp
int add(int a, int b)
{
return a + b;
}
int main()
{
//返回值类型 (*指针变量名)(参数类型列表);
int (*funcPtr)(int, int); // 定义一个指向函数的指针,该函数接受两个int参数并返回int
funcPtr = add; // 或者 = &add
int result = funcPtr(3, 4); // 等同于 add(3, 4)
// 或者
result = (*funcPtr)(3, 4); // 显式解引用
printf("%d", result);//7
return 0;
}
函数指针数组
cpp
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int main() {
int (*operations[3])(int, int) = {add, sub, mul};
for (int i = 0; i < 3; i++) {
printf("%d\n", operations[i](10, 5));
}
// 输出:
// 15 (10 + 5)
// 5 (10 - 5)
// 50 (10 * 5)
return 0;
}
高级用法
cpp
typedef int (*MathFunc)(int, int);//将int(*)(int,int) 简化为 MathFunc类型
int add(int a, int b) { return a + b; }
int main()
{
MathFunc func = add;//此时func就是int(*)(int,int)型指针
printf("%d\n", func(2, 3)); // 输出5
return 0;
}
回调函数
cpp
void processArray(int *arr, int size, int (*callback)(int)) {
for (int i = 0; i < size; i++) {
arr[i] = callback(arr[i]);
}
}
int square(int x) { return x * x; }
int increment(int x) { return x + 1; }
int main() {
int arr[] = {1, 2, 3, 4, 5};
processArray(arr, 5, square);
// arr现在是 [1, 4, 9, 16, 25]
processArray(arr, 5, increment);
// arr现在是 [2, 5, 10, 17, 26]
return 0;
}
C++中的函数指针
C++中还可以使用函数对象和lambda表达式,但函数指针仍然有效:
cpp
#include <iostream>
using namespace std;
int add(int a, int b) { return a + b; }
int main() {
int (*func)(int, int) = add;
cout << func(3, 4) << endl; // 输出7
// 使用auto更简洁
auto func2 = add;
cout << func2(5, 6) << endl; // 输出11
return 0;
}
注意事项
-
函数指针的类型必须与所指向的函数完全匹配(返回值类型和参数类型)
-
不要对函数指针进行不安全的类型转换
-
空函数指针(nullptr)需要先检查再调用
-
成员函数指针与普通函数指针不同(在C++中)
三、const 和 指针
const
关键字与指针结合使用时,会产生多种不同的含义,这取决于 const
修饰的是指针本身还是指针所指向的数据。理解这些区别对于编写安全、可靠的C/C++代码至关重要。
1. 指向常量数据的指针 (Pointer to constant data)
cpp
const int *ptr; // 或 int const *ptr;
cpp
const int x = 10;
const int *ptr = &x;
// *ptr = 20; // 错误:不能修改常量数据
int y = 30;
ptr = &y; // 合法:可以改变指针指向
-
特点:指针可以修改(指向不同的地址),但不能通过指针修改所指向的数据
-
用途:保护数据不被意外修改
2. 常量指针 (Constant pointer)
cpp
int *const ptr;
cpp
int x = 10;
int *const ptr = &x;
*ptr = 20; // 合法:可以修改指向的数据
// ptr = &y; // 错误:不能改变指针本身
-
特点:指针本身是常量(不能指向其他地址),但可以通过指针修改所指向的数据
-
用途:确保指针始终指向特定内存位置
- 指向常量数据的常量指针 (Constant pointer to constant data)
cpp
const int *const ptr;
cpp
const int x = 10;
const int *const ptr = &x;
// *ptr = 20; // 错误:不能修改数据
// ptr = &y; // 错误:不能修改指针
-
特点:指针不能修改,也不能通过指针修改数据
-
用途:完全保护数据和指针
四、库函数的实现
1、memcpy 实现
memcpy
用于从源内存地址复制n个字节到目标内存地址,不处理内存重叠情况。
cpp
void* my_memcpy(void* dest, const void* src, size_t n) {
if (dest == NULL || src == NULL || n == 0) {
return dest;
}
char* d = (char*)dest;
const char* s = (const char*)src;
// 逐字节复制
for (size_t i = 0; i < n; i++) {
d[i] = s[i];
}
return dest;
}
特点:
-
不检查内存重叠
-
简单高效的逐字节复制
-
返回目标指针以便链式调用
2. memmove 实现
memmove
也用于内存复制,但会正确处理源和目标内存重叠的情况。
cpp
void* my_memmove(void* dest, const void* src, size_t n) {
if (dest == NULL || src == NULL || n == 0) {
return dest;
}
char* d = (char*)dest;
const char* s = (const char*)src;
// 判断是否有重叠
if (d > s && d < s + n) {
// 从后向前复制以避免覆盖
for (size_t i = n; i > 0; i--) {
d[i-1] = s[i-1];
}
} else {
// 无重叠或源在前,正常复制
for (size_t i = 0; i < n; i++) {
d[i] = s[i];
}
}
return dest;
}
特点:
-
检测内存重叠情况
-
重叠时从后向前复制
-
非重叠时从前向后复制
-
同样返回目标指针
3. strstr 实现
cpp
char* my_strstr(const char* haystack, const char* needle) {
if (haystack == NULL || needle == NULL || *needle == '\0') {
return (char*)haystack;
}
for (; *haystack != '\0'; haystack++) {
const char* h = haystack;
const char* n = needle;
while (*h != '\0' && *n != '\0' && *h == *n) {
h++;
n++;
}
if (*n == '\0') {
return (char*)haystack;
}
}
return NULL;
}
特点:
-
处理空指针和空子串的特殊情况
-
外层循环遍历主字符串
-
内层循环比较子串
-
返回首次匹配的位置指针或NULL