【C初阶】进一步初识指针

简介: 【C初阶】进一步初识指针

前言

本节博客继续来探讨关于指针初阶的相关基础内容

1.数组名的理解

1.1数组名、取地址数组首元素与取地址数组名的区别

数组名是数组首元素的地址。
取地址数组名取出来的是整个数组的地址。

也就是说arr==&arr[0]!=&arr

我们可以来简单做个测试:

//数组名的理解
int main()
{
  int arr[10] = { 0 };
  printf("p1 = %p\n", arr);
  printf("p2 = %p\n", &arr[0]);
  printf("p3 = %p\n", &arr);
  return 0;
}

结论:完全一致!!!

但是,有些同学可能就好奇了,作者你不是说arr!=&arr吗?结果是一样的啊。的确在单单看这个地址结果是一样的,但是但是,我们对这些地址进行+1操作你就会发现前两者是一样的,但是第三者是不同了:

int main()
{
  int arr[10] = { 0 };
  printf("arr = %p\n", arr);
  printf("&arr[0] = %p\n", &arr[0]);
  printf("&arr = %p\n", &arr);
  printf("指针+1操作:\n");
  printf("arr+1 = %p\n", arr+1);
  printf("&arr[0]+1 = %p\n", &arr[0]+1);
  printf("&arr+1 = %p\n", &arr+1);
  return 0;
}

好好看一下,就发先前两者+1之后都是加了4个字节的地址,第三个变成加了40个字节的地址哈。

那为啥呢?

指针类型的意义:指针类型决定了指针+1操作跳过多少字节。

因为前两者的指针类型都是int类型的,第三者的类型是int()[10],两种指针类型指向内容的类型大小不一样,前者是4个字节,后者是40个字节!

1.2数组名的正确理解总结

数组名就等同于数组首元素地址,&数组名是整个数组的地址,虽然数组首元素的地址和整个数组的地址从地址结果上来看是一样的,但是指针类型是不同的!

但是有1个例外:

sizeof(数组名):这种情况计算出的为整个数组的大小

2.从本质上理解数组与指针的关系

其实有了指针,我们就可以用指针来访问数组了。

//数组与指针的本质联系
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int* p = arr;
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d ", *p);
    p++;//指针p在第十次循环中越界
    if (i == 9)
      p = NULL;
  }
  return 0;
}

但其实,指针访问跟用数组下标访问本质上是一样的:

//数组与指针的本质联系
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int* p = arr;
  int i = 0;
  //数组下标
  printf("arr[i]: >\n");
  for (i = 0; i < 10; i++)
  {
    printf("%d ", arr[i]);
  }
  //一般指针
  printf("\n");
  printf("*(p+i)\n");
  for (i = 0; i < 10; i++)
  {
    printf("%d ", *(p+i));
  }
  printf("\n");
  printf("\n");
  printf("上面得出结论:arr[i]==*(p+i)==*(arr+i)");
  printf("\n"); 
  printf("\n");
  //指针换一种写法
  printf("\n");
  printf("加法符合交换律,*(i+p)\n");
  
  for (i = 0; i < 10; i++)
  {
    printf("%d ", *(i+p));
  }
  printf("\n");
  printf("so,i[p]这种写法也可以吧::\n");
  for (i = 0; i < 10; i++)
  {
    printf("%d ", i[p]);
  }
  printf("\n");
  return 0;
}

说白了编译器在处理arr[i]的时候,也是会把他转换成*(arr+i)进行处理的。

3.一维数组传参的本质

说起一维数组传参来说,可能很多人会以为我们传数组的时候会直接把整个数组传过去,实际上不然。

//一维数组传参的本质
void test(int arr[10])
{
  int sz = sizeof(arr) / sizeof(arr[0]);
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
}
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  test(arr);
  return 0;
}

按照道理来说不应该是1 2 3…10吗?怎么只有一个数字1呢?

我们调试一下来看看哪里出现了问题:

什么?这个sz怎么给我算出来了个1啊!

原来传进去的是arr中的第一个数字的地址而已

那他是怎么找到后面的数字的呢?

答案很简单,因为数字都是连续存放的,找到第一个数字的地址之后,顺藤摸瓜即可。

其实,一维数组传参也可以改写成这样:

void test(int* arr)
{
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d ", arr[i]);
  }
}
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  test(arr);
  return 0;
}

