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

C语言 指针初阶

C语言学习!

目录

文章目录

前言

一、指针和数组

1.1 指针与数组的关系

1.2 指针运算符和下标运算符

1.3 数组传参

二、二级指针

三、指针数组

3.1 一维数组

3.2 二维数组

总结


前言

        上一篇博文C语言 指针-CSDN博客简单介绍了指针,本篇文章是对指针内容的补充。


一、指针和数组

1.1 指针与数组的关系

  • 数组:一组相同类型元素的集合。
  • 指针变量:是一个变量,存放的是地址。
  • 数组可以通过指针访问。

        arr 是数组,表达式 arr 的值就与 arr[0] 的地址,即 &arr[0] 一致,如果数组 arr 的元素类型为 int 型,那么不管元素个数是多少,表达式 arr 的类型就是 int* 型。

代码示例:

#include <stdio.h>int main()
{int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };int* p = arr;int sz = sizeof(arr) / sizeof(arr[0]);int i = 0;for (i = 0; i < sz; i++){printf("&a[%d] = %p   p+%d = %p\n", i, &arr[i], i, p + i);}return 0;
}

运行结果:

&a[0] = 00AFFEAC   p+0 = 00AFFEAC
&a[1] = 00AFFEB0   p+1 = 00AFFEB0
&a[2] = 00AFFEB4   p+2 = 00AFFEB4
&a[3] = 00AFFEB8   p+3 = 00AFFEB8
&a[4] = 00AFFEBC   p+4 = 00AFFEBC
&a[5] = 00AFFEC0   p+5 = 00AFFEC0
&a[6] = 00AFFEC4   p+6 = 00AFFEC4
&a[7] = 00AFFEC8   p+7 = 00AFFEC8
&a[8] = 00AFFECC   p+8 = 00AFFECC
&a[9] = 00AFFED0   p+9 = 00AFFED0

        代码声明了数组 arr 和指针 p ,指针 p 的初始值是 arr ,因为数组名 arr 会被解释为 &arr[0],所以存入 p 的值为 &arr[0] 的值,也就是指针 p 会被初始化为指向数组 arr 的起始元素 arr[0]。

        由运行结果也可看出,指向个元素的指针 p+i 和 &arr[i] 是等价的。 &arr[i] 是指向元素 arr[i] 的指针,其值是 arr[i] 的地址。

指向数组元素的指针,有以下规则成立:

指针 p 指向数组中的元素 e 时

  • p+i 为指向元素 e 后第 i 个元素的指针。
  • p-i 为指向元素 e 前第 i 个元素的指针。

数组名在以下两种情况下不被视为指向起始元素的指针:

1.作为 sizeof 运算符的操作数出现时。

        sizeof(数组名) 不会生成指向起始元素的指针的长度,而是生成数组整体的长度。

2.作为取地址运算符 & 的操作数出现时。

        & 数组名 不是指向起始元素的指针的指针,而是指向数组整体的指针。


1.2 指针运算符和下标运算符

指向数组内部元素的指针 p+i 前加指针运算符 * 

        因为 p+i 是指向 p 所指元素后第 i 个元素的指针,所以在其前加上指针运算符后得到的 *(p+i) 就是该元素的别名。若 p 指向 a[0] ,那么表达式 *(p+i) 就表示 a[i] 本身。

指针 p 指向数组中的元素 e 时,

  • 指向元素 e 后第 i 个元素的 *(p+i),可以写成 p[i]。
  • 指向元素 e 前第 i 个元素的 *(p-i),可以写成 p[-i]。

        关于指针的运算可参考上篇博文C语言 指针-CSDN博客

代码示例:

#include <stdio.h>int main()
{int i = 0;int arr[5] = { 1,2,3,4,5 };int* p = arr;//arr是首元素地址int sz = sizeof(arr) / sizeof(arr[0]);for (i = 0; i < sz; i++){printf("arr[%d] = %d   *(arr+%d) = %d   p[%d] = %d   *(p+%d) = %d\n", i, arr[i], i, *(arr + i), i, p[i], i, *(p + i));}for (i = 0; i < sz; i++){printf("&arr[%d] = %p   arr+%d = %p   &p[%d] = %p   p+%d = %p\n", i, &arr[i], i, (arr + i), i, &p[i], i, (p + i));}return 0;
}

