C语言进阶第二课-----------指针的进阶----------升级版

简介: C语言进阶第二课-----------指针的进阶----------升级版

前提复习

  1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
  2. 指针(指针变量)的大小是固定的4/8个字节(32位平台/64位平台)。
  3. 指针是有类型,指针的类型决定了指针的±整数的步长,指针解引用操作的时候的权限。

 4.指针的运算。(数组元素指针相减等于两者之间的元素个数)

 5.内存单元是有编号的,编号 == 地址 ==指针

字符指针

用法一:

#include<stdio.h>
int main()
{
  char a = 'w';
  char* p = &a;
  return 0;
}

这里的地址使用很简单,下面还有一种用法

#include<stdio.h>
int main()
{
  const char* p = "abcdef";
  printf("%c", *p);
  printf("%s", p);
  return 0;
}

这里的p存储的是字符a的地址,我们可以想象成"abcdef"储存在一个数组中,传入p的值为数组名,也就是首元素的地址,就是a


还有一点需要注意的是"abcdef"是一个常量字符串,不能修改,但是会有些人通过*p来修改,这会造成程序崩溃,但是却不会报出警告,往往我们会加一个const 来把错误显示出来

指针数组

指针数组本质上就是一个数组,只是类型不一样而已

比如 字符数组–存放字符的数组

整形数组—存放整形的数组

指针数组—存放指针的数组,存放在数组中的元素都是指针类型

应用1:

#include<stdio.h>
int main()
{
  int arr[] = { 1,2,3,4,5,6 };
  int arr1[] = { 2,3,4,5,6,7 };
  int arr2[] = { 3,4,5,6,7,8 };
  int arr3[] = { 4,5,6,7,8,9 };
  int arr4[] = { 5,6,7,8,9,10 };
  int* arr5[] = { arr,arr1,arr2,arr3,arr4 };//指针数组
  int i = 0;
  for (i = 0; i < 5; i++)
  {
    int j = 0;
    for (j = 0; j < 6; j++)
    {
      printf("%d ", arr5[i][j]);
    }
    printf("\n");
  }
  return 0;
}

这种效果相当于写出了一个二维数组的效果

应用2:

