【C语言】-指针01

1、指针概念

指针(pointer)是一个值为内存地址变量(或数据对象)。

1.1、指针的声明

c 复制代码
int* pi;	/* pi是指向int类型变量的指针,注意未经初始化的指针不准使用 */

声明指针的时候必须指定指针指向变量的类型,因为不同的变量类型占用不同的存储空间,此外一些指针操作要求知道操作对象的大小。

声明的意思pi是一个指针,*pi是int类型。

1.2、与指针相关的运算符

地址运算符(&) :后跟一个变量名时,&给出该变量的地址。
间接运算符/解引运算符(*):后跟一个指针名或地址时,8给出储存在指针指向地址上的值。

1.3、指针操作

对指针可以进行以下常见的操作。

  • 赋值:可以把地址赋值给指针。但地址应该与指针类型兼容。
  • 解引用:*运算符给出指针指向地址上存储的值。
  • 取址:&运算符给出的是指针本身的地址。(指针也有自己的地址和值)
  • 指针和整数相加:整数和指针都可以做第一个运算对象,指的是整数与指针所指向类型的大小(以字节为单位)相乘,然后把结果与初始地址相加。要注意越界情况,保证指针有效。
  • 递增指针:用于指向数组元素,递增指向数组元素的指针可以让该指针移动至数组的下一个元素。
  • 指针减去一个整数:指针必须是第一个运算对象,整数是第二个运算对象。该整数将乘以指针指向类型的大小,然后初始地址减去乘积。要注意越界情况,保证指针有效。
  • 递减指针,用于指向数据元素,递减指向数组元素的指针可以让该指针移动至数组的上一个元素。
  • 指针求差:可以计算两个指针的差值。比如,当两个指针指向同一个数组中不同元素时,可以由此求出两个元素之间的距离。
  • 指针比较:当两个指针指向同类型的对象时,可以使用关系运算符比较两个指针的数值。
c 复制代码
#include <stdio.h>

int main() {
    // 初始化数组和指针
    int arr[5] = {10, 20, 30, 40, 50};
    int* ptr1 = &arr[0];  // 指针ptr1指向arr[0]
    int* ptr2 = &arr[3];  // 指针ptr2指向arr[3]

    // 1. 赋值操作
    int x = 100;
    int* ptr3 = &x;       // 指针ptr3指向变量x
    printf("ptr3的值: %p\n", (void*)ptr3);  // 输出ptr3存储的地址(类型转换,跨平台兼容需要)

    // 2. 解引用操作
    printf("ptr1解引用: %d\n", *ptr1);      // 输出: 10
    *ptr1 = 100;                             // 修改ptr1指向的值
    printf("修改后ptr1解引用: %d\n", *ptr1); // 输出: 100

    // 3. 取址操作
    printf("ptr1的地址: %p\n", (void*)&ptr1); // 输出ptr1自身的地址

    // 4. 指针和整数相加
    int* ptr_add = ptr1 + 2;                // 指针后移2个元素
    printf("ptr1 + 2: %d\n", *ptr_add);     // 输出: 30 (原arr[2])

    // 5. 递增指针
    ptr1++;                                 // 指针后移1个元素
    printf("ptr1递增后: %d\n", *ptr1);      // 输出: 20

    // 6. 指针减去一个整数
    int* ptr_sub = ptr2 - 1;                // 指针前移1个元素
    printf("ptr2 - 1: %d\n", *ptr_sub);     // 输出: 30

    // 7. 递减指针
    ptr2--;                                 // 指针前移1个元素
    printf("ptr2递减后: %d\n", *ptr2);      // 输出: 30

    // 8. 指针求差
    ptr1 = &arr[0];                         // 重置ptr1指向arr[0]
    ptr2 = &arr[4];                         // 重置ptr2指向arr[4]
    ptrdiff_t diff = ptr2 - ptr1;           // 计算指针差值
    printf("ptr2 - ptr1 = %td\n", diff);    // 输出: 4 (元素间距)

    // 9. 指针比较
    printf("ptr1 < ptr2: %d\n", ptr1 < ptr2); // 输出: 1 (真)

    return 0;
}
//输出
ptr3的值: 0x7ffee...        // 实际地址会不同
ptr1解引用: 10
修改后ptr1解引用: 100
ptr1的地址: 0x7ffee...      // 实际地址会不同
ptr1 + 2: 30
ptr1递增后: 20
ptr2 - 1: 30
ptr2递减后: 30
ptr2 - ptr1 = 4
ptr1 < ptr2: 1

2、常见使用场景

2.1、使用指针在函数间通信

使用和*运算符,函数可以访问存储在对应位置的数值,并改变它们,即修改实参。

c 复制代码
/* 使用指针交换数值 */
void interchange(int* u,int* v){
	int temp;
	temp = *u;
	*u = *v;
	*v = temp;
}

2.2、动态内存分配

通过malloc、calloc、realloc分配内存,返回指针。

