首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !

  • 25-03-03 01:10
  • 3016
  • 11149
blog.csdn.net

LuckiBit

目录

  • C语言指针精讲
    • 1. 什么是指针?
      • 1.1 指针的内存模型
        • 1.1.1 指针演示
        • 输出
      • 1.2 指针运算
        • 1.2.1 指针算术运算
        • 输出
        • 1.2.2 指针与数组的关系
        • 输出
      • 1.3 指针类型
        • 1.3.1 不同类型的指针
        • 示例
        • 输出
        • 1.3.2 void 指针
        • 输出
      • 1.4 指针与内存管理
        • 动态内存分配
        • 输出
      • 1.5 指针与内存泄漏
        • 1.5.1 内存泄漏示例
        • 1.5.2 解决内存泄漏
      • 1.6 指针的常见错误与调试
        • 1.6.1 常见错误示例
        • 1.6.2 调试工具
    • 2. 指针的声明和初始化
      • 2.1 声明指针
      • 2.2 初始化指针
    • 3. 使用指针访问数据
      • 输出
    • 4. 指针的运算
      • 输出
    • 5. 指针与数组
      • 输出
    • 6. 指针数组和数组指针
      • 6.1 指针数组
      • 6.2 数组指针
    • 7. 函数指针
      • 输出
    • 8. 动态内存分配
      • 输出
    • 9. 指针的类型转换
    • 10. 指针的常见错误
      • 10.1 使用未初始化的指针
      • 10.2 解引用空指针(NULL)
      • 10.3 内存泄漏
      • 10.4 访问越界的内存
    • 11. 实例:交换两个变量的值
      • 输出
    • 12. 指针与结构体
      • 12.1 声明和使用结构体指针
      • 输出
      • 12.2 动态分配结构体内存
      • 输出
    • 13. 指针与函数
      • 13.1 使用指针作为函数参数
      • 输出
      • 13.2 使用指针返回多个值
      • 输出
    • 14. 二级指针
      • 14.1 声明和使用二级指针
      • 输出
      • 14.2 动态分配二维数组
      • 输出
    • 15. 指针与位操作
      • 15.1 位操作基础
      • 输出
      • 15.2 使用指针进行位操作
      • 输出
    • 16. 表格总结
    • 17. 结束语

C语言指针精讲

指针是C语言中一个非常重要和强大的概念。它允许直接操作内存,从而可以高效地处理数据和进行系统编程。下面是C语言中指针的详细讲解:

1. 什么是指针?

指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
下面将从底层内存模型、指针运算、指针类型以及指针与内存管理的关系等方面进行深入探讨。

1.1 指针的内存模型

指针的核心是直接操作内存地址。每个变量在内存中都有一个地址,指针变量存储的就是这个地址。

1.1.1 指针演示

在这里插入图片描述

图1. 指针的解引用图解

// C程序,演示指针的使用
#include 

// 函数定义
void geeks()
{
    int var = 10;  // 定义一个整数变量并赋值为10

    // 声明一个指针变量
    int* ptr;

    // 注意指针变量ptr和变量var的数据类型必须相同
    ptr = &var;  // 将变量var的地址赋值给指针ptr

    // 输出指针ptr的地址
    printf("指针ptr的值 = %p \n", ptr);
    // 输出变量var的值
    printf("变量var的值 = %d \n", var);
    // 输出指针ptr指向的值(指针的解引用)
    printf("指针*ptr指向的值 = %d \n", *ptr);
}