运行结果:

arr[0] = 1   *(arr+0) = 1   p[0] = 1   *(p+0) = 1
arr[1] = 2   *(arr+1) = 2   p[1] = 2   *(p+1) = 2
arr[2] = 3   *(arr+2) = 3   p[2] = 3   *(p+2) = 3
arr[3] = 4   *(arr+3) = 4   p[3] = 4   *(p+3) = 4
arr[4] = 5   *(arr+4) = 5   p[4] = 5   *(p+4) = 5
&arr[0] = 004FFD38   arr+0 = 004FFD38   &p[0] = 004FFD38   p+0 = 004FFD38
&arr[1] = 004FFD3C   arr+1 = 004FFD3C   &p[1] = 004FFD3C   p+1 = 004FFD3C
&arr[2] = 004FFD40   arr+2 = 004FFD40   &p[2] = 004FFD40   p+2 = 004FFD40
&arr[3] = 004FFD44   arr+3 = 004FFD44   &p[3] = 004FFD44   p+3 = 004FFD44
&arr[4] = 004FFD48   arr+4 = 004FFD48   &p[4] = 004FFD48   p+4 = 004FFD48
  • p+i 指向 arr[i] ,所以 *(p+i) 是 arr[i] 的别名。
  • *(p+i) 可以写为 p[i] ,所以 p[i] 也是arr[i] 的别名。
  • 数组名 arr 是指向起始元素 arr[0] 的指针,所以 arr+i 就是指向元素 arr[i] 的指针。
  • 指针 arr+i 指向元素 arr[i] ,所以在其前写上指针运算符后得到的 *(arr+i) 就是 arr[i] 的别名。

访问各元素的4个表达式: arr[i] 、*(arr + i) 、 p[i] 、*(p + i) 。

指向各元素指针的4个表达式: &arr[i] 、arr + i 、&p[i] 、p + i 。

总结:

        int* 型指针 p 指向 int 型数组 arr 的起始元素 arr[0] 时,指针 p 的行为就和数组 arr 本身一样。

        这里的类型可以是 char 、double、int 等等。


1.3 数组传参

        数组传参传的是首元素地址。

数组名传参代码示例:

#include <stdio.h>void text(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}
}
int main()
{int arr[10] = { 0 };text(arr, 10);return 0;
}

运行结果:

0 0 0 0 0 0 0 0 0 0

指针传参代码示例:

#include <stdio.h>void text(int* p, int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", *(p + i));}
}int main()
{int arr[10] = { 0 };text(arr, 10);return 0;
}

运行结果:

0 0 0 0 0 0 0 0 0 0

        上述两个代码等效。

二、二级指针

        指针变量也是变量,是变量就有地址,那指针变量的地址也可以存放在指针里。 就是二级指针。

代码示例:

#include <stdio.h>int main()
{int a = 0;int* pa = &a;int** ppa = &pa;**ppa = 6;printf("%d", a);return 0;
}

运行结果:

6

pa是一个指针变量,一级指针变量。

ppa是一个二级指针变量。

对ppa解引用 *ppa ,找到 pa;再解引用 **ppa 才可以找到 a。

int* pa = &a;

pa前面的int表示 pa 指向的对象是int类型的,*说明 pa 是指针变量。

int** ppa = &pa;

int* 是说明 ppa 指向的对象是 int* 类型,后面的 * 是说明 ppa 是指针。

总结:

        二级指针变量是用来存放一级指针变量的地址的。

三、指针数组

        存放指针的数组就是指针数组。

3.1 一维数组

代码示例:

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

运行结果:

0 1 2 3 4

3.2 二维数组

普通二维数组的使用代码示例:

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

运行结果:

