当前位置: 首页 > article >正文

指针的进阶(提高篇)

序言:

前面概要:

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

1.字符指针 

一般情况有:

int main()
{
    char ch = 'a';
    char *pc = &ch;
    *pc = 'a';
    return 0;
}

还有这种:

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

 这里并不是把abcdef这个字符串放到字符指针pc里,而是将字符串的首地址放到字符指针pc里

ba256783b30241199ea3391ace5ecf3f.png

#include <stdio.h>
int main()
{
	const char str1[] = "abcdef";
	const char str2[] = "abcdef";
	const char* str3 = "abcdef";
	const char* str4 = "abcdef";
	if (str1 == str2)
		printf("str1 == str2\n");
	else
		printf("str1 != str2\n");

	if (str3 == str4)
		printf("str3 == str4\n");
	else
		printf("str3 != str4\n");

	return 0;
}

 str1[]创建了一个数组,存放了 a b c d e f \0

 str2[]创建了一个数组,存放了 a b c d e f \0

创建了2个数组,所以它们的首地址的地址是不一样的。

这里str1和str2指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当 几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。

const char* str3 = "abcdef";

是一个常量字符串,是无法改变的,这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域当几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。所以结果应该是:

8a956b582f0f419a971cc8398404848f.png

所以str1和str2不同,str3和str4不同。

 2.指针数组

指针数组是一个存放指针的数组。
int* arr1[10]; //存放整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组
#include<stdio.h>
int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 3,4,5,6,7 };
	int arr3[5] = { 5,6,7,8,9 };
	int* pc[3] = { arr1,arr2,arr3 };  //将arr1,arr2,arr3的首地址存放在指针数组里
	int i = 0;
	for (i = 0; i < 3; i++)	
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
		    printf("%d", *(pc[i] + j));//通过pc[i] + j 可以访问到某个数组里的某个数据,这就成了二维数组
			//printf("%d", pc[i][j]);这两种打印结果是等价的
		}
		printf("\n");
	}
	return 0;
}

3.数组指针 

1.定义

数组指针是指针?还是数组?
答案是:指针。
那数组指针应该是:能够指向数组的指针。
例如:下面p1 和p2哪个是数组指针,哪个是指针数组?
int *p1[10];
int (*p2)[10];
注:
int (*p1)[10];
解释:p1先和*结合,说明p1是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p1是一个 指针,指向一个数组,叫指针数组。
这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p2先和*结合。p2是指针,指向数组,所以int(*p2)[10]是数组指针 。
#include<stdio.h>
int main()
{
	int a = 10;
	int* p = &a;
	int arr[10] = { 0 };

	printf("%p\n", arr);         //类型:int*
	printf("%p\n", arr+1);

	printf("%p\n", &arr[0]);     //类型:int*
	printf("%p\n", &arr[0]+1);
	
	printf("%p\n", &arr);        //类型:int(*)[10]
	printf("%p\n", &arr+1);

	return 0;
}

3fed27091fc5413a904256df27367eb8.png

 可以看出arr和&arr[0]的打印结果一样的,也说明了数组名是首元素的地址。

根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是意义应该不一样的。
实际上: &arr 表示的是 数组的地址,而不是数组首元素的地址。
本例中 &arr 的类型是: int(*)[10] ,是一种数组指针类型
数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40.

2.&数组名   和   数组名    的区别 

数组名该怎么样理解?

通常情况下我们说的数组名都是数组首元素的地址。

但是有两个例外:

1.sizeod(数组名),这里的数组名表示整个数组,sizeof(数组名)是计算的整个数组的大小。

2.&数组名,这里的数组名是表示整个数组,&数组名,取出的是整个数组的地址。

3.数组指针的使用

