指针(Pointer)的深度理解(2)

简介: 指针(Pointer)的深度理解(2)

上一篇博客给大家讲了一部分的指针,指针的内容很多但也很重要,SO现在继续!希望大家多多支持!


指针的使用和传址调用

下面我们先看一段代码:

#include<stdio.h>
int my_strlen(const char* str)
{
  int count = 0;
  assert(str);
  while (*str)
  {
    count++;
    str++;
  }
  return count;
}
int main()
{
  int len = my_strlen("abcdef");
  printf("%d\n", len);
  return 0;
}

这是一段模拟库函数strlen的代码实现,代码中就将字符串的首个字符指针传给了函数,进而完成代码的实现。

接下来我们再看一段Swap函数的代码:

#include <stdio.h>
void Swap1(int x, int y)
{
  int tmp = x;
  x = y;
  y = tmp;
}
int main()
{
  int i = 0;
  int j = 0;
  scanf("%d %d", &i, &j);
  printf("交换前:a=%d b=%d\n", i, j);
  Swap1(i, j);
  printf("交换后:a=%d b=%d\n", i, j);
  return 0;
}

运行之后的结果为:


不难发现其实 i 和 j 的值并未发生转换,这是为什么呢,我们继续往下看:

通过监视窗口我们发现虽然 i,j 的值与x,y的值对应相等但他们的地址却不相等,这是为什么呢?因为x和y确实接收到了a和b的值,不过x的地址和a的地址不 ⼀样,y的地址和b的地址不⼀样,相当于x和y是独立的空间,那么在Swap1函数内部交换x和y的值, 自然不会影响a和b,当Swap函数调用结束后回到main函数,a和b的没法交换。Swap函数在使用 的时候,是把变量本身直接传递给了函数,这种调用函数的方式我们之前在函数的时候就知道了,这 种叫传值调用。

所以我们得出一个结论:实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实参。 所以Swap是失败的了。


所以我们就需要运用到指针的传址调用了!我们只需要把 i 和 j 的地址传给函数即可:

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


我们可以看到实现成Swap的方式,顺利完成了任务,这⾥调⽤Swap2函数的时候是将变量的地址传 递给了函数,这种函数调用方式叫:传址调用。

结论:传址调用,可以让函数和主调函数之间建⽴真正的联系,在函数内部可以修改主调函数中的变量;所 以未来函数中只是需要主调函数中的变量值来实现计算,就可以采⽤传值调用。如果函数内部要修改 主调函数中的变量的值,就需要传址调用。

数组名与指针

数组名和指针有啥关系呢?我们看下面代码:

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

我们发现数组名和数组首元素的地址打印出的结果⼀模⼀样,数组名就是数组首元素(第⼀个元素)的地址。但需要注意的是在两种情况下这两个还是有所区别的:

  • sizeof(数组名),sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小, 单位是字节
  • &数组名,这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素 的地址是有区别的)

下面给大家展示代码理解一下:

int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  printf("%d\n", sizeof(arr));
  return 0;
}


我们可以看到这里显示40个字节,SO这里就印证了上面的说法·

下面我们在思考一下,下面的代码:

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


是不是发现其中的奥妙了呢!

&arr[0]      = 004FFC14     数组首元素地址
&arr[0]+1  = 004FFC18     第二个元素的地址
arr             = 004FFC14     数组首元素地址

arr+1         = 004FFC18     第二个元素的地址
&arr           = 004FFC14     数组首元素地址
&arr+1      = 004FFC3C     跳过整个数组的地址

这里我们就需要注意一个问题了:

#include <stdio.h>
void test(int arr[])
{
  int sz2 = sizeof(arr) / sizeof(arr[0]);
  printf("sz2 = %d\n", sz2);
}
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int sz1 = sizeof(arr) / sizeof(arr[0]);
  printf("sz1 = %d\n", sz1);
  test(arr);
  return 0;
}


可已看出我们将数组传到函数时此时arr是一个地址所以其大小只能是4(在32位机器中)所以

sizeof(arr) / sizeof(arr[0]) 的值为1。

二级指针

二级指针也称为指向指针的指针,是指针的指针,即指针变量的值是指向另一个指针变量的地址。

我们知道指针变量也是变量那么,变量就会有地址,那么二级指针便是用来储存指针的地址。

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


可以看出地址确实是存在的。

指针数组

我们可以联想到之前,整型数组,是存放整型的数组,字符数组是存放字符的数组。 那指针数组呢?那当然是存放指针的数组了。

看了上图是否清晰明了了呢!!!

最后我们来讲一下:指针数组模拟二维数组

代码的实现可以看下面的代码:

#include <stdio.h>
int main()
{
  int arr1[] = { 1,2,3,4,5 };
  int arr2[] = { 2,3,4,5,6 };
  int arr3[] = { 3,4,5,6,7 };
  //数组名是首元素地址,类型是int*的,就可以存放在parr数组中
  int* parr[3] = { arr1, arr2, arr3 };
  int i = 0;
  int j = 0;
  for (i = 0; i < 3; i++)
  {
    for (j = 0; j < 5; j++)
    {
      printf("%d ", parr[i][j]);
    }
    printf("\n");
  }
  return 0;
}

这样我们就可以用指针数组模拟出一个二位数组了。

好了今天到此结束!喜欢的可以三连加关注哦!


目录
相关文章
|
25天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
17天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
4天前
|
JSON 自然语言处理 数据管理
阿里云百炼产品月刊【2024年9月】
阿里云百炼产品月刊【2024年9月】,涵盖本月产品和功能发布、活动,应用实践等内容,帮助您快速了解阿里云百炼产品的最新动态。
阿里云百炼产品月刊【2024年9月】
|
1天前
|
人工智能 Rust Java
10月更文挑战赛火热启动,坚持热爱坚持创作!
开发者社区10月更文挑战,寻找热爱技术内容创作的你,欢迎来创作!
263 12
|
19天前
|
人工智能 IDE 程序员
期盼已久!通义灵码 AI 程序员开启邀测,全流程开发仅用几分钟
在云栖大会上,阿里云云原生应用平台负责人丁宇宣布,「通义灵码」完成全面升级,并正式发布 AI 程序员。
|
21天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2582 22
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
|
3天前
|
存储 人工智能 搜索推荐
数据治理,是时候打破刻板印象了
瓴羊智能数据建设与治理产品Datapin全面升级,可演进扩展的数据架构体系为企业数据治理预留发展空间,推出敏捷版用以解决企业数据量不大但需构建数据的场景问题,基于大模型打造的DataAgent更是为企业用好数据资产提供了便利。
171 2
|
1天前
|
编译器 C#
C#多态概述:通过继承实现的不同对象调用相同的方法,表现出不同的行为
C#多态概述:通过继承实现的不同对象调用相同的方法,表现出不同的行为
101 65
|
21天前
|
机器学习/深度学习 算法 数据可视化
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
2024年中国研究生数学建模竞赛C题聚焦磁性元件磁芯损耗建模。题目背景介绍了电能变换技术的发展与应用,强调磁性元件在功率变换器中的重要性。磁芯损耗受多种因素影响,现有模型难以精确预测。题目要求通过数据分析建立高精度磁芯损耗模型。具体任务包括励磁波形分类、修正斯坦麦茨方程、分析影响因素、构建预测模型及优化设计条件。涉及数据预处理、特征提取、机器学习及优化算法等技术。适合电气、材料、计算机等多个专业学生参与。
1578 16
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
|
5天前
|
Linux 虚拟化 开发者
一键将CentOs的yum源更换为国内阿里yum源
一键将CentOs的yum源更换为国内阿里yum源
263 2