1 2 3 4
2 3 4 5
3 4 5 6


二维指针数组代码示例:

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

运行结果:

1 2 3 4
2 3 4 5
3 4 5 6

        parr 指针数组指向3个数组 arr1、arr2、arr3 的数组首元素地址 arr1[0]、 arr2[0]、 arr3[0]。

parr 因为是指向 int 型数组的指针,所以 parr 类型为 int* 。

        指针数组 parr 把三个独立的一维数组 arr1、arr2、arr3 串在一起管理。可以运行出上述普通二维数组的代码结果。

注:在 printf 函数打印数组时操作符 [ ] 就相当于解引用操作符 * ,arr[i] 等价于 *(arr+i) 。


总结

        以上就是今天要讲的内容,本文仅仅简单介绍了指针和数组、二级指针、指针数组的内容。

相关文章:

  • 【Redis-08】Redis主从复制的实现原理
  • Linux驱动开发之杂项设备注册和Linux2.6设备注册
  • Android IdleHandler闲时加载
  • 《Python机器学习原理与算法实现》学习笔记
  • redis—List列表
  • MySQL:主从复制
  • 【项目】玩具租赁博客测试报告
  • 每日一题(LeetCode)----二叉树-- 二叉树的右视图
  • 【智慧门店】东胜物联蓝牙网关助力解决方案商,推动汽车后市场企业智能化升级
  • Java:基本类型及它们的封装类
  • 复试 || 就业day05(2023.12.31)算法篇
  • SpringCloud(H版alibaba)框架开发教程,使用eureka,zookeeper,consul,nacos做注册中心——附源码(1)
  • IntelliJ IDEA [插件 MybatisX] mapper和xml间跳转
  • 【Spring Security】AuthenticationFailureHandler 用户认证失败后处理
  • 数据特征工程 | PSO粒子群算法的特征选择原理及python代码实现
  • 【技术性】Search知识
  • E-HPC支持多队列管理和自动伸缩
  • es6--symbol
  • flutter的key在widget list的作用以及必要性
  • JS学习笔记——闭包
  • Linux下的乱码问题
  • Nacos系列:Nacos的Java SDK使用
  • Nginx 通过 Lua + Redis 实现动态封禁 IP
  • Python中eval与exec的使用及区别
  • Terraform入门 - 1. 安装Terraform
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 盘点那些不知名却常用的 Git 操作
  • 微服务框架lagom
  • 原生js练习题---第五课
  • 源码安装memcached和php memcache扩展
  • 《TCP IP 详解卷1:协议》阅读笔记 - 第六章
  • kubernetes资源对象--ingress
  • (1)SpringCloud 整合Python
  • (bean配置类的注解开发)学习Spring的第十三天
  • (Redis使用系列) SpringBoot中Redis的RedisConfig 二
  • (附源码)springboot宠物医疗服务网站 毕业设计688413
  • (四)linux文件内容查看
  • (最优化理论与方法)第二章最优化所需基础知识-第三节:重要凸集举例
  • .net on S60 ---- Net60 1.1发布 支持VS2008以及新的特性
  • .NET 常见的偏门问题
  • .NET 将混合了多个不同平台(Windows Mac Linux)的文件 目录的路径格式化成同一个平台下的路径
  • .Net 应用中使用dot trace进行性能诊断
  • .NET开源项目介绍及资源推荐:数据持久层
  • .NET业务框架的构建
  • @Conditional注解详解
  • @reference注解_Dubbo配置参考手册之dubbo:reference
  • @synthesize和@dynamic分别有什么作用?
  • [2018][note]用于超快偏振开关和动态光束分裂的all-optical有源THz超表——
  • [AIGC] SpringBoot的自动配置解析
  • [BUUCTF 2018]Online Tool(特详解)
  • [C++] new和delete
  • [CSS]CSS 字体属性
  • [dfs搜索寻找矩阵中最长递减序列]魔法森林的秘密路径
  • [Editor]Unity Editor类常用方法
  • [hdu 1247]Hat’s Words [Trie 图]