c 复制代码
int* arr = (int*)malloc(5 * sizeof(int));  // 分配5个int的空间
if (arr != NULL) {
    for (int i = 0; i < 5; i++) {
        arr[i] = i + 1;  // 通过指针访问数组元素
    }
    free(arr);  // 释放内存
}

2.3、指针与字符串、数组

数组名可以隐式转换成指针,但不等于指针变量

c 复制代码
char* str = "Hello";  // 字符串字面量存储在常量区,str指向其首地址
printf("%c\n", *(str + 2));  // 输出:'l'(等价于str[2])

// 指针遍历字符串
while (*str != '\0') {
    printf("%c ", *str);
    str++;
}

2.4、指针与数据结构

指针是构造复杂数据接口的基础

c 复制代码
// 链表节点定义
struct Node {
    int data;
    struct Node* next;  // 指向下一个节点的指针
};

// 创建节点
struct Node* createNode(int data) {
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = data;
    newNode->next = NULL;
    return newNode;
}

2.5、函数指针

将函数作为参数传递(回调函数)

c 复制代码
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }

// 函数指针作为参数
int calculate(int (*func)(int, int), int a, int b) {
    return func(a, b);
}

int main() {
    int result = calculate(add, 5, 3);  // 等价于add(5, 3)
    printf("%d\n", result);  // 输出:8
    return 0;
}

2.6、指针访问硬件地址

直接操作内存控制寄存器

c 复制代码
// 访问GPIO寄存器(示例)
volatile unsigned int* gpio_output = (unsigned int*)0x40020014;
*gpio_output = 0xFF;  // 向指定地址写入数据

3、使用注意事项

3.1、空指针(Null Pointer)

未初始化的指针可能指向随机地址,使用前必须赋值。

c 复制代码
int* ptr = NULL;  // 初始化为空指针
if (ptr != NULL) {
    *ptr = 10;  // 错误:空指针解引用
}

正确的使用方法。

c 复制代码
#include <stdio.h>
#include <stdlib.h>  // 包含malloc和free的头文件

int main() {
    int* ptr = NULL;  // 初始化为空指针(好习惯)
    
    // 方法1:指向已存在的变量
    int num = 5;
    ptr = &num;       // 指向变量num的地址
    if (ptr != NULL) {
        *ptr = 10;    // 正确:ptr指向有效内存
    }
    printf("num = %d\n", num);  // 输出:num = 10

    // 方法2:动态分配内存
    ptr = (int*)malloc(sizeof(int));  // 分配4字节内存
    if (ptr != NULL) {  // 检查分配是否成功
        *ptr = 20;      // 正确:ptr指向有效内存
        printf("*ptr = %d\n", *ptr);  // 输出:*ptr = 20
        free(ptr);      // 必须释放内存,防止泄漏
        ptr = NULL;     // 释放后设为NULL,避免野指针
    }

    return 0;
}

3.2、内存泄漏

动态分配的内存必须通过free()释放。

c 复制代码
int* ptr = (int*)malloc(sizeof(int));
// ... 使用ptr ...
free(ptr);  // 必须释放,否则内存泄漏

3.3、野指针(Dangling Pointer)

指针指向已经释放的内存。

c 复制代码
int* ptr = (int*)malloc(sizeof(int));
free(ptr);
*ptr = 10;  // 错误:ptr成为野指针

3.4、指针越界

访问数组范围外的指针。

c 复制代码
int arr[5];
int* p = arr;
p[10] = 100;  // 错误:越界访问,可能导致程序崩溃

3.5、指针与数组名有区别

数组名是常量指针,不可修改。

c 复制代码
int arr[5];
// arr = NULL;  // 错误:数组名不可赋值
int* ptr = arr;
ptr = NULL;  // 正确:指针可修改
相关推荐
梦境虽美,却不长1 小时前
C语言 学习 宏命令(预处理) 2025年6月9日14:41:39
c语言·开发语言·学习
时时三省1 小时前
【时时三省】(C语言基础)将外部变量的作用域扩展到其他文件
c语言
Fighting_19972 小时前
VSCode占C盘内存太大,如何处理
c语言·ide·vscode
黑听人4 小时前
【力扣 简单 C】21. 合并两个有序链表
c语言·开发语言·数据结构·算法·leetcode
黑听人4 小时前
【力扣 简单 C】83. 删除排序链表中的重复元素
c语言·开发语言·数据结构·算法·leetcode
彷徨而立6 小时前
【C/C++】创建文件夹
c语言·开发语言·c++
程序猿小D8 小时前
第30节 Node.js C/C++ 插件
c语言·c++·后端·node.js·vim
W说编程8 小时前
算法导论第一章:算法基础与排序艺术
c语言·数据结构·算法
慢半拍iii17 小时前
数据结构——D/串
c语言·开发语言·数据结构·c++
whoarethenext18 小时前
使用 C/C++ 和 OpenCV 提取图像的感兴趣区域 (ROI)
c语言·c++·opencv