// 主程序
int main()
{
    geeks();  // 调用geeks函数
    return 0; // 返回0,表示程序正常结束
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
输出
ptr 处的值 = 0x7ffca84068dc 
var 处的值 = 10 
*ptr 处的值 = 10
  • 1
  • 2
  • 3

1.2 指针运算

指针不仅可以存储地址,还可以进行算术运算,这在数组和动态内存管理中非常有用。

1.2.1 指针算术运算
int arr[] = {1, 2, 3, 4, 5};
int *p = arr;

printf("First element: %d\n", *p);       // 输出第一个元素
printf("Second element: %d\n", *(p + 1)); // 输出第二个元素
  • 1
  • 2
  • 3
  • 4
  • 5
输出
First element: 1
Second element: 2
  • 1
  • 2
1.2.2 指针与数组的关系

数组名在表达式中实际上是一个指向第一个元素的指针。

int arr[] = {10, 20, 30};
int *p = arr;

for (int i = 0; i < 3; i++) {
    printf("%d ", *(p + i));
}
printf("\n");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
输出
10 20 30
  • 1

1.3 指针类型

指针的类型决定了它解引用时读取的数据类型。

1.3.1 不同类型的指针

常见的指针类型包括:

  • 整数指针:int *
  • 字符指针:char *
  • 浮点数指针:float *
  • 双精度指针:double *

不同类型的指针之间不能互相赋值,除非通过强制类型转换。

Copy code
int a = 10;
float b = 3.14;
int *p1 = &a;
float *p2 = &b;

p1 = (int *)p2; // 强制类型转换
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
示例
int a = 5;
float b = 5.5;
int *pInt = &a;
float *pFloat = &b;

printf("Value of a: %d\n", *pInt);
printf("Value of b: %.1f\n", *pFloat);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
输出
Value of a: 5
Value of b: 5.5
  • 1
  • 2
1.3.2 void 指针

void指针是一种特殊的指针类型,可以指向任何类型的数据,但不能直接解引用。

int a = 10;
void *pVoid = &a;
printf("Value of a through void pointer: %d\n", *(int *)pVoid);  // 需要类型转换
  • 1
  • 2
  • 3
输出
Value of a through void pointer: 10
  • 1

1.4 指针与内存管理

指针在内存管理中扮演着重要角色,特别是在动态内存分配方面。

动态内存分配
int *p = (int *)malloc(sizeof(int) * 5);
if (p != NULL) {
    for (int i = 0; i < 5; i++) {
        p[i] = i * 2;
        printf("%d ", p[i]);
    }
    free(p);
    printf("\n");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
输出
0 2 4 6 8
  • 1

1.5 指针与内存泄漏

内存泄漏是指程序在运行过程中动态分配的内存没有被正确释放,从而导致内存资源的浪费甚至程序崩溃。使用指针时,必须注意及时释放动态分配的内存。

1.5.1 内存泄漏示例
void memoryLeakExample() {
    int *p = (int *)malloc(sizeof(int) * 10);
    // 忘记调用free(p); 导致内存泄漏
}
  • 1
  • 2
  • 3
  • 4
1.5.2 解决内存泄漏
void correctMemoryManagement() {
    int *p = (int *)malloc(sizeof(int) * 10);
    if (p != NULL) {
        // 使用p...
        free(p);  // 正确释放内存
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

1.6 指针的常见错误与调试

使用指针时,常见错误包括解引用空指针、使用未初始化的指针、内存越界等。调试这些错误需要细致的检查和使用调试工具。

1.6.1 常见错误示例
int *p;  // 未初始化的指针
*p = 10; // 未定义行为,可能导致程序崩溃

int *q = NULL; 
*q = 10; // 解引用空指针,可能导致程序崩溃
  • 1
  • 2
  • 3
  • 4
  • 5
1.6.2 调试工具

使用工具如gdb可以帮助发现和调试指针相关的错误。例如,设置断点并逐步执行代码,检查指针的值和指向的内存内容。

2. 指针的声明和初始化

2.1 声明指针

声明指针时,需要指定指针将要指向的数据类型。例如:

int *p;  // 声明一个指向int类型的指针变量p
  • 1

2.2 初始化指针

初始化指针时,可以将其设置为一个有效的内存地址。例如:

int a = 10;
int *p = &a;  // p指向变量a的地址
  • 1
  • 2

3. 使用指针访问数据

通过指针访问和修改指向的数据,可以使用解引用操作符(*)。例如:

int a = 10;
int *p = &a;

printf("a = %d\n", *p);  // 输出a的值,即10

*p = 20;  // 修改p指向的变量的值
printf("a = %d\n", a);  // 输出修改后的a的值,即20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

输出

a = 10
a = 20
  • 1
  • 2

4. 指针的运算

指针可以进行一些算术运算,如加法、减法等。这些运算通常用于数组遍历。

int arr[] = {1, 2, 3, 4, 5};
int *p = arr;

for (int i = 0; i < 5; i++) {
    printf("%d ", *(p + i));  // 输出数组元素
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

输出

1 2 3 4 5
  • 1

5. 指针与数组

数组名本身就是一个指针,指向数组的第一个元素。例如:

int arr[] = {1, 2, 3};
int *p = arr;

printf("%d\n", *(p + 1));  // 输出第二个元素,即2
  • 1
  • 2
  • 3
  • 4

输出

2
  • 1

6. 指针数组和数组指针

6.1 指针数组

指针数组:数组的每个元素都是一个指针。

int *p[3];
  • 1

6.2 数组指针

数组指针:指向数组的指针。

int (*p)[3];
  • 1

7. 函数指针

函数指针是指向函数的指针,允许通过指针调用函数。

void func() {
    printf("Hello, World!\n");
}

void (*pFunc)() = func;  // 声明并初始化函数指针
pFunc();  // 通过指针调用函数
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

输出

Hello, World!
  • 1

8. 动态内存分配

使用指针进行动态内存分配可以更加灵活地管理内存。常用的函数有malloc、calloc和free。

int *p = (int *)malloc(sizeof(int) * 5);  // 分配内存
if (p != NULL) {
    for (int i = 0; i < 5; i++) {
        p[i] = i * 2;
    }
    for (int i = 0; i < 5; i++) {
        printf("%d ", p[i]);
    }
    free(p);  // 释放内存
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

输出

0 2 4 6 8
  • 1

9. 指针的类型转换

指针可以进行类型转换,但需要谨慎使用,以避免不安全的操作。

void *p = malloc(10);
int *intP = (int *)p;  // 将void指针转换为int指针
  • 1
  • 2

10. 指针的常见错误

10.1 使用未初始化的指针

int *p;  // p未初始化
*p = 10; // 未定义行为,可能导致程序崩溃
  • 1
  • 2

解释和原理:
未初始化的指针没有指向有效的内存地址,因此对它进行解引用操作会导致未定义行为,可能引发程序崩溃或其他错误。

10.2 解引用空指针(NULL)

int *p = NULL;
*p = 10;  // 未定义行为,可能导致程序崩溃
  • 1
  • 2

解释和原理:
空指针(NULL)表示指针不指向任何有效的内存地址。对NULL指针进行解引用操作会导致未定义行为,通常会引发程序崩溃。

10.3 内存泄漏

int *p = (int *)malloc(sizeof(int) * 5);
// 忘记调用free(p); 释放内存
  • 1
  • 2

解释和原理:
动态分配的内存在不再需要时必须释放。如果忘记释放,会导致内存泄漏,长时间运行的程序可能耗尽内存资源,导致系统性能下降或崩溃。

10.4 访问越界的内存

int arr[5];
int *p = arr;
p[5] = 10;  // 越界访问,未定义行为
  • 1
  • 2
  • 3

解释和原理:
访问数组越界的内存会导致未定义行为,可能覆盖其他重要数据或导致程序崩溃。编译器无法检测所有的越界访问,必须在编写代码时注意避免。

11. 实例:交换两个变量的值

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 10, y = 20;
    swap(&x, &y);
    printf("x = %d, y = %d\n", x, y);  // 输出x=20, y=10
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

输出

x = 20, y = 10
  • 1

好的,下面是修改和优化后的内容:

12. 指针与结构体

在C语言中,指针和结构体的结合可以实现更加复杂的数据结构和操作。

12.1 声明和使用结构体指针

struct Person {
    char name[50];
    int age;
};

struct Person person1 = {"Alice", 30};
struct Person *pPerson = &person1;

printf("Name: %s, Age: %d\n", pPerson->name, pPerson->age);  // 使用箭头操作符访问成员
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

输出

Name: Alice, Age: 30
  • 1

12.2 动态分配结构体内存

struct Person *pPerson = (struct Person *)malloc(sizeof(struct Person));
if (pPerson != NULL) {
    strcpy(pPerson->name, "Bob");
    pPerson->age = 25;

    printf("Name: %s, Age: %d\n", pPerson->name, pPerson->age);  // 输出动态分配的结构体数据

    free(pPerson);  // 释放动态分配的内存
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

输出

Name: Bob, Age: 25
  • 1

13. 指针与函数

指针与函数结合使用,可以实现函数参数的传递和返回更为复杂的数据类型。

13.1 使用指针作为函数参数

void increment(int *p) {
    (*p)++;
}

int main() {
    int value = 10;
    increment(&value);
    printf("Value: %d\n", value);  // 输出经过增量操作后的值
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

输出

Value: 11
  • 1

13.2 使用指针返回多个值

void getMinMax(int *arr, int size, int *min, int *max) {
    *min = *max = arr[0];
    for (int i = 1; i < size; i++) {
        if (arr[i] < *min) *min = arr[i];
        if (arr[i] > *max) *max = arr[i];
    }
}

int main() {
    int arr[] = {3, 5, 1, 9, 2};
    int min, max;
    getMinMax(arr, 5, &min, &max);
    printf("Min: %d, Max: %d\n", min, max);  // 输出数组中的最小值和最大值
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

输出

Min: 1, Max: 9
  • 1

14. 二级指针

二级指针是指向指针的指针,常用于动态分配二维数组或处理指针数组。

14.1 声明和使用二级指针

int a = 10;
int *p = &a;
int **pp = &p;

printf("Value of a: %d\n", **pp);  // 使用二级指针访问a的值
  • 1
  • 2
  • 3
  • 4
  • 5

输出

Value of a: 10
  • 1

14.2 动态分配二维数组

int rows = 3, cols = 4;
int **matrix = (int **)malloc(rows * sizeof(int *));
for (int i = 0; i < rows; i++) {
    matrix[i] = (int *)malloc(cols * sizeof(int));
}

// 初始化并打印二维数组
for (int i = 0; i < rows; i++) {
    for (int j = 0; j < cols; j++) {
        matrix[i][j] = i * cols + j;
        printf("%2d ", matrix[i][j]);
    }
    printf("\n");
}

// 释放二维数组的内存
for (int i = 0; i < rows; i++) {
    free(matrix[i]);
}
free(matrix);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

输出

 0  1  2  3
 4  5  6  7
 8  9 10 11
  • 1
  • 2
  • 3

15. 指针与位操作

指针与位操作结合使用,可以更高效地处理低层数据操作,尤其在嵌入式系统中。

15.1 位操作基础

unsigned char a = 0b10101010;
unsigned char b = 0b11001100;
unsigned char c = a & b;  // 按位与操作

printf("Result: %02X\n", c);  // 输出结果
  • 1
  • 2
  • 3
  • 4
  • 5

输出

Result: 88
  • 1

15.2 使用指针进行位操作

void setBit(unsigned char *byte, int bit) {
    *byte |= (1 << bit);  // 设置指定位
}

int main() {
    unsigned char value = 0x00;
    setBit(&value, 3);
    printf("Value: %02X\n", value);  // 输出设置指定位后的值
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

输出

Value: 08
  • 1

16. 表格总结

概念描述
指针声明int *p; 声明一个指向int类型的指针变量p
指针初始化int *p = &a; 将指针p初始化为变量a的地址
指针解引用*p 访问指针p指向的变量的值
指针运算*(p + i) 访问指针p偏移i个位置后的值
指针数组int *p[3]; 声明一个指针数组,每个元素都是一个指针
数组指针int (*p)[3]; 声明一个数组指针,指向一个包含3个int类型元素的数组
函数指针void (*pFunc)(); 声明一个指向函数的指针
动态内存分配int *p = (int *)malloc(sizeof(int) * 5); 使用malloc分配内存
指针类型转换int *intP = (int *)p; 将void指针转换为int指针
指针常见错误未初始化指针、解引用空指针、内存泄漏、访问越界内存
交换两个变量的值使用指针参数进行值交换 void swap(int *a, int *b);

17. 结束语

  1. 本节内容已经全部介绍完毕,希望通过这篇文章,大家对C语言中的指针有了更深入的理解和认识。
  2. 感谢各位的阅读和支持,如果觉得这篇文章对你有帮助,请不要吝惜你的点赞和评论,这对我们非常重要。再次感谢大家的关注和支持!LuckiBit
文章知识点与官方知识档案匹配,可进一步学习相关知识
C技能树指针指向函数的指针211226 人正在系统学习中
注:本文转载自blog.csdn.net的LuckiBit的文章"https://blog.csdn.net/EleganceJiaBao/article/details/140648720"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top