数组指针指向的是数组,那数组指针中存放的应该是数组的地址。
#include <stdio.h>
int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,0};
    int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p
    return 0;
}
int arr [ 5 ];                 //整型数组    数组5个元素
int * parr1 [ 10 ];         //指针数组,数组10个元素,每个元素是int*类型
int ( * parr2 )[ 10 ];       //parr2是数组指针,该指针指向一个数组,数组是10个元素,每个元素是int类型的
int ( * parr3 [ 10 ])[ 5 ];   //parr3是指针数组,数组10个元素,数组的每个元素类型是int(*)[5]
(判断该类型就把该名称去掉)

4.数组传参,指针传参

一维数组传参 :

#include <stdio.h>
void test(int arr[])          //ok?  可以   传的数组参数,数组接收
{}
void test(int arr[10])        //ok?  可以    数组只是指定了10个元素
{}
void test(int *arr)           //ok?  可以   数组传参可以用数组首元素地址
{}
void test2(int *arr[20])      //ok?  可以    形参写成数组也可以
{}
void test2(int **arr)         //ok?  可以     arr2是int*类型,传int **arr也可以
{}
int main()
{
 int arr[10] = {0};
int *arr2[20] = {0};
 test(arr);
 test2(arr2);
}

二维数组传参:

void test(int arr[3][5])//ok?      可以
{}
void test(int arr[][])//ok?         不可以
{}
void test(int arr[][5])//ok?      可以
{}
//二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
void test(int *arr)//ok?       不可以,数组3行5列,数组名是首元素的地址
{}
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);
}
//传参得保证和要传的类型一样

指针传参

#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;
}
//二级指针
#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;
}

 5.函数指针

int  Add(int a,int b)
{
  return a+b;
}
int main()
{
  int (*pf)(int ,int)=&Add;
  int (*pf)(int ,int)=Add;
   return 0;
}
//&函数名和函数名都是函数的地址
//pf是一个存放函数的指针变量——函数指针

 #指针数组,数组指针,函数指针的区分

int main()
{
//指针数组    本质上是数组,里面存放的是指针
 char* ch[5]
int arr[10]={0};
//数组指针   本质上是指针,存的是地址
int (*pa)[5]=&arr;
//pf是函数指针
int my_strlen(const  char *str)
{
return 0;
}
int (*pf)(const char*)=&my_strlen;
}

 6函数指针数组

是用来存放  函数指针  的数组

int main()
{

//pf是函数指针
int my_strlen(const  char *str)
{
return 0;
}
int (*pf)(const char*)=&my_strlen;
//函数指针数组,那么在函数指针上加个数组呗!
int (*pfA[5])(const char*)={&my_strlen};
}

那么能干什么???

下面是一个简易的计算机小代码:

#include <stdio.h>
int add(int a, int b)
{
 return a + b;
}
int sub(int a, int b)
{
 return a - b;
}
int mul(int a, int b)
{
 return a*b;
}
int div(int a, int b)
{
 return a / b;
}
int main()
{
 int x, y;
 int input = 1;
    int ret = 0;
    do
   {
        printf( "*************************\n" );
        printf( " 1:add           2:sub \n" );
        printf( " 3:mul           4:div \n" );
        printf( "*************************\n" );
        printf( "请选择:" );
        scanf( "%d", &input);
        switch (input)
       {
 case 1:
              printf( "输入操作数:" );
              scanf( "%d %d", &x, &y);
              ret = add(x, y);
              printf( "ret = %d\n", ret);
              break;
        case 2:
              printf( "输入操作数:" );
              scanf( "%d %d", &x, &y);
              ret = sub(x, y);
              printf( "ret = %d\n", ret);
              break;
        case 3:
              printf( "输入操作数:" );
              scanf( "%d %d", &x, &y);
              ret = mul(x, y);
              printf( "ret = %d\n", ret);
              break;
        case 4:
              printf( "输入操作数:" );
              scanf( "%d %d", &x, &y);
              ret = div(x, y);
              printf( "ret = %d\n", ret);
              break;
        case 0:
                printf("退出程序\n");
 breark;
        default:
              printf( "选择错误\n" );
              break;
       }
 } while (input);
    
    return 0;
}

