【指针的进阶(1)】指针的类型、数组传参和指针传参

简介: 【指针的进阶(1)】指针的类型、数组传参和指针传参

一、指针是什么?

  • 含义:内存被划分为一个个小的内存单元,每个内存单元都有一个编号,编号也称为地址,地址在C语言中被称为指针。
    指针(地址)需要存储起来,存储在变量中,这个变量就被称为指针变量。
int a=10;
int* pa=&a;

比如要存储变量a到pa里,&a就是a的地址,pa就是指针变量,还要说明pa的类型是int*。

  • 指针的大小:在32位平台上是4个字节,在64位平台上是8个字节

二、字符指针

顾名思义是用来存放字符的地址

2.1 字符指针的两种使用方法

在初阶指针,学习了字符指针的一般使用方式:

#include<stdio.h>
//字符指针
int main()
{
  char ch= "w";
  char* pc =&ch;
  pc = "a";
  return 0;
}

方法思路:改变ch的字符,可以直接用ch来改,但是字符指针pc的好处就是可以代替ch去改,把ch的地址存到pc,pc就可以通过地址找到ch,改变ch的字符,有种借刀杀人的意思。

本章继续学习另一种使用方法

int main()
{
  //创建arr字符数组,用abcdef给arr初始化
  //相当于在内存中创建数组,里面放了[a b c d e f \0] 
  char arr[] = "abcdef";
  //在x86环境下,p指针是4个字节,abcdef是放不完的
  //不是把abcdef放进p指针
  //思路:给p一个地址,向后打印字符串
  //就是是把首元素a的地址赋给p,让p找到字符串的起始位置
  //注意:字符串是常量字符串,不能被改,在*左边加上const,限制p不能改字符串
  const char* p ="abcdef";
  //打印字符串,要指定格式%s,还要给p一个起始地址,从这个地址向后打印字符串
  printf("%s\n", p);
  //给p解引用,打印字符串的首字符a
  printf("%c", *p);
  return 0;
}

三、指针数组

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

类比:

  • 整型数组:存放整型的数组
  • 字符数组:存放字符的数组
int main()
{
  int arr[10];//存放整型数组
  int* arr[10];//存放整型指针数组
  char* arr[5];//一级字符指针数组
  char* arr[6];//二级字符指针的数组
  return 0;
}

四、数组指针

4.1数组指针的定义

是指针,是指向数组变量的指针,存放数组变量的地址的指针变量

类比:

  • 整型指针:指向整型变量的指针,存放整形变量的地址的指针变量
  • 字符指针:指向字符变量的指针,存放字符变量的地址的指针变量

指针数组和数组指针的区别

int* p1[10];

p1是数组,元素有10个,每个元素的类型是int*

int(* p2)[10];

p2和*结合,p2是指针,p2外边是数组int[10],说明p2指向的是数组,所以p2是数组指针变量

4.2 &数组名 VS 数组名

数组名的理解

int main()
{
  //arr和&arr[0]的地址一样
  //说明数组名是数组首元素的地址
  int arr[10] = { 0 };
  printf("%p\n", arr);
  printf("%p\n", &arr[0]);
  printf("%d\n", sizeof(arr));
  printf("%p\n", &arr);
  return 0;
}

两个例外

1.sizeof(数组名),这里的数组名不是数组首元素的地址, 而是代表整个数组,sizeof(数组名)计算整个数组的大小, 单位是字节

2.&数组名,这里的数组名表示整个数组,&数组名取出的是整个数组的地址 单纯打印出来和首元素地址一样,没有区别,但调试起来发现它们的类型不一样

验证第二点:

int main() 
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  printf("%p\n", arr);
  printf("%p\n", arr + 1);//跳过一个元素,4个字节
  printf("%p\n", &arr[0]);
  printf("%p\n", &arr[0] + 1);//跳过一个元素,4个字节
  printf("%p\n", &arr);
  printf("%p\n", &arr + 1);//跳过整个数组,40个字节
  return 0;
}

4.3 数组指针的使用方法

  1. 普通的二维数组传参
void print(int arr[4][5], int r, int c)//数组,行,列接收
{
  int i = 0;
  for (i = 0; i < r;i++)//第i行
  {
    int j = 0;
    for (j = 0; j < c; j++)//循环打印第i行的每个元素
    {
      printf("%d ", arr[i][j]);
    }
    printf("\n");//换行
  }
}
int main()
{
  int arr[4][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7, 4,5,6,7,8 };
  print(arr, 4, 5);
    return 0;
}
  1. 用数组指针传参

思路:

