手把手教你玩转指针之-----指针进阶(1)

1.初级指针的复习

首先在初级指针时便向大家介绍了指针的概念:

  1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。

    在图片中&a是a的地址,变量pa就是储存地址的指针变量
  1. 指针的大小是固定的4/8个字节(32位平台/64位平台)。
    在这里插入图片描述
  1. 指针是有类型,指针的类型决定了指针的±整数的步长,指针解引用操作的时候的权限。
  2. 指针的运算。

2,字符指针

在指针的类型中我们知道有一种指针类型为字符指针 char* ;
在平常时,我们通常是将它指向一个字符变量。
代码如下:

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

今天我要介绍的另一种用,将它指向一个字符数组
代码如下:

#include<stdio.h>
int main()
{
  char* pstr = "hello world";//为了代码的严谨性因改为const char* pstr = "hello world";
 //这里是把一个字符串放到pstr指针变量里了吗?
 //在这里字符串"hello world"相当一个字符串表达式,结果为字符串首元素的地址,
 //因此代码const char* pstr = "hello world";相当于是把字符串首元素的地址赋值给了字符指针pstr
 printf("%s\n", pstr);
 return 0;
}

注:在上述代码中字符串"hello world"为常量字符串(常量字符串中的任何值都不能被改变),因此为了代码的严谨性,上述代码
char
pstr = “hello world”;因该为 const char
pstr = “hello world”;**

.
.
例题:

#include <stdio.h>
int main()
{
 char str1[] = "hello bit.";
 char str2[] = "hello bit.";
 const char *str3 = "hello bit.";
 const char *str4 = "hello bit.";
 if(str1 ==str2)
 printf("str1 and str2 are same\n");
 else
 printf("str1 and str2 are not same\n");
 
 if(str3 ==str4)
 printf("str3 and str4 are same\n");
 else
 printf("str3 and str4 are not same\n");
 
 return 0;
}

这里最终输出的是:

在这里我们分析。在定义与初始化时,内存就给数组str1,和str2分别分配好了内存空间。因此数组str1和str2的首元素地址时不同的。
而由于指针变量str3和str4都是指向了同一个常量字符串,而又由于常量字符串不可被改变的特性,使同一常量字符串的地址相等,所以str3与str4相等都是指向的同一地址。
如图:
在这里插入图片描述
因此输出结果为:
str1 and str2 are not same
str3 and str4 are same

3,数组指针

在学习此知识时我们可以通过类比。
类比:

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

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

下面代码哪个是数组指针?

int *p1[10];
int (*p2)[10];

解释:

int (p)[10];
//解释:p先和
结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指
向一个数组,叫数组指针。 //这里要注意:[ ]的优先级要高于号的,所以必须加上()来保证p先和结合。

此时大家可能在疑惑要如何获得数组的地址?
这是就不得不提出数组名不表示数组首元素地址的两种特殊情况了。

3.1&数组名VS数组名

这里我们通过这个例子来说明。

int arr[10];

arr和&arr分别是啥?
,
,
1. arr:

在正常情况下数组名arr表示的是数组首元素的地址,
但是当出现sizeof(数组名)时,这里的数组名不是数组首元素的地址,数组名表示整个数组,因此此时sizeof(数组名)表示的是,整个数组的大小。

2…&arr:

&数组名,这里数组名表示的是整个数组,因此&数组名表示的是整个数组的地址

验证:
在这里插入图片描述

从图片中我们可以看出arr,&arr[0],&arr打印的地址值是一样的,但是我们可以通过调试时的监视中可以看出arr,&arr[0],&arr的类型不同。
arr和&arr[0]都是整形指针。
&arr是数组指针

*注:&arr的指针类型为int(*)[4]。int[4]是错误的写法

而我们还知道当指针的类型不同时,一些相同的操作是由不同的结果的。
如图:
在这里插入图片描述

从上图我们可以看出arr+1与&arr+1都比原来的大4,而&arr+1比原来大16。

因此我们可以这样给数组指针初始化:

#include<stdio.h>
int main()
{
	int arr[4] = { 1,2,3,4 };
	int(*p)[4] = &arr;
	return 0;
}

3.2数组指针的使用

3.2.1一维数组时:

