搞定C语言指针,指针超详细讲解,及指针面试题

Posted Jiawen_captial

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了搞定C语言指针,指针超详细讲解,及指针面试题相关的知识,希望对你有一定的参考价值。

C语言指针进阶

1、 字符指针

常见的字符指针
在指针的类型中我们知道有一种指针类型为字符指针 char* ;一般使用为:

int main()

    char ch = 'w';
    char *pc = &ch;
    *pc = 'w';
    return 0;

不可修改的字符指针

int main()

    char* pstr ="hello";
    //一定切记不是把hello放到pstr里面去
    printf("%s\\n", pstr);
    //直接使用地址打印字符串,不用解引用
    printf("%c",*pstr);//打印h
    //证明里面放的是首元素的地址
    
    //常量字符串不允许被修改,用const加以修饰
    const char* pstr ="hello";
    return 0;

代码 char* pstr = “hello”; 特别容易让同学以为是把字符串 hello bit 放到字符指针 pstr 里了,但是/本质是把字符串 hello 首字符的地址放到了pstr中。
字符指针的例题:

#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;

//str1不等于str2,str3等于str4

解析:str1和str2是两个独立的数组,开辟了两块独立的空间,所以str1和str2地址不同,str3和str4都指向同一个常量字符串,因为常量字符串不能被修改,又str3和str4都保存的字符串的首地址,即都指向同一位置。

图片解析:

2、指针数组

int arr[10];//整型数组-存放整型的数组
char s[10];//字符数组-存放字符的数组
//指针数组-存放指针的数组
int *p[10];//存放int *的数组
char*p[10];//存放char*的数组

int main()

    int arr1[]=1,3,4,5;
    int arr2[]=2,6,5,7;
    int arr3[]=1,9,6,4;
    int *p[]=arr1,arr2,arr3;
    int i=0;
    for(i=0;i<3;i++)
    
        int j=0;
        for(j=0;j<5;j++)
        
            printf("%d",p[i][j]);
        //抽象理解为二维数组,依靠p[i][j]==*(p[i]+j)
        
        printf("\\n");
    

图片解析:


常量字符串数组

int main()

    const char *arr[3]="asdc","bds","cbsd";
    //将a,b,c等首元素的地址存入arr
    int i=0;
    for(i=0;i<3;i++)
    
        printf("%s\\n",arr[i]);
    
    return 0;

补充:关于printf打印字符串,只要提供起始地址就能进行打印,但是只针对打印字符串。另外指针数组的数组名是一级指针,要用二级指针存储。

图片解析:

3、数组指针

数组指针是指针,一种指向数组的指针。

int main()

    int arr[10];
    int *p=arr;//取出数组首元素的地址,放到整型指针中
    int (*parr)[10]=&arr;//取出的是数组的地址,应该存放到数组指针中
    //int (*)[10]为指针parr的类型
    //首先parr为数组,指向大小为10的数组,数组的的元素为int类型
    
    int *p1[10];
    int (*p2)[10];
    //p1, p2分别是什么?
    p1为指针数组,p2为数组指针
    
    int (*p)[10];

解释:p先和*结合,说明p是一个指针变量,然后接着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。

这里要注意:[]的优先级要高于号的,所以必须加上()来保证p先和结合。

使用数组指针的一种遍历
这种写法是为了让大家加强对指针的理解,在实际的操作过程中,这样写只会加强代码的复杂性。

void Print(int(*parr)[10],int sz)

    int i=0;
    for(i=0;i<sz;i++)
    
        printf("%d",parr[0][i]);
        //[0]找到了这个数组的第一行,[i]来找一行的元素
        printf("%d",(*(parr+0))[i]);
        //将其看成一个只有一行的二维数组
        printf("%d",(*parr)[i]);
        //(*parr)相当于parr指向的数组的数组名
    

int mian()

    int arr[10]=1,2,3,4,5,6,7,8,9,10;
    int sz=sizeof(arr)/sizeof(arr[0]);
    Print(&arr,sz);
    return 0;


对于数组指针,在使用的过程中一定要慎重,随意的使用会造成复杂的的升高和难以理解,如上述代码,下面来看一下数组指针的优点使用,推荐在二维数组及其以上使用。

参数部分也是二维数组
Print1(int arr[3][5],int r,int c)

    int i=0;
    for(i=0;i<r;i++)
    
        int j=0;
        for(j=0;j<c;j++)
        
            printf("%d",arr[i][j]);
        
        printf("\\n")
    

void Print2(int (*p)[5],int r,int c)

    int i=0;
    for(i=0;i<r;i++)
    
        int j=0;
        for(j=0;j<c;j++)
        
            printf("%d",*(*(p+i)+j));
            printf("%d",p[i][j]);
            //两种方法都能实现打印,细细体会两种方法的实现
        
    

int main()

    int arr[3][5]=1,2,3,4,5,2,3,4,5,6,3,4,5,6,7;
    //二维数组传参
    Print1(arr,3,5);
    Print2(arr,3,5);//arr是数组名,数组名是首元素的地址
    //把二维数组的每一行看成一个元素,元素为一行的一维数组的数组
    //数组名是首元素的地址,二维数组的数组名是第一行的地址
    

