c语言 指针集合

指针概念

  1. 什么是指针
    在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。
  2. 指针和内存的关系
    在这里插入图片描述
  3. 代码表示
#include<stdio.h>

int main()
{
    
    
    int a = 0x11223344;  // a申请一块内存空间 同时获得该内存空间的编号 
                        //内存空间用于存放a的数值0x11223344
    int* p = &a;        // 存放a的数值的内存空间编号存放在变量p
    return 0;
}

总结
指针存放的是指向空间的内存编号

指针类型集合

在这里插入图片描述

字符型指针

字符型指针两种用途

  1. 存放一个字符的地址
#include<stdio.h>

int main()
{
    
    
	char a = 'a';
	printf("%d", a);
	char* p = &a; //p存放的就是a的地址
	return 0;
}

在这里插入图片描述
具体内存存储方式 戳这里动态内存分配

  1. 存放一个字符常量数组
#include<stdio.h>


int main()
{
    
    
	char* p = "abcdef";
	printf("%c\n", *p); //打印结果可知 cha*p存放的任然是首地址 
	printf("%s\n", p);
	return 0;
}

注意

  1. 使用%s 打印传的首地址 碰到\0就停止 无需解应用
  2. 该定义的字符数组为 加了const修饰的常量数组
    我们在这里看一道百度的面试题
#include <stdio.h>
int main()
{
    
    
    char str1[] = "hello bit.";
    char str2[] = "hello bit.";
    char *str3 = "hello bit.";
    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;
}

结果
在这里插入图片描述
结果是显而易见的 作为const修饰的数组 他就成为一个常量,系统无需要为其开辟两个内存空间我们在vs2019 调试验证
在这里插入图片描述
str3 和str4虽然是指针 但是他们指向的地址却是一样的
可见 系统不会为相同的常量开辟两个空间。

整形指针

整形指针用来存放整形变量的地址的指针

#include<stdio.h>

int main()
{
    
    
	int i = 0x11223344;
	int* p = &i;       //将i的地址编号存放在p中
	return 0;
}

浮点数指针

浮点数使用方式同整形一致
但浮点数 存储方式与整形不同戳这里 浮点数动态内存分配

#include<stdio.h>

int main()
{
    
    
	float a = 3.14;
	float* p = &a;
	printf("%f", *p);
	return 0;
}

在这里插入图片描述

指针步长

指针步长与指针对应数据类型的大小一致
在这里插入图片描述
char*的指针走一步只能前进一个字节

数组指针

1.数组指针的基本使用

在这里插入图片描述

由打印结果 可知 数组指针的步长就是数组指针定义的数组 ·

2.数组指针在一维数组的使用
打印一个数组

#include<stdio.h>

int main()
{
    
    
	int arr[10] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	int(*parr)[10] = &arr;
	for (int i = 0; i < 10; i++)
	{
    
    
		printf("%d ", *((*parr) + i));
		printf("%d ", parr[0][i]);
		printf("%d ", (*parr)[i]);
	}
	return 0;
}

我们在对arr和&arr 进行总结
在这里插入图片描述
接下来我们解读 parr *parr arr &arr 内存存放 以及 内存编号
在这里插入图片描述

  1. *((*parr)+i))
    因为parr 存放的是整个arr的地址 对*parr解应用可得 *(arr+i)
    arr 存放的首元素地址 相当于 &arr[0]
    给arr 加i 就跳过i个元素 得到第0+i个元素的地址
    对arr+i解引用 得到arr[0+i]的值
  2. parr[0][i]
    parr[0]=> parr指向了第0行的所有元素 =>对parr解引用就得到了第0行所有元素=>(*(parr+0)<==>arr)=>第0行的arr[i]元素。
  3. printf("%d ", (*parr)[i])
    *(parr+0)<==>指针parr指向arr的0行

注意:

  1. 在使用数组指针时 [ ]中的数表示指针指向数组的的个数 不可空置
  2. parr 可以看做arr的二阶的指针
  3. 数组指针在一维数组中无明显效果 不建议在一维数组中使用
    3.数组指针在二维数组中的使用
    e.g 使用数组指针打印二维数组

void print(int(*parr)[5], int r, int c)
{
    
    
	for (int i = 0; i < r; i++)
	{
    
    
		for (int j = 0; j < c; j++)
		{
    
    
			printf("%d ", *(*(parr + i) + j));
		}
		printf("\n");
	}
}

