【C语言进阶】指针 上

目录

初阶指针的知识

 指针进阶的知识点

1、字符指针

2、指针数组(本质就是数组)

3、数组指针

3.1数组指针的定义

 3.2  数组指针的使用

3.2.1 数组指针在一维数组中的使用

 3.2.2  数组指针在二维数组中的使用

 3.3  &数组名vs数组名

4、数组参数、指针参数

4.1、一维数组传参

4.2、二维数组传参

4.3、一级指针传参

4.4、二级指针传参

5、函数指针

6、函数指针数组

 6.1函数指针数组的定义

6.2、函数指针数组的使用

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


初阶指针的知识

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

2、指针的大小是固定的4/8个字节(32位平台/64为平台)。

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

4、指针的运算

 指针进阶的知识点

1、字符指针

字符指针char*

#include<stdio.h>
int main()
{
    char ch = 'w';
    char* pc = &ch
    char* ps = "abcdef";//字符串常量不可以修改
//这里是把字符串abcdef的起始地址存入指针ps中。一个字符串指针中可以存入一个字符串的起始地址
    
    char arr = "abcdef";
    char* p = arr;//通过这两步来改变字符串中的字符

    printf("%c\n",*pc);

    printf("%c\n", ps);
    
    return 0;
} 
结果是:
w
abcdef

注释:1、当不需要对字符串改变时,可以用指针定义。若想修改,需将字符串放入一个可               修改的空间中,通过指针的操控来改变字符串。

           2、如果一个指针指向了一个字符串,那我们就说p是一个指向字符串的指针。

           3、字符串中的所有字符在内存中是连续排列的,p指向的是字符串的第0个字符;                 通常我们将第0个字符的地址称为字符串的首地址。

 例题:

#include<stdio.h>
int main()
{
    char str1[] = "hello bit.";//有独立空间
    char str2[] = "hello bit.";//有独立空间
    
    const char* str3 = "hello bit.";//常量字符串,不可修改
    const char* str4 = "hello bit.";//从代码优化的角度来看指针str3和str4指向同一个地址

    if(str1 == str2)//这里比的是地址,而不是内容,数组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;
}

 输出结果如图:

 如果想要比较字符串的内容使用:strcmp


2、指针数组(本质就是数组)

指针数组顾名思义————就是存放指针的数组

int main()
{
    char* arr[5] = {"abcdef","zhangsan","wangcai","ruhua"};//存放的是这五个字符串的起始 
 //地址(首字符的地址),在这里用来初始化
    int i = 0;
    for(i=0;i<5;i++)
    {
        printf("%s\n",arr[i]);
    }
    return 0;
}
int* arr1[10];//存放整型指针的数组
char *arr2[4];//存放一级字符指针的数组
char **arr3[5];//二级字符指针的数组

 相关指针数组的内容在初阶指针


3、数组指针

3.1数组指针的定义

数组指针————是指针,并指向数组

通过比对探讨数组指针

//整型指针 — 指向整型的指针 —存放整型变量的地址
int* p1;
//字符指针 — 指向字符的指针 —存放字符变量的地址
char* p2;
//数组指针 — 指向数组的指针 —存放一个大小为10个整型的数组的地址
int(*p3)[10];
int main()
{
    int a = 10;
    int *p1 = &a;

    char ch = 'w';
    char* p2 = &ch;
    
    int arr[10] = {1,2,3,4,5};
    int (* pa)[10] = &arr;//取出的是数组的地址存放到pa中,pa是数组指针变量

    return 0;
}

注释:上述代码中int(*)[10]数组指针的类型


 3.2  数组指针的使用

数组指针在二维数组中用的比较多,在一维数组中使用较少

3.2.1 数组指针在一维数组中的使用

void print1(int(*p)[10],int sz)//指针变量p中存放数组arr的地址,指针变量p的类型是int(*)[10]
{
    int i = 0;
    for(i = 0 ; i < sz ; i++)
    {
        printf("%d ",(*p)[i]);//对p解引用得到arr,表示数组首元素地址,arr[i]表示下标位i数 
        //组元素
        printf("%d ",(*arr+i));//在这里(*p)[i]与(*arr+i)表示的结果相同,&arr传给P的地址 
        //是数组首地址,arr表示的是元素首地址,解引用来表示数组元素
    }
    printf("\n");
}
int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int sz =sizeof(arr)/sizeof(arr[10]);
    print1(&arr,sz);//传给print1函数数组arr的地址
    return 0;
}

 3.2.2  数组指针在二维数组中的使用