用函数指针数组改一下

#include <stdio.h>
int add(int a, int b)
{
           return a + b;
}
int sub(int a, int b)
{
           return a - b;
}
int mul(int a, int b)
{
           return a*b;
}
int div(int a, int b)
{
           return a / b;
}
int main()
{
int x, y;
     int input = 1;
     int ret = 0;
//从这开始变化
     int(*p[5])(int x, int y) = { 0, add, sub, mul, div };
 //这个函数指针数组就像跳转一样就被称为  转移表。
     while (input)
     {
          printf( "*************************\n" );
          printf( " 1:add           2:sub \n" );
          printf( " 3:mul           4:div \n" );
          printf( "*************************\n" );
          printf( "请选择:" );
      scanf( "%d", &input);
          if ((input <= 4 && input >= 1))
         {
          printf( "输入操作数:" );
              scanf( "%d %d", &x, &y);
              ret = (*p[input])(x, y);
         }
          else
               printf( "输入有误\n" );
          printf( "ret = %d\n", ret);
     }
      return 0;
}

这样就不需要do while循环中的一直case!

7指向函数指针数组的指针

int main()
{
 int arr[10];  //整型数组
 int  (*pa)[10]=&arr;  

  int (*pf[5])(int ,int);
  int(*(*ppf)[5](int ,int)=&pf;
//pf是函数指针数组,&pf后,ppf再指向它,那ppf就是指向函数指针数组的指针
}

8回调函数

回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另一个 函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

 

void calc(int (*pf)(int ,int))

{
  int x = 0;
  int y = 0;
  int ret = 0;
  printf("请输入两个操作数:");
  scanf( "%d %d", &x, &y);
  ret = pf(x, y);
  printf( "%d\n", ret);
}

int main()


{
 int x, y;
 int input = 1;
    int ret = 0;
    do
   {
        printf( "*************************\n" );
        printf( " 1:add           2:sub \n" );
        printf( " 3:mul           4:div \n" );
        printf( "*************************\n" );
        printf( "请选择:" );
        scanf( "%d", &input);
        switch (input)
       {
 case 1:
              calc(Add);
        case 2:
              calc(Sub);
              break;
        case 3:
             calc(Mul);
              break;
        case 4:
             calc(Div);
              break;
        case 0:
                printf("退出程序\n");
 breark;
        default:
              printf( "选择错误\n" );
              break;
       }
 } while (input);
    
    return 0;
}

 通过定义一个calc函数,把AddSubMulDiv的函数的地址作为参数传给calc函数,去调用这些函数就是回调函数!


http://www.kler.cn/a/570040.html

相关文章:

  • 11.字符函数和字符串函数
  • js的简单介绍
  • 算法-回溯篇03-电话号码的字母组合
  • codewave初识
  • 一文讲清楚 MySQL 事务隔离级别和实现原理
  • Linux查看TP6 command定时任务并重启
  • CSS - 妙用Sass
  • shell脚本编程实践第6天
  • 高性能采集服务上线回顾
  • React 各模块相关全面面试及答案解析
  • 毓恬冠佳即将登陆资本市场,深耕汽车天窗领域,引领行业创新发展
  • [MySQL初阶]MySQL(1)MySQL的理解、库的操作、表的操作
  • MySQL深分页如何优化?
  • IDEA 2025最新版2024.3.3软件安装、插件安装、语言设置
  • c语言中return 数字代表的含义
  • 探秘基带算法:从原理到5G时代的通信变革【一】引言
  • 第三百七十二节 JavaFX教程 - JavaFX HTMLEditor
  • 【HDLbits--FSM续(二)】
  • 搭建iOS逆向开发环境 (下) - 越狱设备与高级工具配置
  • 蓝桥杯4T平台(串口控制LD状态)