指针的进阶(1)下

简介: 指针的进阶(1)下

         

4.  数组参数、指针参数

我们写代码的时候难免要把数组或者指针传给函数,那么函数的参数该如何设计呢?

4.1  一维数组传参

void test(int arr[])//ok?

{}

void test(int arr[10])//ok?

{}

void test(int* arr)//ok?

{}

void test2(int* arr[20])//ok?

{}

void test2(int* arr[])//ok?

{}

void test2(int** arr)//ok?

{}

int main()

{

   int arr[10] = { 0 };

   int* arr2[20] = { 0 };

   test(arr);

   test2(arr2);

}


4.2  二维数组传参

void test(int arr[3][5])//ok?

{}

void test(int arr[][])//ok?

{}

void test(int arr[][5])//ok?

{}

void test(int* arr)//ok?

{}

void test(int* arr[5])//ok?

{}

void test(int(*arr)[5])//ok?

{}

void test(int** arr)//ok?

{}

int main()

{

   int arr[3][5] = { 0 };

   test(arr);

}


4.3  一级指针传参

思考:当一个函数的参数部分为一级指针的时候,函数能接收什么参数?

答案是:①一级指针变量;②一维数组数组名;③变量地址。

比如:

void test1(int *p)
{}
//test1函数能接收什么参数?
void test2(char* p)
{}
//test2函数能接收什么参数?

void test1(int* p)
{}
void test2(char* p)
{}
int main()
{
  //test1函数能接收什么参数?
  int a = 10;
  int* pi = &a;
  int arr[5] = { 0 };
  test1(pi);//一级指针变量
  test1(arr);//一位数组数组名
  test1(&a);//变量地址
  //test2函数能接收什么参数?
  char ch = 'w';
  char* pc = &ch;
  char arr1[6] = { '\0' };
  test2(pc);
  test2(arr1);
  test2(&ch);
  return 0;
}

4.4  二级指针传参

思考:当函数的参数为二级指针的时候,函数能接收什么参数?

答案是:①二级指针变量;②指针数组数组名;③一级指针变量地址。

代码实例:

void test(char** p)
{ }
int main()
{
  char c = 'b';
  char* pc = &c;
  char** ppc = &pc;
  char* arr[10];
  test(&pc);//一级指针变量地址
  test(ppc);//二级指针变量
  test(arr);//指针数组数组名
  return 0;
}

5、函数指针

我们先打印一下函数的地址

#include<stdio.h>
int Add(int x, int y)
{
  return x + y;
}
int main()
{
  //打印函数地址
  printf("%p\n", &Add);
  printf("%p\n", Add);
  return 0;
}

运行结果:

从结果我们发现:取函数名和函数名,打印的地址一样。

结论:①取函数名和函数名,都是函数的地址。

那函数的地址是怎么保存的呢?

看下面代码:

#include<stdio.h>
int Add(int x, int y)
{
  return x + y;
}
int main()
{
  //数组指针-指向数组的指针
  int arr[10] = { 0 };
  int(*pa)[10] = &arr;
  //函数指针-指向函数的指针
  int (*pf)(int, int) = &Add;//pf 是一个存放函数地址的指针变量-函数指针
  int ret1 = pf(2, 3);//pf是函数地址,Add也是函数地址,所以不用解引用也可以
  int ret2 = (*pf)(2, 3);//写上*也可以,符合语法,但是需注意带()
  printf("%d %d\n", ret1, ret2);
  return 0;
}

类比数组指针,函数指针也是如此要注意带()保证*和变量pf先结合。

使用函数指针找到所指向的函数:

①直接写函数指针:因为函数指针变量存储的就是函数的地址,所以在使用的时候可以不解引用。

②函数指针解引用:解引用其实没必要,这样写就是方便理解。因为pf是指针,指针要找到他所指向的对象(对象函数名就是函数的地址),解引用更容易理解,符合语法的理解。(注意:优先级缘故,如果要解引用,一定要使用()把*和函数指针变量括起来。)

现在我们来看两段有趣的代码:

//代码1

(*(void (*)())0)();

//代码2

