当前位置: 首页 > news >正文

C语言教程(十四):C 语言指针详解

一、指针的基本概念

        指针是一个变量,其值为另一个变量的内存地址。简单来说,指针指向了内存中的某个位置,通过指针可以间接访问该位置存储的数据。指针的使用可以让程序更加高效地处理数据,特别是在处理数组、动态内存分配等方面。

二、指针的定义和初始化

  2.1 指针的定义

        定义指针变量的语法格式为:数据类型 *指针变量名;

        数据类型:指定指针所指向的变量的数据类型,如 `int`、`float`、`char` 等。

        指针变量名:是用户自定义的标识符,用于标识这个指针变量。

         `*` :是指针声明符,用于表明这是一个指针变量。

        例如,定义一个指向整数的指针:int *ptr;

  2.2 指针的初始化

        指针变量在使用之前最好进行初始化,否则它可能包含一个随机的内存地址,访问该地址可能会导致未定义行为。指针可以初始化为 `NULL` 或者某个变量的地址。

        初始化为 `NULL`:`NULL` 是一个特殊的指针值,表示指针不指向任何有效的内存地址。int *ptr = NULL;

        初始化为某个变量的地址:使用取地址运算符 `&` 来获取变量的地址,并将其赋值给指针。

#include <stdio.h>int main() {int num = 10;int *ptr = &num;  // ptr 指向 num 的地址printf("num 的地址: %p\n", &num);printf("ptr 存储的地址: %p\n", ptr);return 0;
}

三、指针的解引用

        解引用是指通过指针访问其所指向的内存位置的数据。使用解引用运算符 `*` 来实现。

#include <stdio.h>int main() {int num = 10;int *ptr = &num;printf("num 的值: %d\n", num);printf("通过指针访问 num 的值: %d\n", *ptr);*ptr = 20;  // 通过指针修改 num 的值printf("修改后 num 的值: %d\n", num);return 0;
}

        在上述代码中,`*ptr` 表示访问 `ptr` 所指向的内存位置的数据。可以通过 `*ptr` 来读取或修改该位置的数据。

四、指针与数组

  4.1 数组名与指针的关系

        在C语言中,数组名在大多数情况下会被隐式转换为指向数组第一个元素的指针。例如:

#include <stdio.h>int main() {int numbers[5] = {1, 2, 3, 4, 5};int *ptr = numbers;  // numbers 被转换为指向第一个元素的指针printf("数组的第一个元素: %d\n", *ptr);return 0;
}

  4.2 通过指针访问数组元素

        可以使用指针的算术运算来访问数组元素。指针的算术运算包括指针的加法和减法。

#include <stdio.h>int main() {int numbers[5] = {1, 2, 3, 4, 5};int *ptr = numbers;for (int i = 0; i < 5; i++) {printf("%d ", *(ptr + i));}printf("\n");return 0;
}

在上述代码中,`ptr + i` 表示指向数组中第 `i` 个元素的指针,`*(ptr + i)` 表示访问该元素的值。

五、指针与函数

  5.1 指针作为函数参数

        将指针作为函数参数可以实现通过函数修改实参的值。这是因为传递的是实参的地址,函数可以直接访问和修改该地址所存储的数据。

#include <stdio.h>void swap(int *a, int *b) {int temp = *a;*a = *b;*b = temp;
}int main() {int x = 10, y = 20;printf("交换前: x = %d, y = %d\n", x, y);swap(&x, &y);printf("交换后: x = %d, y = %d\n", x, y);return 0;
}

        在上述代码中,`swap` 函数接受两个指针作为参数,通过指针交换了两个变量的值。

  5.2 指针作为函数返回值

        函数可以返回一个指针,但需要注意返回的指针必须指向有效的内存地址。如果返回的是局部变量的地址,可能会导致未定义行为,因为局部变量在函数结束后会被销毁。

#include <stdio.h>int *getMax(int *arr, int size) {int *max = arr;for (int i = 1; i < size; i++) {if (*(arr + i) > *max) {max = arr + i;}}return max;
}int main() {int numbers[5] = {1, 3, 2, 5, 4};int *maxPtr = getMax(numbers, 5);printf("数组中的最大值: %d\n", *maxPtr);return 0;
}

        在上述代码中,`getMax` 函数返回一个指向数组中最大值的指针。