int main()
{
    
    
	int arr[3][5] = {
    
     {
    
    1,2,3,4 },{
    
    2,3,4,5,6},{
    
    3,4,5,6,7} };
	print(&arr, 3, 5);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

函数指针

举例使用

#include<stdio.h>
void print(int x)
{
    
    
	printf("%d", x);
}

int main()
{
    
    
	int i = 12;
	print(i);
	void (*pf)(int) = &print;
	return 0;

在这里插入图片描述
e.g 2

#include<stdio.h>

int Add(int x, int y)
{
    
    
	return x + y;
}

int main()
{
    
    
	int i = 9;
	int k = 10;
	int (*pf)(int, int) = &Add;
	int ret=(*pf)(i, k);
	printf("%d", ret);
		return 0;
}

上例中 int (*pf)(int, int) = &Add;中* 不可少 但 int ret=(*pf)(i, k); 但这个却可以改成int ret=(pf)(i, k);甚至可以加任意*号 这是为什么呢
我们观察函数地址以及内存存放
在这里插入图片描述

我们发现 在vs2019编译器中函数的地址没有放在函数指针里面
在这里插入图片描述
我们在调试反汇编可看出 函数指针存的地址需要跳到另一个地址处 该地址存放着函数的指针 所以 函数指针很不安全 使用过程应该小心
接下来 我们研究两个经典案例

  1. 例1
(*(void (*)())0)();//如何解读??

在这里插入图片描述
然后在调用该函数 及是本行代码的旨意。
总结: 调用0地址处函数 该函数无参 返回值为void。

  1. 例2
void (*signal(int , void(*)(int)))(int);

在这里插入图片描述
在这里插入图片描述
去掉函数传参部分 就是函数返回类型 可见 函数返回类型任然是函数指针。
那么我们怎么简化这个函数声明呢。

我们可先定义一个 指针类型 pf

typedef void(*pf)(int);

注 : pf 不可写在外面 这是函数指针定义规则
那么

pf<==>void(* ) ( int );
pf signal(int, pf);

注意:

  1. 在vs编译器中函数指针存放的不是函数的地址,
  2. 使用函数指针时 其安全性有待提高。
  3. typedef 定义的函数指针 注意其书写格式

数组指针概念

数组元素全为指针变量的数组称为指针数组,指针数组中的元素都必须具有相同的存储类型、指向相同数据类型的指针变量
在这里插入图片描述

数组指针和指针的关系

在这里插入图片描述
可以得出结论 指针数组是有一个个指针构成的 将函数指针存放在数组中就可得到函数数组。

一般函数指针

在这里插入图片描述

指针数组,含有10个指针元素 也就是说每一个元素都是指针。先是解析[]表示它是一个数组,然后*表示指针,int表示为int型指针m 即表示定义一个指针数组,含有10个int类型指针元素。

可以这样初始化:

 for(int i=0; i<10;i++)

{
    
    
     p[0] = &i;  // 每个元素都是指针
}
  1. 与int * q
    进行对比 int * q ; //表示 q是指向 int的一个指针,即只是一个指针。但是如果 q 指向一个数组的首地址,那么可以用 q[] 这样的形式来访问数组的元素。

如下:

    int students[] = {
    
     101, 102, 103, 104, 105, 0, 0, 0, 0, 0, 201, 202,203, 204, 0, 0, 0, 0, 0, 0, 301, 302, 303, 0, 0, 0, 0, 0, 0, 0 };

 int * q = students;


3、对上面进行总结

指针是一个指向int的地址,   数值指针 是一个指向int* 值

辨析数组指针与指针数组 阿里面试题

在这里插入图片描述

#include<stdio.h>


int main()
{
    
    
	int i = 2;
	int a[4][4] = {
    
     0 };
	int* p;
	int* q[4];
     //p = a; 
	q[i] = a[i];
	p = q[i];  
	p = &a[2][1];

	return 0;
}

因为a是错误选项 所以将a注释后 调试观察
在这里插入图片描述
p作为一个普通指针 不能用二维数组对其赋值 应使用数组指针
在这里插入图片描述
可见此题a错误。

函数指针数组

  1. 函数指针数组:函数指针的一个用法出现在菜单驱动系统中。例如程序可以提示用户输入一个整数值来选择菜单中的一个选项。用户的选择可以做函数指针数组的下标,而数组中的指针可以用来调用函数
    举例:
#include "stdio.h"

void fun1(void)
{
    
    
    printf("fun1 \r\n");    
} 
void fun2(void)
{
    
    
    printf("fun2 \r\n");    
}
void fun3(void)
{
    
    
    printf("fun3 \r\n");    
}

int main()
{
    
        
    int i=0;
    void (*p1)(void)=NULL;
    void (*p2)(void)=NULL;
    void (*p3)(void)=NULL;    
    void(*p_arry[3])(void)={
    
    NULL,NULL,NULL};
    p1=fun1;
    p2=fun2;
    p3=fun3;
    p_arry[0]=p1;
    p_arry[1]=p2;
    p_arry[2]=p3;
    for(i=0;i<3;i++)
    {
    
    
        (*(p_arry+i))();
    }
    printf("*************************\r\n");
    p1();
    p2();
    p3();
    return 0;
 }

2. 函数指针数组是一个存放函数指针的数组 。本质也是数组

猜你喜欢

转载自blog.csdn.net/qq_45849625/article/details/114375756
今日推荐