void (*signal(int , void(*)(int)))(int);

代码1的解读:

int main()
{
  ( *( void(*)() ) 0 )();
  //分析:
  // ①void(*)()-->函数指针类型,这个函数是无参的
  // ②()里面放类型-->强制类型转换
  // ③( void(*)() ) 0 -->把0强转成这种函数指针类型,
  // 就是0被当成了函数的地址
  // ④(*指针变量)-->解引用操作,找到0地址的函数
  // ⑤最后的()-->调用函数不用传参(因为强转的0函数类型是无参的)
  //
  //总结:该代码是一次函数调用,调用0地址处的一个函数
  //    首先代码中将0强转为类型为( void(*)() )的函数指针
  //      然后去调用0地址处的函数
  return 0;
}

破题点:从0出发,再开始分析。

代码2的解读:

//函数返回类型是函数指针
int main()
{
  void (*signal(int, void(*)(int))) (int);
  //分析:①signal没有与*结合,这不是指针
  //    ②signal后有括号()-->signal是函数名
  //    ③函数名括号()里-->函数参数
  //    第一个参数是int类型,第二个参数是函数指针类型
  //    ④语句最后有;没有函数体-->函数是声明
  //    ⑤函数的声明-->形式:函数返回类型 函数名 (形参列表);
  //    那剩下的void(*)(int)就是函数返回类型了
  //    
  //总结:该代码是一次函数的声明
  //    声明的函数名字叫signal
  //    signal函数的参数有2个,第一个是int类型,第二个是函数指针类型,
  //    该函数指针能够指向的那个函数的参数是int,返回类型是void。
  //    signal函数的返回类型是一个函数指针,
  //    该函数指针能够指向的那个函数的参数是int,返回类型是void。
  //我们能不能这么写?
  //void(*)(int) signal(int,void(*)(int));
  //答案是不能这么写,帮助理解可以这么写,但是语法上是错的
  //函数的返回类型是函数指针是,只能把函数名 (形参列表)移到*的后面。
  //优化:类型简化--->typedef
  //函数指针的重命名
  typedef void(*pf_t)(int);
  pf_t signal(int, pf_t);
  return 0;
}

破题点:从signal出发,再开始分析。

知识点:

1、函数返回类型是指针函数时的写法:当函数的返回类型是函数指针时,只能把函数名 (参数列表)移到返回类型这个函数指针的*的后面。

2、指针类型简化(重命名)--->typedef

       指针类型重命名,别名写在*的后面并且还要注意操作符的优先级(即*先与别名结合)

本次知识点总结:

1.  字符指针的两种使用:

       (1)一般用:让字符指针指向一个字符变量(字符数组),可通过解引用来访问这个字符变量(字符数组)。

       (2)还有一种使用方式:让字符指针指向一个常量字符串的首字符地址。

加油站:

       (1)常量字符串:①常量字符串不能被修改:用const修饰;②常量字符串出现在表达式中时,这个常量字符串的值是首字符的地址。

       (2)const修饰的常变量:不能被修改。

       (3)(常量字符串,不能被修改)C/C++会把常量字符串存储到单独一个内存区域,当有几个指针,指向同一个常量字符串的时候,他们实际会指向同一块内存。

       (4)但是用相同的常量字符串去初始化不同的数组的时候,就会开辟不同的内存块。

       (5)一个变量对应着一个唯一的空间。

2.  指针数组

指针数组-存放指针(地址)的数组

回顾数组的创建方式:

       type_t  arr_name  [const_n];

       //type_t:是数组的元素类型

       //arr_name:数组名

       //const_n:常量表达式,用来表示数组中元素个数,即数组的大小(长度)

指针数组的使用:一般是使用一维数组模拟二维数组

3.  数组指针

3.1  数组指针的定义

       数组指针-存放数组地址的指针-指向数组的指针

       如:int arr[10];

              int (*parr)[10]=&arr;//数组指针

对数组指针的理解:

       ①parr先和*结合,说明是一个指针,前面的int是所指向数组的类型,后面的[]是所指向数组的大小。

       ②注意:[]的优先级要高于*的,所以必须加上()来保证变量先和*结合。

       ③指针变量前的第一个*与变量结合表示它是指针,再往前所有的东西表示这个指针所指向对象的类型。

