Appearance
指针
指针是C语言最重要也是最具特色的概念之一。指针允许直接操作内存地址,是C语言强大功能的核心。本章将详细介绍指针的概念和使用方法。
指针基础
什么是指针
c
/*
* 指针的基本概念
*/
#include <stdio.h>
int main(void)
{
int num = 10;
/*
* 指针是一个变量,存储的是另一个变量的地址
* & 取地址运算符:获取变量的内存地址
* * 解引用运算符:获取指针指向的值
*/
/* 声明指针变量 */
int *ptr; /* 声明一个指向int类型的指针 */
/* 将num的地址赋给ptr */
ptr = # /* &num 获取num的地址 */
printf("num的值: %d\n", num);
printf("num的地址: %p\n", (void*)&num);
printf("ptr的值(存储的地址): %p\n", (void*)ptr);
printf("ptr指向的值: %d\n", *ptr); /* *ptr 解引用 */
/* 通过指针修改值 */
*ptr = 20;
printf("\n通过指针修改后:\n");
printf("num的值: %d\n", num);
/* 指针的大小 */
printf("\n指针的大小: %zu 字节\n", sizeof(ptr));
printf("int的大小: %zu 字节\n", sizeof(int));
return 0;
}指针的声明和初始化
c
/*
* 指针的声明和初始化
*/
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
/* 声明指针 */
int *p1; /* 方式1:*靠近变量名 */
int* p2; /* 方式2:*靠近类型 */
int * p3; /* 方式3:*在中间(不推荐) */
/* 注意:声明多个指针时,每个都需要* */
int *pa, *pb; /* pa和pb都是指针 */
int* pc, pd; /* pc是指针,pd是int变量! */
/* 初始化指针 */
int num = 10;
int *ptr1 = # /* 初始化为变量的地址 */
int *ptr2 = NULL; /* 初始化为NULL(推荐) */
int *ptr3 = 0; /* 等价于NULL */
/* 空指针 */
int *null_ptr = NULL;
printf("空指针的值: %p\n", (void*)null_ptr);
/* 使用前检查空指针 */
if (null_ptr != NULL) {
printf("值: %d\n", *null_ptr);
} else {
printf("指针为空\n");
}
/* 野指针(危险) */
int *wild_ptr; /* 未初始化,包含随机值 */
/* printf("%d", *wild_ptr); */ /* 未定义行为! */
/* 悬空指针 */
int *dangling_ptr;
{
int local = 20;
dangling_ptr = &local;
} /* local被销毁,ptr成为悬空指针 */
/* printf("%d", *dangling_ptr); */ /* 未定义行为! */
/* 正确做法:指针不用时设为NULL */
dangling_ptr = NULL;
return 0;
}指针的类型
c
/*
* 不同类型的指针
*/
#include <stdio.h>
int main(void)
{
/* 整型指针 */
int i = 10;
int *ip = &i;
printf("int指针: *ip = %d\n", *ip);
/* 字符指针 */
char c = 'A';
char *cp = &c;
printf("char指针: *cp = %c\n", *cp);
/* 浮点指针 */
float f = 3.14f;
float *fp = &f;
printf("float指针: *fp = %f\n", *fp);
/* 双精度指针 */
double d = 3.14159;
double *dp = &d;
printf("double指针: *dp = %lf\n", *dp);
/* void指针(通用指针) */
void *vp;
vp = &i;
printf("void指针: *(int*)vp = %d\n", *(int*)vp);
vp = &f;
printf("void指针: *(float*)vp = %f\n", *(float*)vp);
/* 指针的大小(同一平台上所有指针大小相同) */
printf("\n指针大小:\n");
printf("int*: %zu\n", sizeof(int*));
printf("char*: %zu\n", sizeof(char*));
printf("void*: %zu\n", sizeof(void*));
return 0;
}指针运算
指针的算术运算
c
/*
* 指针的算术运算
*/
#include <stdio.h>
int main(void)
{
int arr[] = {10, 20, 30, 40, 50};
int *ptr = arr; /* 指向数组首元素 */
printf("=== 指针算术运算 ===\n");
/* 指针加法 */
printf("ptr = %p, *ptr = %d\n", (void*)ptr, *ptr);
printf("ptr + 1 = %p, *(ptr + 1) = %d\n",
(void*)(ptr + 1), *(ptr + 1));
printf("ptr + 2 = %p, *(ptr + 2) = %d\n",
(void*)(ptr + 2), *(ptr + 2));
/* 指针自增 */
printf("\n指针自增:\n");
ptr++;
printf("ptr++后: *ptr = %d\n", *ptr);
ptr++;
printf("ptr++后: *ptr = %d\n", *ptr);
/* 指针减法 */
printf("\n指针减法:\n");
ptr--;
printf("ptr--后: *ptr = %d\n", *ptr);
/* 指针差值 */
int *p1 = arr;
int *p2 = arr + 3;
printf("\np2 - p1 = %ld (元素个数)\n", (long)(p2 - p1));
/* 使用指针遍历数组 */
printf("\n使用指针遍历数组:\n");
ptr = arr;
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d (地址: %p)\n",
i, *(ptr + i), (void*)(ptr + i));
}
/* 指针和下标 */
printf("\n指针使用下标:\n");
ptr = arr;
for (int i = 0; i < 5; i++) {
printf("ptr[%d] = %d\n", i, ptr[i]);
}
return 0;
}指针与数组
c
/*
* 指针与数组的关系
*/
#include <stdio.h>
int main(void)
{
int arr[5] = {1, 2, 3, 4, 5};
/* 数组名是数组首元素的地址 */
int *ptr1 = arr; /* 等价于 &arr[0] */
int *ptr2 = &arr[0];
printf("arr = %p\n", (void*)arr);
printf("&arr[0] = %p\n", (void*)&arr[0]);
printf("ptr1 = %p\n", (void*)ptr1);
printf("ptr2 = %p\n", (void*)ptr2);
/* 数组名和指针的区别 */
printf("\nsizeof(arr) = %zu\n", sizeof(arr)); /* 整个数组的大小 */
printf("sizeof(ptr1) = %zu\n", sizeof(ptr1)); /* 指针的大小 */
/* arr是常量,不能修改 */
/* arr++; */ /* 错误! */
ptr1++; /* 正确 */
/* 使用指针访问数组 */
printf("\n使用指针访问数组:\n");
int *p = arr;
for (int i = 0; i < 5; i++) {
printf("%d ", *(p + i)); /* 或 p[i] */
}
printf("\n");
/* 指向数组的指针 vs 指针数组 */
/* 指向数组的指针 */
int (*ptr_to_arr)[5] = &arr; /* 指向整个数组的指针 */
printf("\n(*ptr_to_arr)[0] = %d\n", (*ptr_to_arr)[0]);
/* 指针数组 */
int a = 1, b = 2, c = 3;
int *ptr_array[3] = {&a, &b, &c}; /* 存储指针的数组 */
printf("ptr_array[0]指向的值: %d\n", *ptr_array[0]);
return 0;
}指针与函数
指针作为函数参数
c
/*
* 指针作为函数参数
*/
#include <stdio.h>
/* 交换两个数(使用指针) */
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
/* 通过指针返回多个值 */
void minmax(int arr[], int len, int *min, int *max)
{
*min = arr[0];
*max = arr[0];
for (int i = 1; i < len; i++) {
if (arr[i] < *min) *min = arr[i];
if (arr[i] > *max) *max = arr[i];
}
}
/* 修改数组元素 */
void double_elements(int *arr, int len)
{
for (int i = 0; i < len; i++) {
arr[i] *= 2; /* 或 *(arr + i) *= 2; */
}
}
int main(void)
{
/* 交换两个数 */
int x = 5, y = 10;
printf("交换前: x = %d, y = %d\n", x, y);
swap(&x, &y);
printf("交换后: x = %d, y = %d\n", x, y);
/* 返回多个值 */
int arr[] = {3, 7, 2, 9, 5};
int min, max;
minmax(arr, 5, &min, &max);
printf("\n最小值: %d, 最大值: %d\n", min, max);
/* 修改数组 */
int nums[] = {1, 2, 3, 4, 5};
printf("\n原数组: ");
for (int i = 0; i < 5; i++) printf("%d ", nums[i]);
double_elements(nums, 5);
printf("\n翻倍后: ");
for (int i = 0; i < 5; i++) printf("%d ", nums[i]);
printf("\n");
return 0;
}指针作为返回值
c
/*
* 指针作为返回值
*/
#include <stdio.h>
#include <stdlib.h>
/* 返回较大数的地址 */
int *max(int *a, int *b)
{
return (*a > *b) ? a : b;
}
/* 在数组中查找元素 */
int *find(int arr[], int len, int target)
{
for (int i = 0; i < len; i++) {
if (arr[i] == target) {
return &arr[i]; /* 返回元素的地址 */
}
}
return NULL; /* 未找到 */
}
/* 动态分配内存并返回指针 */
int *create_array(int len)
{
int *arr = (int*)malloc(len * sizeof(int));
if (arr != NULL) {
for (int i = 0; i < len; i++) {
arr[i] = i + 1;
}
}
return arr;
}
/* 错误示例:返回局部变量的地址 */
int *wrong_function(void)
{
int local = 10;
/* return &local; */ /* 错误!local在函数返回后销毁 */
/* 正确做法:使用static或动态分配 */
static int static_local = 10;
return &static_local;
}
int main(void)
{
/* 返回较大数的地址 */
int a = 10, b = 20;
int *p = max(&a, &b);
printf("较大的数: %d\n", *p);
/* 查找元素 */
int arr[] = {3, 7, 2, 9, 5};
int *found = find(arr, 5, 9);
if (found != NULL) {
printf("找到元素: %d (地址: %p)\n", *found, (void*)found);
}
/* 动态创建数组 */
int *dynamic_arr = create_array(5);
if (dynamic_arr != NULL) {
printf("动态数组: ");
for (int i = 0; i < 5; i++) {
printf("%d ", dynamic_arr[i]);
}
printf("\n");
free(dynamic_arr); /* 释放内存 */
}
return 0;
}指针与const
c
/*
* 指针与const
*/
#include <stdio.h>
int main(void)
{
int a = 10;
int b = 20;
/* 1. 指向常量的指针(指针指向的内容不可修改) */
const int *p1 = &a;
/* *p1 = 100; */ /* 错误!不能修改指向的值 */
p1 = &b; /* 正确!可以修改指针本身 */
printf("const int *p1: *p1 = %d\n", *p1);
/* 2. 常量指针(指针本身不可修改) */
int * const p2 = &a;
*p2 = 100; /* 正确!可以修改指向的值 */
/* p2 = &b; */ /* 错误!不能修改指针本身 */
printf("int * const p2: *p2 = %d\n", *p2);
/* 3. 指向常量的常量指针 */
const int * const p3 = &a;
/* *p3 = 200; */ /* 错误!不能修改指向的值 */
/* p3 = &b; */ /* 错误!不能修改指针本身 */
printf("const int * const p3: *p3 = %d\n", *p3);
/* 记忆技巧:从右往左读 */
/* const int *p1; -> p1 is a pointer to const int */
/* int * const p2; -> p2 is a const pointer to int */
/* const int * const p3; -> p3 is a const pointer to const int */
return 0;
}指针数组
c
/*
* 指针数组
*/
#include <stdio.h>
int main(void)
{
/* 指针数组:存储指针的数组 */
int a = 10, b = 20, c = 30;
int *ptrs[3] = {&a, &b, &c};
printf("指针数组:\n");
for (int i = 0; i < 3; i++) {
printf("ptrs[%d] = %p, *ptrs[%d] = %d\n",
i, (void*)ptrs[i], i, *ptrs[i]);
}
/* 字符串数组(常用) */
char *names[] = {"张三", "李四", "王五"};
int count = sizeof(names) / sizeof(names[0]);
printf("\n字符串数组:\n");
for (int i = 0; i < count; i++) {
printf("names[%d] = %s\n", i, names[i]);
}
/* 数组指针 vs 指针数组 */
/* int *p[3]; 指针数组:3个int指针 */
/* int (*p)[3]; 数组指针:指向包含3个int的数组 */
int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*p_arr)[3] = arr; /* 指向二维数组的行 */
printf("\n使用数组指针遍历二维数组:\n");
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", p_arr[i][j]);
}
printf("\n");
}
return 0;
}小结
本章学习了:
- 指针基础:指针的概念、声明、初始化
- 指针运算:加减运算、指针与数组
- 指针与函数:指针参数、指针返回值
- const与指针:指向常量的指针、常量指针
- 指针数组:存储指针的数组
下一章,我们将学习字符串,了解C语言字符串的处理方法。
