指针(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;
}

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

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


目录
相关文章
|
机器学习/深度学习 算法 决策智能
【5分钟Paper】Pointer Network指针网络
【5分钟Paper】Pointer Network指针网络
290 0
【5分钟Paper】Pointer Network指针网络
|
1月前
|
C++
C++(十八)Smart Pointer 智能指针简介
智能指针是C++中用于管理动态分配内存的一种机制,通过自动释放不再使用的内存来防止内存泄漏。`auto_ptr`是早期的一种实现,但已被`shared_ptr`和`weak_ptr`取代。这些智能指针基于RAII(Resource Acquisition Is Initialization)原则,即资源获取即初始化。RAII确保对象在其生命周期结束时自动释放资源。通过重载`*`和`-&gt;`运算符,可以方便地访问和操作智能指针所指向的对象。
|
5月前
|
算法 安全 C++
C++ Effective Modern Pointer (智能指针模块)
C++ Effective Modern Pointer (智能指针模块)
|
C++
C++中的智能指针(smart pointer)
C++中的智能指针(smart pointer)
134 0
|
存储 Java Go
借问变量何处存,牧童笑称用指针,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang类型指针(Pointer)的使用EP05
指针是指什么?指针是存储另一个变量的内存地址的变量。变量是一种使用方便的占位符,用于引用计算机内存地址,一个指针变量可以指向任何一个值的内存地址它指向那个值的内存地址。类比的话,指针就是书籍中的目录,本身也占据书页,既可以通过目录获得章节内容,又可以指向具体章节的页数(地址)。
借问变量何处存,牧童笑称用指针,Go lang1.18入门精炼教程,由白丁入鸿儒,go lang类型指针(Pointer)的使用EP05
|
存储 Java Go
Go 编程 | 连载 14 - 指针 Pointer
Go 编程 | 连载 14 - 指针 Pointer
Go 编程 | 连载 14 - 指针 Pointer
|
Java 编译器 Go
Golang 笔记(一):值方法和指针方法(Value Methods vs Pointer Methods)
Golang 笔记(一):值方法和指针方法(Value Methods vs Pointer Methods)
151 0
|
安全 Shell
【C++11】Smart Pointer 智能指针
首先看一个下面的栗子,左边是木有使用智能指针的情况,当执行foo()函数,其中的e指针会在bar(e)时传入bar函数,但是在bar函数结束后没有人为delete e时,就会导致内存泄漏;但是在右边的栗子中,使用了unique_ptr智能指针(single ownership),就能防止内存泄漏。
193 0
【C++11】Smart Pointer 智能指针
|
存储 编译器 C语言
五分钟杀穿指针 pointer——C语言专题
正片开始👀 指针👏 指针提供了对地址操作的一种方法,因此,使用指针可使得 C 语言能够更高效地实现对计算机底层硬件的操作。另外,通过指针可以更便捷地操作数组。在一定意义上可以说,指针是 C 语言的精髓。
五分钟杀穿指针 pointer——C语言专题
LeetCode 138:复制带随机指针的链表 Copy List with Random Pointer
给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。 要求返回这个链表的深拷贝。 A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.
837 0