void print(int (*p)[5],int r,int c)//二维数组arr将首地址传给p的时候,p指向二维数组第一 
 //行,二维数组的首地址就是第一行地址。r表示行,c表示列
{
    int i = 0;
    for(i = 0;i < r;i++)
    {
        int j = 0;
        for(j = 0;j < c;j++)
        {
            printf("%d ",*(*(p+i)+j));//*(p+i)表示的是第i行元素,相当于p[i],p+i指向第一 
                                         //行,p+i表示第一行地址.
        }
        printf("\n");
    }
}
void test()
{
    int arr[3][5] = {{1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7}};
    print(arr,3,5);//传递二维数组首元素地址,二维数组首元素地址表示第一行5个元素
}
int main()
{
    test();
}

 注释:上述代码中int(*p)[5]指向的是二维数组的第一行,上述场景下才是数组指针的常用形式

 画图解释上述代码:

学习了指针数组和数组指针后,下面的代码意思就可以理解了

int arr[5];    整型数组,存放5个整型
int *parr1[10];   指针数组,存放10个int*类型指针的数组
int (*parr2)[10];   数组指针,parr2指针指向一个存放10个int类型的数组
int(*parr[10])[5];   为一个数组包含10个元素,每个元素为数组指针,指针指向一个存放5个int类 
                     型的数组,简单来说就是一个存放数组指针的数组。

 3.3  &数组名vs数组名

arr和&arr分别是啥?

arr是数组名,数组名表示数组首元素的地址。

&arr表示的是啥:

 在这里从数值的角度看结果一样,但是他们代表的涵义还是有所不同

 数组名是数组是首元素的地址有两个例外:

1、sizeof(数组名)     //这里的数组名表示的是整个数组

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


4、数组参数、指针参数

在写代码的时后难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?

4.1、一维数组传参

#include<stdio.h>
void test(int arr[])//这里的数组大小可以省略,数组传参的时候,不会将整个数组传过去,那么接 
 //收的时候不需要创建一个数组来接收传过来的值,不需要创建数组,就不需要大小,所以数组的大小 
 //就省略了。数组传参的时候,传递的是数组首地址。
{}

void test(int arr[10])//前两个传参,形参是数组
{}

void test(int *arr) //数组传参,形参是指针的形式
{}

void test2(int* arr[20])//这里20也可以省略
{}

void test2(int **arr)//int* arr2[20]表示数组arr2中存放了20个int*类型的指针,形参 
 //int**arr中的两个**中的一个*表示arr是指针变量,指针arr指向数组arr2的首元素的地址,int* 
 //表示数组中存的是int*类型的指针
{}

int main()
{
    int arr[10] = {0};
    int* arr2[20] = {0};//这里是存放整型指针的数组
    test(arr);
    test(arr2);
}

数组传参,形参可以用数组接收,也可以用指针接收,以上的一维数组传参都是可行的。

4.2、二维数组传参

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

void test(int arr[][])//err
{}

void test(int arr[][5])//ok
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字,因为对一个二维数组,可以不知道 //多少行,但是必须知道一行有多少元素,这样才方便运算

void test(int *arr)//err,
 //这里传过来的是二维数组首元素的地址指(第一行地址),数组有多少列,无法知道
{}

void test(int* arr[5])//err
{}

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

void test(int **arr)//err
//二维数组传参传过去的是第一行的地址,是一个一维数组的地址,二级指针用来接收一级指针变量的 //地址
{}

int main()
{
    int arr[3][5] = {0};
    test(arr);
}

注释:二级指针不能用来接收二维数组的传参,二级指针用来接收一级指针变量的地址

4.3、一级指针传参

#include<stdio.h>
void print(int *p,int sz)//形参用一级指针接收,接收的是指针p中的内容
{
    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]);
    print(p,sz);//一级指针传参
}

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

void test(int *p)
{}
//1、整型变量的地址
int a = 0;
test(&a);
//2、整型数组的首元素地址
int arr[10] = {0};
test(arr);
//3、一级指针变量本身
int a = 0;
int *p = &a;
test(p);

4.4、二级指针传参

#include<stdio.h>
void test(int** ptr)//这里ptr和pp的内容相同,用法也相同。
{
    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';
   //1、一级指针变量的地址
    char* p = &c;
   //2、二级指针变量本身
    char**pp = &p;  
   //3、指针数组的数组名
    char* arr[10];
    test(arr);
}

5、函数指针

函数指针表示指向函数的指针

先看一段代码

int Add(int x,int y)
{
    return x + y;
}
int main()
{
    printf("%p\n",Add);//函数取地址和不取地址结果都是相同的,拿到的都是函数的地址,函数和数组不同,数组存在取首元 
                        //素地址,而函数不存在这样的问题
    printf("%p\n",&Add);
}

 输出结果如图:

注释:上述代码表示Add和&Add都能让代码得到函数的地址,所以函数名Add和&Add都能代表函数地址。

 函数指针的几种方式

int Add(int x,int y)
{
    return x+y;
}
int main()
{
    int(*pf)(int x,int y) = Add;//函数指针,将Add的地址传给指针pf
    int sum = (*pf)(3,5);//这样写更容易让人理解
    //在函数调用中直接调用Add也可以
    int sum = Add(3,5);
    int sum = pf(3,5);//这样写也可以
    printf("%d\n",sum);
}

注释:由第一段代码可知函数名可以表示函数地址,Add和pf一样都代表地址,所以在写代码是它俩可以用相同的语法来写

 学完函数指针,来解读两个有意思的代码:1、(  *( void ( * ) ( ) ) 0 )( )

解读这个代码,从0为突破口,

表示把0直接转换成一个void (*) ( )的函数指针,然后去调用0地址处的函数

这个代码只是放在这里用作理解函数指针,

补充:

关于强制类型转换的知识点:

一般形式为:(类型说明符)(表达式)其功能是把表达式的运算结果强制转换成类型说明符所表示的类型。

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

上述代码是一次函数声明

声明函数叫:signal

signal函数的第一个参数是int类型对的

signal函数的第二个参数是一个函数指针类型,该函数指针指向的函数参数是int,

返回类型是void

signal函数的返回类型也是一个函数指针类型


6、函数指针数组

 6.1函数指针数组的定义

数组是一个存放相同类型数据的存储空间,我们已经学了指针数组,

比如:

int* arr[10];

那要把函数的地址存放到一个数组中,那这个数组就叫函数指针数组,那函数指针数组如何定义呢?

int(*parr[10])();

parr先和[ ]结合,说明parr是数组,数组的内容为int(*)( )类型的函数指针

6.2、函数指针数组的使用

举例:写一个计算器进行整数的加、减、乘、除

#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;
}
void menu()
{
    printf("***********************\n");
    printf("**** 1.add  2.sub  ****\n");
    printf("**** 3.mul  4.div  ****\n");
    printf("**** 0.exit        ****\n");
    printf("***********************\n");
}

int main()
{
    int input = 0;
    int x = 0;
    int y = 0;
    int ret = 0; 
    do
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        printf("请输入两个操作数:>");//将这句代码写在这里,当input输入的是0或0~4以外的数,都要进行这两句代码,显然是存在问题的,
        scanf("%d %d", &x,&y);//然而将这两句代码写进case语句中,来解决这个问题,那么每 
//个case语句中都要写进这两句代码,那么就会形成代码冗余
        switch (input)
        {
        case 1:
            ret = add(x,y);
            printf("%d\n",ret);
            break;
        case 2:
            ret = sub(x,y);
            printf("%d\n",ret);
            break;
        case 3:
            ret = mul(x,y);
            printf("%d\n",ret);
            break;
        case 4:
            ret = div(x,y);
            printf("%d\n",ret);
            break;
        case 0:
            printf("退出计算器\n");
            break:
        defalut:
            printf("选择错误\n");
            break;
        }
    } while (input);
}

上述代码中的问题,可以通过函数指针数组来解决

#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;
}
void menu()
{
    printf("***********************\n");
    printf("**** 1.add  2.sub  ****\n");
    printf("**** 3.mul  4.div  ****\n");
    printf("**** 0.exit        ****\n");
    printf("***********************\n");
}

int main()
{
    int input = 0;
    int x = 0;
    int y = 0;
    int ret = 0;
    //这里使用函数指针数组  
    int(*pfArr[])(int ,int) = {0, add, sub, mul, div}; 
    do
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        if(input == 0)
        {
            printf("退出计算器\n");
        }
        if(input >= 1 && input <= 4)
        {
            printf("请输入两个操作数:>");
            scanf("%d %d", &x,&y);
            ret = pfArr[input](x,y);
            printf("%d\n", ret);

        }
        
    }
    while(input);
}

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

//函数指针
int (*pf)(int , int);

要将多个函数指针存起来,这个时候就需要函数指针数组
//函数指针数组
int (*pfArr[4])(int,int);

要取出函数指针数组的地址
//用指针ptr来接收
ptr = &pfArr
int (*(*ptr)[4])(int,int) = &pfArr;
//ptr表示的是函数指针数组的指针
//ptr的指针类型是int (*(*)[4])(int ,int)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值