void print(int(*p)[5], int r, int c)//用数组指针,行,列接收
{
  int i = 0;
  for (i = 0; i < r; i++)//第i行
  {
    int j = 0;
    for (j = 0; j < c; j++)//循环打印第i行中的每个元素
    {
      //p是数组第一行的地址,相当于首元素的地址,p+i是找到数组第i行的地址
      //*(p+i) 是找到第i行中的每一个元素的地址
      //*(p+i)+j 是找到第i行第j列的地址
      //*(*(p + i) + j) 是给地址解引用,找到第i行第j列的那个元素
      printf("%d ", *(*(p + i) + j));
    }
    printf("\n");//换行
  }
}
int main()
{
  int arr[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
  print(arr,3,5);//把数组,行,列传过去
  return 0;
}

五、数组参数,指针参数

5.1 一维数组传参

  • 一维数组传参可以是数组,也可以是指针
  • 形式上两种都可以,但本质上数组传参就是指针传参,只不过写成数组更直观
void test1(int arr[5], int sz)
{
}
void test2(int* p, int sz)
{
}
int main()
{
  int arr[5] = { 0 };
  test1(arr,5);
  test2(arr, 5);
  return 0;
}

5.2二维数组传参

二维数组传参可以是数组,也可以是指针

void test3(char arr[3][5], int r, int c)//行可以省略,[3][5]可以写成[][5],但不能像数组指针那样写成[5]
{
}
void test4(char(*p)[5], int r, int c)//行可以省略,[3][5]可以写成[5]
{
}
int main()
{
  char arr[3][5] = { 0 };//char或int初始化都是0
  test3(arr, 3, 5);
  test4(arr, 3, 5);
  return 0;
}

需要注意的是:

可以用数组指针传参,也可以用数组传参,但是不能用指针数组传参

错误写法:用指针数组传参:void test (int* arr[5])

5.3一级指针传参

void print(int* p, int sz)
{
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", *(p+i));
  }
}
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int* p = arr;//把首元素的地址赋给指针p
  int sz = sizeof(arr) / sizeof(arr[0]);//计算数组大小
  print(p, sz);//传一级指针p和数组的大小给函数
  return 0;
}

思考:

当一个函数的的参数部分是一级指针时,函数可以接收什么参数?

void test(char* p)
{
}
int main()
{
  return 0;
}

思路: 传的实参类型要跟形参类型完全匹配上。

  1. char ch = '2'; //单引号里面只能放一个任何类型的字符
    test(&ch); //传字符变量的地址
  2. char* ptr = &ch;//把ch的地址取出来放进ptr里
    test(ptr);//ptr是char类型的,参数也用char类型的指针接收它
  3. char arr[] = "abcdef";//字符数组
    test(arr);//传字符数组的首元素地址,参数用char*类型接收

5.4二级指针传参

void test(int **ptr)//用二级指针接收
{
}
int main()
{
  int n = 10;
  int* p = &n;
  //把n的地址放进指针变量p里,p是一级指针
  int** p1 = &p;
  //对一级指针p取地址赋给p1,p1是二级指针
  test(p1);
  //第一种传参
  //传二级指针p1,因为p的地址赋给了p1
  test(&p);
  //第二种传参
  //传p的地址,因为p接收了n的地址,再传p的地址,&p就是二级指针
  return 0;
}

思考:

当一个函数的的参数部分是二级指针时,函数可以接收什么参数?

void test(char** ptr)
{
}
int main()
{
  char n = 'a';
  return 0;
}
  1. char* p = &n;
    test(&p); //把n的地址放进指针变量p里,p是一级指针,再传p的地址,&p就是二级指针
  2. char** p1 = &p;//对一级指针p取地址,p1是二级指针
    test(p1);//可以传二级指针p1, 因为p的地址赋给了p1
  3. char* arr[5];//arr本身是首元素地址,它的元素类型又是char*类型
    test(arr);//所以arr是二级指针,可以用arr传参
    总结
    指针的进阶第一章内容就写到这,如果对你有帮助的话,不妨来一个关注吧!
相关文章
|
13天前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
29 3
|
12天前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
25 2
|
21天前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
26 1
|
30天前
|
存储
如何使用指针数组来实现动态二维数组
指针数组可以用来实现动态二维数组。首先,定义一个指向指针的指针变量,并使用 `malloc` 为它分配内存,然后为每个子数组分配内存。通过这种方式,可以灵活地创建和管理不同大小的二维数组。
|
30天前
|
存储
如何通过指针数组来实现二维数组?
介绍了二维数组和指针数组的概念及其区别,详细讲解了如何使用指针数组模拟二维数组,包括定义与分配内存、访问和赋值元素、以及正确释放内存的步骤,适用于需要动态处理二维数据的场景。
|
30天前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
1月前
魔法指针 之 二级指针 指针数组
魔法指针 之 二级指针 指针数组
19 1
|
1月前
|
C语言
无头链表二级指针方式实现(C语言描述)
本文介绍了如何在C语言中使用二级指针实现无头链表,并提供了创建节点、插入、删除、查找、销毁链表等操作的函数实现,以及一个示例程序来演示这些操作。
22 0
|
2月前
|
存储 人工智能 C语言
C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
本文详细讲解了C语言中的指针,包括指针变量的定义与引用、指向数组及字符串的指针变量等。首先介绍了指针变量的基本概念和定义格式,随后通过多个示例展示了如何使用指针变量来操作普通变量、数组和字符串。文章还深入探讨了指向函数的指针变量以及指针数组的概念,并解释了空指针的意义和使用场景。通过丰富的代码示例和图形化展示,帮助读者更好地理解和掌握C语言中的指针知识。
|
3月前
|
C语言
【C初阶——指针5】鹏哥C语言系列文章,基本语法知识全面讲解——指针(5)
【C初阶——指针5】鹏哥C语言系列文章,基本语法知识全面讲解——指针(5)