```cpp
#include<stdio.h>
int main()
{
  char* arr[5] = { "hello bit", "hehe", "penggeC","nitejiuyeke", "C++" };
  int i = 0;
  for (i = 0; i < 5; i++)
  {
    printf("%s\n", arr[i]);
  }
  return 0;
}

这里的效果就相当于写成了一个字符串组成的数组,原理就是数组arr元素保存的是字符串首字符地址,通过遍历取出来,然后一一打印出来

数组指针

指针数组是存放指针的数组,

数组指针是啥呢?,

我们可以想象一下,字符指针就是指向字符的指针, 整形指针就是指向整形的指针,所以,数组指针就是指向数组的指针

#include<stdio.h>
int main()
{
  int a = 10;
  int* p = &a;
  char b = 'q';
  char* pr = &b;
  int arr[10];
  int* pa = arr;
  //数组名是首元素的地址
  //存在两个例外
  //sizeof (arr)  这里的arr表示的是整个数组,sizeof(数组名)计算的是整个数组的大小
  // &arr 取出的是整个数组的地址
  return 0;
}

//数组名是首元素的地址

//存在两个例外

//sizeof (arr) 这里的arr表示的是整个数组,sizeof(数组名)计算的是整个数组的大小

// &arr 取出的是整个数组的地址,如果从值的方向考虑就会发现arr和&arr的值是一样的

可以看出数组名和 &数组名的差别,指针类型决定了指针+1 加了几个字节

#include<stdio.h>
int main()
{
  int arr[5] = { 1,2,3,4,5 };
  int(*p)[5] = &arr;//数组指针,里面的大小必须写 !!!!
  char arr1[100] = "123456";
  char(*pa)[100] = &arr1;//告诉我们pa是一个指针,指向的是char类型的数组地址
  char* arr2[5];
  char* (*pa2)[5] = &arr2;//告诉我们pa2是一个指针,指向的是char*类型的数组地址
  printf("%p\n", p);
  printf("%p\n", arr);
  return 0;
}

数组指针的写法就是 类型 (p)[数组大小,为了好理解我们可以理解为 char p[10],如果写出这样就会变成指针数组了,为了区分开来,才加()区分,指针类似还是char* 没有改变,只是写法不同而已

#include<stdio.h>
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int(*pa)[10] = &arr;
  int* p = arr;
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d", (*pa)[i]);//如果使用pa[i] 等价于*(pa + i)
  }
  for (i = 0; i < 10; i++)
  {
    printf("%d", p[i]);
  }
  return 0;

输出的时候我们可以这样理解 arr[i] 可以访问元素, p = arr 所以p[i]可以访问元素

但是pa是数组指针 *pa可以消除& 得到arr , (*pa)[i]可以访问元素

一般我们使用数组指针是在二维数组使用的

在使用之前我们回忆一下一维数组传参给函数时

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

p[i] 等价于*(p + i), 所以打印地址一般用 arr + i 或者 &arr[i] 或者 &arr[0] + i

二维数组的应用

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

二维数组是一维数组的数组,我们可以想象一下二维数组arr里面的元素是一维数组,传参arr是首元素的地址,也就是把一维数组的整个地址传进去,

这里使用了数组指针pa ,如果要访问第二个元素可以写成 pa[1]

那我们就再扩展一下

int (*parr2)[10];
int (*parr3[10])[5];

第一行的意思就是,parr2是一个数组指针,指向的数组有10个元素,每个元素为整形

第二行代码的意思就是parr3是一个数组,进行存放数组指针的,可以存放10个数组指针,每个数组指针指向一个元素个数为5的数组,并且这个数组的元素类型为int


总结:这里我们要清楚数组指针的使用,及写法,比如 int (*pa)[10] = &arr

如果arr是一维数组,访问每个元素可以写成(pa)[i],不能写成pa[i],因为pa[i]

等价于(pa + i) ,如果arr为二维数组,我们可以写成pa[i][j]访问每一个元素

数组传参和指针传参

一维数组传参

,数组传参本质上是,传递了首元素的地址数组传参,形参也可以是指针

数组传参,形参接收,形参的形式可以写成arr[] 或者 arr[大小] 或者 指针变量

如果碰见指针传参指针,我们就要思考要使用哪级指针接收,上图就是二级指针接收arr2数组首元素的地址,arr2数组首元素也是存储地址的

二维数传参

一级指针传参

二级指针传参

int* arr[5] int ** p = arr ,这个也可以

总结:指针传参有很多种传入方法,但是传入的意思是一样的

函数指针

数组指针是指向数组的指针,存放的是整个数组的地址 ----&数组名

那么函数指针是指向函数的指针,存放的是函数的地址,

那怎么得到函数的地址呢?

#include<stdio.h>
int Add(int x, int y)
{
  return x + y;
}
int main()
{
  //&函数名就是函数的地址
  //函数名也是函数的地址
  //这两者没有区别
  printf("%p\n", &Add);
  printf("%p\n", Add);
  //函数指针
  int (*pA)(int, int) = Add;//意思就是pA为函数指针 ,指向的函数的类型为int 参数的类型也为int
  int (*pA1)(int, int) = &Add;
  printf("%p\n", pA);
  printf("%p\n", pA1);
  int tr = (*pA)(2, 3);// 解引用pA找到函数,并传入参数
  int re = pA(2, 3);
  printf("%d\n", tr);
  printf("%d\n", re);//这种写法是因为Add和&Add是一样的 ,我们调用函数是Add(2,3),所以pA存储Add,我们也可以使用pA(2,3)效果和调用函数是一样的
  return 0;
}

可以看出获取函数的地址有两种 &函数名或者函数名

我们调用函数除了使用函数名,还可以使用函数指针调用,使用函数指针调用,我们要熟悉函数调用的写法,Add(2,3),因为函数指针存储的是函数名 Add,所以通过函数指针调用函数,可以写成 pA(2,3),或者(*pA)(2,3)(可以理解为解引用找到函数并传参给函数)

从这个图中我们可以看出函数指针变量的类型

我们从这张图可以看出

代码1的意思就是 把0 强制类型转换成函数指针类型 ,并且解引用找到函数调用,这个函数没有参数

代码2 是一个函数声明,看到这里是不是很吃惊,那我们分析一下,

signal函数有两个参数,第一个参数的类型为int 第二个参数的类型是void()(int),因为不可能是函数调用,当我们吧函数去掉就会发现 void ()(int)是一个函数指针类型,如果我们换个类型就很好理解了 int signal( int, void(*)(int) ); 平时我们函数声明就是这样的,只是这个函数指针类型有写法有点特殊,但不改变这就是一个函数指针类型,

如果觉得这样写很麻烦,我们可以把这个函数指针类型重定义


typedef void(vi_int)(int);注意一下不要写成typedef void()(int) vi_int;这样是不行的

总结:这里我们要熟悉函数指针类型和指针传参 方式有很多但是意思是一样的

相关文章
|
8天前
|
搜索推荐 C语言
【排序算法】快速排序升级版--三路快排详解 + 实现(c语言)
本文介绍了快速排序的升级版——三路快排。传统快速排序在处理大量相同元素时效率较低,而三路快排通过将数组分为三部分(小于、等于、大于基准值)来优化这一问题。文章详细讲解了三路快排的实现步骤,并提供了完整的代码示例。
30 4
|
1月前
|
存储 C语言
【C语言篇】深入理解指针3(附转移表源码)
【C语言篇】深入理解指针3(附转移表源码)
36 1
|
24天前
|
C语言
【c语言】指针就该这么学(1)
本文详细介绍了C语言中的指针概念及其基本操作。首先通过生活中的例子解释了指针的概念,即内存地址。接着,文章逐步讲解了指针变量的定义、取地址操作符`&`、解引用操作符`*`、指针变量的大小以及不同类型的指针变量的意义。此外,还介绍了`const`修饰符在指针中的应用,指针的运算(包括指针加减整数、指针相减和指针的大小比较),以及野指针的概念和如何规避野指针。最后,通过具体的代码示例帮助读者更好地理解和掌握指针的使用方法。
45 0
|
23天前
|
C语言
【c语言】指针就该这么学(3)
本文介绍了C语言中的函数指针、typedef关键字及函数指针数组的概念与应用。首先讲解了函数指针的创建与使用,接着通过typedef简化复杂类型定义,最后探讨了函数指针数组及其在转移表中的应用,通过实例展示了如何利用这些特性实现更简洁高效的代码。
15 2
|
24天前
|
C语言
如何避免 C 语言中的野指针问题?
在C语言中,野指针是指向未知内存地址的指针,可能引发程序崩溃或数据损坏。避免野指针的方法包括:初始化指针为NULL、使用完毕后将指针置为NULL、检查指针是否为空以及合理管理动态分配的内存。
|
24天前
|
C语言
C语言:哪些情况下会出现野指针
C语言中,野指针是指指向未知地址的指针,通常由以下情况产生:1) 指针被声明但未初始化;2) 指针指向的内存已被释放或重新分配;3) 指针指向局部变量,而该变量已超出作用域。使用野指针可能导致程序崩溃或不可预测的行为。
|
30天前
|
存储 C语言
C语言32位或64位平台下指针的大小
在32位平台上,C语言中指针的大小通常为4字节;而在64位平台上,指针的大小通常为8字节。这反映了不同平台对内存地址空间的不同处理方式。
|
30天前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
30天前
|
存储 C语言
C语言指针与指针变量的区别指针
指针是C语言中的重要概念,用于存储内存地址。指针变量是一种特殊的变量,用于存放其他变量的内存地址,通过指针可以间接访问和修改该变量的值。指针与指针变量的主要区别在于:指针是一个泛指的概念,而指针变量是具体的实现形式。
|
1月前
|
C语言
C语言指针(3)
C语言指针(3)
11 1