六、动态内存分配

  6.1 `malloc` 函数

        `malloc` 函数用于在堆内存中分配指定大小的内存块。其原型为:void *malloc(size_t size);

 `size` 是要分配的内存块的大小,单位是字节。

`malloc` 函数返回一个指向分配的内存块的指针,如果分配失败则返回 `NULL`

#include <stdio.h>
#include <stdlib.h>int main() {int *ptr = (int *)malloc(5 * sizeof(int));if (ptr == NULL) {printf("内存分配失败\n");return 1;}for (int i = 0; i < 5; i++) {ptr[i] = i + 1;}for (int i = 0; i < 5; i++) {printf("%d ", ptr[i]);}printf("\n");free(ptr);  // 释放分配的内存return 0;
}

        在上述代码中,使用 `malloc` 函数分配了一个可以存储 5 个整数的内存块,并对其进行初始化和访问,最后使用 `free` 函数释放了分配的内存。

  6.2 `calloc` 函数

        `calloc` 函数也用于在堆内存中分配内存块,与 `malloc` 不同的是,`calloc` 会将分配的内存块初始化为 0。其原型为:void *calloc(size_t num, size_t size);

`num` 是要分配的元素个数。

`size` 是每个元素的大小,单位是字节。

#include <stdio.h>
#include <stdlib.h>int main() {int *ptr = (int *)malloc(3 * sizeof(int));if (ptr == NULL) {printf("内存分配失败\n");return 1;}for (int i = 0; i < 3; i++) {ptr[i] = i + 1;}ptr = (int *)realloc(ptr, 5 * sizeof(int));if (ptr == NULL) {printf("内存重新分配失败\n");return 1;}for (int i = 3; i < 5; i++) {ptr[i] = i + 1;}for (int i = 0; i < 5; i++) {printf("%d ", ptr[i]);}printf("\n");free(ptr);return 0;
}

七、指针的注意事项

        空指针解引用:对 `NULL` 指针进行解引用会导致未定义行为,可能会使程序崩溃。在使用指针之前,最好检查其是否为 `NULL`。

        野指针:野指针是指指向无效内存地址的指针,如指向已经释放的内存或未初始化的指针。使用野指针会导致未定义行为,要避免产生野指针。

        内存泄漏:在使用动态内存分配函数(如 `malloc`、`calloc`、`realloc`)分配内存后,要记得使用 `free` 函数释放内存,否则会导致内存泄漏,使系统的可用内存逐渐减少。

相关文章:

  • 学习threejs,使用EffectComposer后期处理组合器(采用RenderPass、ShaderPass渲染通道),案例一
  • C语言中封装JSON数组对象
  • 使用RabbitMQ实现判题功能
  • 从入门到精通【MySQL】视图与用户权限管理
  • 大屏资源汇总
  • DS B/B+/B*树及其应用(21)
  • 电子削铅笔刀顺序图详解:从UML设计到PlantUML实现
  • JMeter 安装及使用 [软件测试工具]
  • C++ 日志系统实战第二步:不定参数函数解析
  • 经验分享 | 如何高效使用 `git commit --amend` 修改提交记录
  • 项目右键没有add as maven project选项
  • 实时步数统计系统 kafka + spark +redis
  • springboot在eclipse里面运行 run as 是Java Application还是 Maven
  • 如何创建一个父类 Maven项目,然后在父类下再创建子项目,构建多模块 Maven 项目
  • 深入浅出JavaScript常见设计模式:从原理到实战(1)
  • 基于Python+Flask的MCP SDK响应式文档展示系统设计与实现
  • 第J5周:DenseNet+SE-Net实战
  • 机器学习漏洞大汇总——利用机器学习服务
  • 手撕C++STL list:深入理解双向链表的实现
  • 电子病历高质量语料库构建方法与架构项目(计划篇)
  • 中国天主教组织发唁电对教皇去世表示哀悼
  • 大理洱源4.8级地震致442户房屋受损,无人员伤亡
  • 消费补贴政策力度最大的一届!第六届上海“五五购物节” 4月底启幕
  • 再放宽!新版市场准入负面清单发布,无人驾驶航空器、电子烟等新业态被纳入
  • 云南大理州洱源县发生4.8级地震,震源深度10千米
  • 海南一季度GDP为1904.17亿元,同比增长4.0%