3.2  &数组名和数组名

       数组名-数组首元素的地址

       &数组名-数组的地址

       数组首元素的地址和数组的地址从值的角度来看是一样的,但是意义不一样。

加油站:

       (1)指针的类型决定了指针+-整数的步长,指针解引用操作的时候的权限。

       (2)存储一个数组所需的内存字节数:

               ①一维数组:总字节数=sizeof(类型)*元素个数

               ②二维数组:总字节数=sizeof(类型)*行数*列数

       (3)对二维数组数组名的加深理解:

               数组名是数组首元素的地址,首元素即第一行的地址;

               对于二维数组来说可以看成:一行一个元素(即一维数组),二维数组是一个一维组的数组

3.3  数组指针的使用-二维数组数组名做实参

4.  数组参数、指针参数

4.1  数组传参

       数组名作函数实参,地址传递

       形参可写为:

       (1)数组形式(注意类型一致,若要标注数组大小:数组大小一致)

               ①一维数组的大小可省略

               ②二维数组的大小:只能省略第一维[]的大小,第二维[]的不可省略。因为对一个维数组,可以不知道有多少行,但是必须知道一行多少元素(列),这样才方便计算。

       (2)指针形式

4.2  一级指针传参

       思考:当函数的参数为一级指针的时候,函数能接收什么参数?

       答案是:①一级指针变量;②一维数组数组名;③变量地址

4.3二级指针传参

       思考:当函数的参数为二级指针的时候,函数能接收什么参数?

       答案是:①二级指针变量;②数组指针数组名;③一级指针变量地址

5.函数指针

       结论:取函数名和函数名,都是函数的地址。

(1)函数指针怎么写?

       类比数组指针,函数指针也是要注意操作符的优先级,所以要带()保证*先和变量结合

(2)对函数指针的理解:变量先与*结合,说明变量是一个指针;然后前面的是所指向函数的返回类型,后面的()是所指向函数的参数列表。

(3)使用函数指针找到所指向的函数:

       ①直接写函数指针:因为函数指针变量存储的就是函数的地址(函数名也是函数的地址),所以在使用的时候可以不解引用。

       ②函数指针解引用:解引用其实是没必要的,这样写就是方便理解。因为变量是指针,指针要找到它所指向的对象(对象函数名就是函数的地址),解引用更容易理解,符合语法的理解。(注意:优先级缘故,如果要解引用,一定要使用()把*和指针变量括起来。)

加油站:

       ①函数返回类型是函数指针时的写法:当函数的返回类型是函数指针时,只能把函数名和(参数列表)移到返回类型这个函数指针的*的后面。

       ②指针类型简化--->typedef

       指针类型重命名,别名写在*的后面并且还要注意操作符的优先级(即*先和别名结合)。

指针进阶的主题并未结束,在下一次文章终结。

有什么不足希望大家指出,我会更加努力写出更好的文章。

相关文章
|
5月前
|
C语言
指针进阶(C语言终)
指针进阶(C语言终)
|
5月前
|
机器学习/深度学习 搜索推荐 算法
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
|
5月前
|
C语言
指针进阶(回调函数)(C语言)
指针进阶(回调函数)(C语言)
|
5月前
|
存储 C语言 C++
指针进阶(函数指针)(C语言)
指针进阶(函数指针)(C语言)
|
5月前
|
编译器 C语言
指针进阶(数组指针 )(C语言)
指针进阶(数组指针 )(C语言)
|
5月前
|
搜索推荐
指针进阶(2)
指针进阶(2)
47 4
|
5月前
指针进阶(3)
指针进阶(3)
37 1
|
5月前
|
C++
指针进阶(1)
指针进阶(1)
42 1
|
5月前
|
存储 安全 编译器
C++进阶之路:何为引用、内联函数、auto与指针空值nullptr关键字
C++进阶之路:何为引用、内联函数、auto与指针空值nullptr关键字
41 2
|
5月前
|
Java 程序员 Linux
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
45 0