C语言:指针详解

个人博客网址:https://ljsblog.com

指针(七)

指针

指针就是变量,用来存放内存单元的地址
存放在指针中的值都被当成地址处理
指针在32位平台大小是4个字节,指针在64位平台大小是8个字节

printf("%d\n",sizeof(char*));//4
printf("%d\n",sizeof(short*));//4
printf("%d\n",sizeof(int*));//4
printf("%d\n",sizeof(float*));//4
printf("%d\n",sizeof(double*));//4

指针类型

一.指针类型决定了指针进行解引用操作的时候,能访问空间的大小。
例:

int* p;    *p能访问4个字节
char* p;   *p能访问1个字节 
double* p; *p能访问8个字节

二.指针类型决定了指针向前或者向后走一步的距离。
例:

int a=0;
int *pi=&a;
char *pc=&a;
printf("%p\n",pi);//010FFEC4
printf("%p\n",pi+1);//010FFEC8
printf("%p\n",pc);//010FFEC4
printf("%p\n",pc+1);//010FFEC5
int* p;    p+1-->4 
char* p;   p+1-->1
double* p; p+1-->8

  • void* 类型的指针可以接受任意类型的地址
  • void* 类型的指针不能进行解引用操作
  • void* 类型的指针不能进行加减整数的操作

野指针

野指针就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)

野指针由来

一.局部变量没有初始化

//错误示例
#include<stdio.h>
int main()
{
	int *p;//指针变量如果未初始化,其值是随机的
	*p=20;
	return 0;
}

二.指针越界访问

//错误示例
#include <stdio.h>
int main()
{
	int arr[10]={0};
	int i=0;
	int* p=arr;
	for(i=0;i<11;i++)
	{
		*(p+i)=i;//当指针指向超出arr范围时,p就是野指针
	}
    return 0;
}

三.指针指向的空间释放

//错误示例
#include <stdio.h>
int* Wild_Pointer()
{
    int a=1;
    return &a;
}
int main()
{
    int *p=Wild_Pointer();//空间已经被释放了
    *p=10;
    return 0;
}
如何规避野指针
  1. 指针初始化
  2. 小心指针越界
  3. 指针指向空间释放即使置NULL(NULL和0的值都是一样的,但是为了目的和用途及容易识别的原因,NULL用于指针和对象,0用于数值)
  4. 指针使用之前检查有效性

指针的运算

指针加减整数
#include <stdio.h>
int main()
{
    int arr[]={1,2,3,4,5,6,7,8,9,10};
    int *p=arr;
    int i=0;
    int sz=sizeof(arr)/sizeof(arr[0]);
    for(i=0;i<sz;i++)
    {
        printf("a[%d]=%d\n",i,*(p+i));
    }
    return 0;
}
/*结果为
a[0]=1
a[1]=2
a[2]=3
a[3]=4
a[4]=5
a[5]=6
a[6]=7
a[7]=8
a[8]=9
a[9]=10
*/
指针减指针

指针减指针时,两个指针必须在同一个空间内,例如;在一个数组内

#include <stdio.h>
int main()
{
    int arr[]={1,2,3,4,5,6,7,8,9,10};
    int *p=arr;
    printf("%d\n",&arr[9]-&arr[0]);//9
    return 0;
}
指针的关系运算
#include <stdio.h>
int main()
{
    int arr[10]={1,2,3,4,5,6,7,8,9,10};
    int *p=arr;
	for(p=&arr[10];p>&arr[0];)
	{
		*--p=0;
	}
    return 0;
}

字符指针

一般用法

#include <stdio.h>
int main()
{
    char a='m';
    char* p=&a;
    *p='y';
    printf("%c\n",a);//y
    return 0;
}

另一种用法

#include <stdio.h>
int main()
{
    char* p="myblog";
    //这里是将常量字符串"myblog"首字符的地址放到了p中。
    printf("%c\n",*p);
    printf("%s\n",p);
    return 0;
}
/*打印结果为
m
myblog
*/

指针数组

指针数组就是存放指针的数组
例:

#include <stdio.h>
int main()
{
    int arr1[]={1,2,3,4,5};
    int arr2[]={6,5,4,6,7};
    int arr3[]={5,6,7,4,8};
    int* arr[]={arr1,arr2,arr3};
    int i=0;
    int j=0;
    for(i=0;i<3;i++)
    {
        for(j=0;j<5;j++)
        {
            printf("%d ",*(arr[i]+j));
        }
        printf("\n");
    }
    return 0;
}
/*打印结果
1 2 3 4 5
6 5 4 6 7
5 6 7 4 8
*/

数组指针

数组指针就是指针,指向数组的指针就是数组指针
例如:

#include <stdio.h>
int main()
{
    //int arr[10]={0};
    //arr首元素地址
    //&arr[0]首元素地址
    //&arr整个数组的地址
    int arr[5]={1,2,3,4,5};
    int (*p)[5]=&arr;//p就是一个数组指针
    return 0;
}
int* p1[10];//存放指针的数组 
int (*p2)[10];//指向数组的指针

例如:

#include <stdio.h>
int main()
{
    char* arr[5];//指针数组
    char* (*pa)[5]=&arr;//数组指针
    //*pa说明pa是个指针
    //[5]说明pa指向的数组有五个元素
    //char* 说明pa指向的数组的元素类型是char*
}
数组指针的使用
#include <stdio.h>
//参数是数组
void print1(int arr[3][5],int x,int y)
{
    int i=0;
    int j=0;
    for(i=0;i<3;i++)
    {
        for(j=0;j<5;j++)
        {
            printf("%d ",arr[i][j]);
        }
        printf("\n");
    }
}
//参数是指针
void print2(int (*pa)[5],int x,int y)
{
    int i=0;
    int j=0;
	for(i=0;i<3;i++)
    {
        for(j=0;j<5;j++)
        {
            printf("%d ",*(*(pa+i)+j));
			//printf("%d ",*(pa[i]+j));
            //printf("%d ",(*(pa+i))[j]);
			//printf("%d ",pa[i][j]);
            // *(*(pa+i)+j) == *(pa[i]+j) == (*(pa+i)[j]) == pa[i][j]
        }
        printf("\n");
    }
}
int main()
{
    int arr[3][5]={{3,5,7,4,1},{5,6,7,6,4},{6,7,3,5,6}};
    print1(arr,3,5);//arr数组名,数组名就是首元素地址
	printf("\n");
	print2(arr,3,5);
    return 0;
}
/*
3 5 7 4 1
5 6 7 6 4
6 7 3 5 6

3 5 7 4 1
5 6 7 6 4
6 7 3 5 6
*/

int arr[5];
arr是一个5个元素的整型数组
int *parr1[10];
parr1是一个指针数组,10个元素,每个元素的类型是int*
int (*parr2)[10];
parr2是一个指向数组的指针,该数组有10个元素,元素的类型是int,parr2是数组指针
int (*parr3[5])[10];
parr3是一个数组,数组有5个元素,每个元素是一个数组指针,数组指针指向的数组有10个元素,元素类型为int

数组传参

一维数组传参
#include <stdio.h>
//传参方式一
void test(int arr[])
{}
//传参方式二
void test(int arr[10])
{}
//传参方式三
void test(int* arr)
{}
int main()
{
    int arr[10]={0};
    test(arr);
    return 0;
}
#include <stdio.h>
//传参方式一
void test(int* arr[])
{}
//传参方式二
void test(int **arr)
{}
int main()
{
    int* arr[10]={0};
    test(arr);
    return 0;
}
二维数组传参
//传参方式一
void test(int arr[3][5])
{}
//传参方式二
void test(int arr[][5])
//注:二维数组传参时,行可以省略,列不能省略
{}
//传参方式三
void test(int (*arr)[5])
{}
#include <stdio.h>
int main()
{
    int arr[3][5]={0};
    test(arr);
    return 0;
}

指针传参

一级指针传参

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

#include <stdio.h>
void test(int *p)
{}
int main()
{
    int a=0;
    int* p=&a;
    test(p);
    test(&a);
    return 0;
}
#include <stdio.h>
void test(char *p)
{}
int main()
{
    char a='w';
    char* p=&a;
    test(p);
    test(&a);
    return 0;
}
二级指针传参

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

#include <stdio.h>
void test(int **p)
{}
int main()
{
    int a=0;
    int* p=&a;
    int** pp=&p;
    int* arr[10];
    test(pp);
    test(&p);
    test(arr);
    return 0;
}
#include <stdio.h>
void test(char **p)
{}
int main()
{
    char a='w';
    char* p=&a;
    char** pp=&p;
    char* arr[10];
    test(pp);
    test(&p);
    test(arr);
    return 0;
}

函数指针

指向函数的指针就是函数指针
&函数名函数名都是函数的地址

#include <stdio.h>
void test()
{}
int main()
{
    
    printf("%p\n",&test);
    printf("%p\n",test);
    //打印结果一样
    return 0;
}
#include <stdio.h>
int add(int x,int y)
{
    return x+y;
}
int main()
{
    int (*pf)(int,int)=add;
    printf("%d\n",(*pf)(10,20));//30
    printf("%d\n",pf(10,20));//30
    //pf前面的 * 没多大意义,加不加都一样
    return 0;
}
#include <stdio.h>
void print(char* str)
{
    printf("%s\n",str);//myblog    
}
int main()
{
    void (*p)(char*)=print;
    (*p)("myblog");
    return 0;
}

函数指针数组

#include <stdio.h>
int add(int x,int y)
{
    return x+y;
}
int sub(int x,int y)
{
    return x-y;
}
int mul(int x,int y)
{
    return x*y;
}
int div(int x,int y)
{
    return x/y;
}
int main()
{
    int i=0;
    int (*pfa[4])(int,int)={add,sub,mul,div};
    for(i=0;i<4;i++)
    {
        printf("%d\n",pfa[i](6,3));
    }
    return 0;
}
/*
9
3
18
2
*/

指向函数指针数组的指针

#include <stdio.h>
int main()
{
    int (*pfa[4])(int,int);//pfa是一个函数指针数组
    int (*(*ppfa)[4])(int,int)=&pfa;
    //ppfa是一个数组指针,指针指向的数组有4个元素
    //每个元素的类型是一个函数指针int(*)(int,int)
    //ppfa是一个指向函数指针数组的指针
    return 0;
}

回调函数

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

#include <stdio.h>
void print(char* str)
{
    printf("haha:%s\n",str);
}
void test(void (*p)(char*))
{
    p("myblog");
}
int main()
{
    test(print);
    return 0;
}
/*
haha:myblog
*/
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值