4.冒泡排序

紧接着,我们来科普一下最基本的一个排序算法哈,冒泡排序

核心思想是两个相邻的数字进行比较

这里不重点说这个,简单一提。

void bubble_sort(int arr[], int sz)//参数接收数组元素个数
{
  int i = 0;
  for (i = 0; i < sz - 1; i++)
  {
    int j = 0;
    for (j = 0; j < sz - i - 1; j++)
    {
      if (arr[j] > arr[j + 1])
      {
        int tmp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = tmp;
      }
    }
  }
}
int main()
{
  int arr[] = { 3,1,7,5,8,9,0,2,4,6 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  bubble_sort(arr, sz);
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

优化一下啊:

void bubble_sort(int arr[], int sz)//参数接收数组元素个数
{
  int i = 0;
  for (i = 0; i < sz - 1; i++)
  {
    int flag = 1;//假设这⼀趟已经有序了
    int j = 0;
    for (j = 0; j < sz - i - 1; j++)
    {
      if (arr[j] > arr[j + 1])
      {
        flag = 0;//发⽣交换就说明,⽆序
        int tmp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = tmp;
      }
    }
    if (flag == 1)//这⼀趟没交换就说明已经有序,后续⽆序排序了
      break;
  }
}
int main()
{
  int i = 0;
  int arr[] = { 3,1,7,5,8,9,0,2,4,6 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  bubble_sort(arr, sz);
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

5.二级指针

啥是二级指针?

指向一级指针的指针变量。

大概示意图如下:

6.指针数组

首先问个问题哈,指针数组是数组还是指针啊?

为了让大家好理解呢,我举个例子,好孩子是好还是孩子啊?好孩子也是孩子吧。

那么答案就很明确了,指针数组也是数组,只不过这个数组里放的是指针而已。

//指针数组
int main()
{
  int a = 1;
  int b = 2;
  int c = 3;
  int* pa = &a;
  int* pb = &b;
  int* pc = &c;
  int* arr[3] = { pa,pb,pc };
  int i = 0;
  for (i = 0; i < 3; i++)
  {
    printf("%d ", *arr[i]);
  }
  return 0;
}

大概示意图是这样滴:

7.指针数组模拟二维数组

指针数组比较典型的例子还得是模拟二维数组的这个代码了。

下面来进行代码展示:

//指针数组模拟二维数组
int main()
{
  int arr1[] = { 1,2,3,4,5 };
  int arr2[] = { 2,3,4,5,6 };
  int arr3[] = { 3,4,5,6,7 };
  int* arr[3] = { arr1,arr2,arr3 };
  int i = 0;
  int j = 0;
  for (i = 0; i < 3; i++)
  {
    for (j = 0; j < 5; j++)
    {
      printf("%d ", *(*(arr+i)+j));
    }
    printf("\n");
  }
  return 0;
}

当然,我这里用指针和数组下标还可以进行杂交处理一下,其实数组下标就是指针而已,不同的写法罢了:

当然了,为了同学们便于理解,我还是来画个图给大家瞅瞅:

相关文章
|
6月前
|
C语言
C语言(指针详解)重点笔记:指针易错点,都是精华
C语言(指针详解)重点笔记:指针易错点,都是精华
75 0
|
Serverless
C进阶 :征服指针之指针与数组强化笔试题练习(1)(上)
C进阶 :征服指针之指针与数组强化笔试题练习(1)
72 0
|
6月前
深度解析指针与数组:探索内存管理的艺术
深度解析指针与数组:探索内存管理的艺术
69 0
|
11月前
|
存储 程序员 C语言
指针一些关键知识点和难点的总结
指针一些关键知识点和难点的总结
77 0
|
存储 C语言 索引
从零开始教你拿捏指针---指针初阶
从零开始教你拿捏指针---指针初阶
64 0
C进阶 :征服指针之指针与数组强化笔试题练习(1)(下)
C进阶 :征服指针之指针与数组强化笔试题练习(1)(下)
52 0
C进阶:征服指针之指针与数组强化笔试题练习(2)
C进阶:征服指针之指针与数组强化笔试题练习(2)
72 0
C语言指针深度解剖
对C语言的指针进行深入学习
C语言指针深度解剖
|
存储 C语言 C++
《C和指针》读书笔记(第六章 指针)
《C和指针》读书笔记(第六章 指针)