代码:

#include <stdio.h>
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9,0};
 int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p
 //但是我们一般很少这样写代码
 for(int i=0;i<10;i++)
 {
   printf("%d",(*p)[i]);
 }
 return 0;
}

3.2.2二维数组时:

#include <stdio.h>

void print_arr2(int (*arr)[5], int row, int col)
{
 int i = 0;
 for(i=0; i<row; i++)
 {
 for(j=0; j<col; j++)
 {
 printf("%d ", arr[i][j]);
 }
 printf("\n");
 }
}
int main()
{
 int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};
 print_arr1(arr, 3, 5);
 //二维数组的每一行可以理解为二维数组的一个元素
 //每一行又是一个一维数组
// 所以,二维数组其实是一维数组的数组
 //数组名arr,表示首元素的地址
 //但是二维数组的首元素是二维数组的第一行
 //所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
 //可以数组指针来接收
 print_arr2(arr, 3, 5);
 return 0;
}

4.数组参数,指针参数

4.1一维数组传参

参数可以为数组,也可以为指针

#include<stdio.h>
void test1(int arr[5],int row)//参数为数组
{
    
}
void test2(int *p,int row)//参数为指针
{

}
int main()
{
   int arr[5]={0};
   test1(arr,5);
   test2(arr,5);
   return 0;
}

4.2二维数组传参

形参的部分可以是数组,也可以是指针

#include<stdio.h>
void test1(int arr[3][5],int row)//参数为数组
{
    
}
void test2(int (*p)[5],int row)//参数为数组指针
{

}
int main()
{
   int arr[3][5]={0};
   test1(arr,3,5);
   test2(arr,3,5);
   return 0;
}

4.3一级指针传参

#include <stdio.h>
void print(int *p, int sz)
{
 int i = 0;
 for(i=0; i<sz; i++)
 {
 printf("%d\n", *(p+i));
 }
}
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9};
 int *p = arr;
 int sz = sizeof(arr)/sizeof(arr[0]);
 //一级指针p,传给函数
 print(p, sz);
 return 0;
}

思考:

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

void test(char *p)
{

}
char ch='2';
char *ptr=&ch;
char arr[]="abcdef";
test(&ch);//变量的地址
test(ptr);//一级指针
test(arr);//一维数组

4.4二级指针传参

#include <stdio.h>
void test(int** ptr)
{
 printf("num = %d\n", **ptr); 
}
int main()
{
 int n = 10;
 int*p = &n;
 int **pp = &p;
 test(pp);
 test(&p);
 return 0;
}

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

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>
void test()
{
 printf("hehe\n");
}
int main()
{
 printf("%p\n", test);//函数名为函数的地址
 printf("%p\n", &test);//&函数名 也是函数的地址
 return 0;
}

二。函数指针的类型:
例子:指向函数int add(int x,int y){return x+y;}的函数指针类型:

int (p)(int,int);

在这里插入图片描述

三。创建函数指针并初始化与调用
例子:

#include<stdio.h>
int add(int x,int y)
{
   return x+y;
}
int main()
{
  int (*p)(int,int)=&add;//创建并初始化函数指针
  int m=p(1,2);//调用函数指针
  return 0; 
}

与函数指针有关的两段代码:
一:

(*(void (*)())0)();//这段代码是干什么的?

解释:我们从向外看(void (*)())0。是把0强制类型转化为返回值,函数参数都为空的函数指针类型,因此(void (*)( ) )0为函数指针变量。然后(*((void (*) ())0)()为调用函数指针(void (*)())0

二:

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

解释:signal是一个函数声明。
signal函数有两个参数一个是int类型,第二个为void (*)(int)函数指针类型
该函数指针指向的函数有一个int类型的参数,返回类型是void。
signal函数返回类型也是void(*)(int)函数指针类型,该函数指针指向的函数有一个int类型的参数,返回类型是void。

.
.
.
我们可以看出第二个代码十分复杂,这里我们可以用typedef简化代码

typedef void (*p)(int);

注:这里p必须放在括号里面星号(*)的后面。

简化后的代码:

p (*signal(int,p);



今天就这里了,各位看官 欲知后事如何,且听下回分解。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

慢慢走,慢慢等

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值