分析一些数组和指针的问题:

int arr[5];
//arr和[]结合说明arr为数组,int说明为整型数组
int *parr1[10];
//parr1是一个数组,10个元素,每个元素是int*的
//所以parr1是一个存放指针的数组
int (*parr2)[10];
//parr2是一个数组指针,该指针指向的数组有10个元素,每个元素是int类型的
int (*parr3[10])[5];
int(*)[5]
//parr3是个数组,数组有10个元素
//每个元素是一个数组指针,该指针指向的数组有5个元素
//每个元素是int类型

4、数组参数、指针参数

我们在写程序的过程中,自定义函数的使用是必不可少的,既然有函数的使用,那么一定会有函数传参的过程,下面让我们来研究一下数组和指针在传参过程中的问题:
一维数组传参

#include <stdio.h>
void test(int arr[])//ok

void test(int arr[10])//ok

void test(int arr[100])//ok 虽然语法正确,但是不建议

void test(int *arr)//ok

void test2(int *arr[20])//ok

void test2(int *arr[])//ok

void test2(int **arr)//ok 接收一级指针的地址,使用二级指针

int main()

 int arr[10] = 0;
 int *arr2[20] = 0;
 //20个元素每个元素都是int*的,首元素的地址为一个一级指针的地址
 test(arr);
 test2(arr2);

解析:因为传参不会真实的创建数组,所以[]里的数值可以省略,数组也可为任意值,但是任意值的写法容易产生歧义,所以不建议。
二维数组传参

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

void test(int arr[][])//no

void test(int arr[3][])//no

void test(int arr[][5])//ok

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

void test(int* arr[5])//no

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

void test(int **arr)//no

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;

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

void test1(int *p)

//test1函数能接收什么参数?
//1、整型变量的地址
//2、整型数组首元素的地址
//3、一级指针变量
void test2(char* p)

//test2函数能接收什么参数?
//1、字符变量的地址
//2、字符数组首元素的地址
//3、一级指针的变量

二级指针传参
当函数的参数为二级指针的时候,可以接收什么参数?如下所示:

#include <stdio.h>
void test(int** ptr)

 printf("num = %d\\n", **ptr); 

int main()

 int n = 10;
 int*p = &n;
 int **pp = &p;
 int *arr[5];
 test(pp);
 test(&p);
 tset(arr);
 return 0;

5、函数指针

先来看一段代码:

int Add(int x,int y)

    return x+y;

int main()

    printf("%p\\n",&Add);
    printf("%p\\n",Add);
    //两个一句打印的内容一样
    //&函数名和函数名意义相同

上述代码说明函数是有地址的,那么存放函数地址的指针称为函数指针。

int Add(int x,int y)

    return x+y;

int main()

    printf("%p\\n",&Add);
    printf("%p\\n",Add);
    int (*pf)(int,int)=&Add;
    int(*)(int,int)//函数指针的类型
    //pf是用来存放函数地址的-pf就是函数指针变量
    //pf与*结合说明pf是个指针,后面的括号说明函数的参数
    //为int,int类型的,前面的int说明返回值为int类型
    
    //利用函数指针调用函数
    ret=(*pf)(4,5);
    //或者ret=pf(4,5);

两段高阶代码:

//代码1
(*(void (*)())0)();
//代码是一次函数调用
//解析:
//1、代码中把0强制类型转换成为类型为void(*)()的一个函数的地址
//2、解引用0地址,就是去0地址处的这个函数,被调用的函数无参数,返回类型是void
//代码2
void (*signal(int , void(*)(int)))(int);
//这个代码是一次函数声明
//signal函数有两个参数,第一个是int类型,第二个是void(*)(int)的函数指针类型
//signal函数的返回值类型依然是:void(*)(int)的函数指针类型
//使用typedef简化
//typedef int s;s为int的别名,具有int的属性
//typedef void(*pf)(int);把void(*)(int)从新命名为pf,具有同等属性
//pf signal(int,pf);等价void (*signal(int , void(*)(int)))(int);

6、函数指针数组

函数指针数组-存放函数指针的数组

int main()

    int (*pf1)(int,int)=Add;
    int (*pf2)(int,int)=Sub;
    int (*pf3)(int,int)=Mul;
    int (*pf4)(int,int)=Div;
//对于同类型的函数指针单独存储,代码太过复杂,所以要使用函数指针数组
    int (*pfArr[4])(int,int以上是关于搞定C语言指针,指针超详细讲解,及指针面试题的主要内容,如果未能解决你的问题,请参考以下文章

C语言超详讲解☀️指针是个什么针?(一次性搞定指针问题)

c语言指针用法及实际应用详解,通俗易懂超详细!

指针的这些知识你知道吗?C语言超硬核指针进阶版3w+字详解+指针笔试题画图+文字详细讲解

C语言 关于POINTER(指针)的问题, 急!!!!

超详细的c进阶教程!手撕c指针

C语言指针(指针数组数组指针函数指针传参回调函数等)超详细