C语言——指针全解

目录

一、指针变量 

 二、指针和指针类型

        1、指针类型

        2、指针变量大小

 三、指针类型的意义

        1、解引用 

        2、指针+-整数 

        3、void* 指针类型 

四、野指针

 五、指针的运算

        1、指针 +- 整数

        2、指针 -+ 指针

六、二级指针

 七、指针数组

 八、数组指针

 九、函数指针

 十、函数指针数组


一、指针变量 

        指针就是地址,口语中说的指针通常指的是指针变量

        我们通过 & (取地址操作符)取出一个变量的内存地址,把地址存放到一个变量里面,那这个变量就是指针变量

int main()
{
    int q1=10;
    int* qi=&q1;
    //此时这个qi就是一个指针变量
    printf("%p\n",qi);
}

 二、指针和指针类型

        1、指针类型

                指针类型由两部分组成分别为 数据类型 与 *,比如( int* pi)

                * 说明pi是一个指针变量,int 说明 pi 指向的对象为 int 类型

char* pc=null;//字符指针
// * 说明pc是一个指针变量,char 说明 pc 指向的对象为 char 类型
int* pi=null;//整型指针
// * 说明pi是一个指针变量,int 说明 pi 指向的对象为 int 类型
short* ps=null;//短整型指针
long* pl=null;//长整型指针
float* pf=null;//单精度浮点型指针
double* pd=null;//双精度浮点型指针

        2、指针变量大小

                指针变量本质上来说就是存放地址的,地址的存放需要多少大小,指针变量就有多大

        32位机器上,有32根地址线,一个地址为32个二进制位,需要4个字节

        比如在32为机器上,double*、int*、char*、short*等都是4个字节大小

        64位机器上,有64根地址线,一个地址为64个二进制位,需要8个字节

         比如在64为机器上,double*、int*、char*、short*等都是8个字节大小

int main()
{
    int num = 1;
    //通过 & 操作符取出变量的地址存放到指针变量中
    int* pi = #
    //pi就是一个指针变量
    
    //32位机器上,有32根地址线,一个地址为32个二进制位,需要4个字节
    //64位机器上,有64根地址线,一个地址为64个二进制位,需要8个字节
    printf("%d\n", sizeof(pi));// 8
    return 0;
}

 三、指针类型的意义

        指针类型决定了,在对指针变量进行解引用操作时,一次可以操作多少个字节

        1、解引用 

int main()
{
	int num = 0x11111111;
	//通过 & 操作符取出变量的地址存放到指针变量中
	int* pi = #
	//pi就是一个指针变量

	*pi = 0;
	printf("%d\n", *pi);//输出0
	return 0;
}

 

        可以看到在进行解引用操作时,内存中修改了4个字节,,因为一个int类型就占4个字节

int main()
{
    int num = 0x11111111;
    //通过 & 操作符取出变量的地址存放到指针变量中
    char* pi = (char*) & num;
    //pi就是一个指针变量
    
    *pi = 0;
    printf("%d\n", num);//不是0
    return 0;
}

        可以看到在进行解引用操作时,内存中修改了1个字节,,因为一个char类型就占1个字节 

        2、指针+-整数 

        指针类型决定了指针在运算时访问的字节大小(走一步多大距离)

        1、char*指针+1,将访问1个字节,也就是先后走4个字节

        2、int*指针+1,将访问4个字节,也就是先后走4个字节

        3、double*指针+1,将访问8个字节,也就是先后走8个字节

        ...................

//演示实例
#include <stdio.h>
int main()
{
 	int n = 10;
 	char *pc = (char*)&n;
 	int *pi = &n;
 
 	printf("%p\n", &n);
 	printf("%p\n", pc);
 	printf("%p\n", pc+1);
 	printf("%p\n", pi);
 	printf("%p\n", pi+1);
 	return  0; 
 }

 ​

        3、void* 指针类型 

                void 指针类型可以接受任意类型的指针类型,但是不能直接对void* 类型的指针进行指针+-与解引用操作,必须通过强制类型转换后才能进行指针+-与解引用操作

int main()
{
    int num = 10;
    //通过 & 操作符取出变量的地址存放到指针变量中
    void* pi = &num;
    //pi就是一个指针变量
    
    //不能直接对void* 类型的指针进行指针+-与解引用操作
    *pi = 0;// error
    pi + 1;// error
    //必须通过强制类型转换后才能进行指针+-与解引用操作
    *(int*)pi = 1;
    printf("%d\n", num);
    return 0;
}

四、野指针

        指针指向的位置是不可知的,随机的等不明确的被称为野指针 

        1、指针未初始化

        2、指针越界访问

        3、指针指向的空间已经释放(局部变量在离开作用域时会被销毁)

        ................

#include <stdio.h>
int main()
{ 
    (1)指针未初始化
     int *p;//局部变量指针未初始化,默认为随机值
     *p = 20;// error
     return 0; 
     
     (2)指针越界访问
      int arr[10] = {0};
      int *p = arr;
      int i = 0;
      for(i=0; i<=11; i++)
      {
          //当指针指向的范围超出数组arr的范围时,p就是野指针
          *(p++) = i;
      }
      
     (3)指针指向的空间已经释放
     int test()
     {
         int q1=1;// 局部变量在离开作用域时会被销毁
         return &q1;     
     }
     int* p = test();
     *p=200;// error
     return 0;

}

如何规避野指针

  1. 指针初始化(已知指向时明确初始化;未知初始化为NULL)
  2. 小心指针越界
  3. 指针指向空间释放,及时置NULL
  4. 避免返回局部变量的地址
  5. 指针使用之前检查有效性

 五、指针的运算

        1、指针 +- 整数

        指针类型决定了指针+-整数在时访问的字节大小(走一步多大距离)

        1、char*指针+n,将访问1个字节,也就是向后走1*n个字节

        2、int*指针+n,将访问4个字节,也就是向后走4*n个字节

        3、double*指针+n,将访问个字节,也就是向后走8*n个字节

        ...................

int main()
{
    // 数组在内存中是连续存储的,当我们知道首元素地址时,就可以找到所有元素
    const int len = 10;
    int arr[len];
    for (int i = 0; i < len; i++)
    {
        arr[i] = i + 1;
    }
    
    int* p = &arr[0];
    // int*指针+n,将访问4个字节,向后走4*n个字节    
    for (int i = 0; i < len; i++)
    {
        printf("%d ", *(p + i));
    }
    return 0;
}

        2、指针 -+ 指针

        1、两个指针相减的前提是:指针指向的是同一块连续的空间

        2、指针和指针相减的绝对值等于指针和指针之间的元素个数

        3、指针+指针没有意义

#include<stdio.h>
int main()
{	
    int arr[10] = {0};
    //指针和指针相减的绝对值等于指针和指针之间的元素个数
    printf("%d\n", &arr[9] - &arr[0]);//9
    printf("%d\n", &arr[0] - &arr[9]);//-9
    
    //两个指针相减的前提是:指针指向的同一块连续的空间
    int arr2[10] = {0};
	
    printf("%d\n", &arr[9] - &arr2[0]);//err
    
    return 0;
}

六、二级指针

        指针变量也是变量,是变量就有地址,用来存放指针变量的指针就是二级指针 

        int * p -----> * 表示 p 是一个指针变量,而 int 是 p 所指向地址的类型

        int* * p -----> * 表示 p 是一个指针变量,而 int* 是 p 所指向地址的类型

int main()
{
    int a = 10;//
    int *pa = &a;//pa就是指针变量,一级指针变量,表示指针指向的a是int
    int* *ppa = &pa;//ppa就二级指针,表示pp指向的p的类型是int*
    return 0;

    //理解 int * p -----> * 表示 p 是一个指针变量,而 int 是 p 所指向地址的类型
    // int* * p -----> * 表示 p 是一个指针变量,而 int* 是 p 所指向地址的类型            
}

 七、指针数组

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

int main()
{
    //整型数组-存放整型的数组
    int arr[10];
    //字符数组-存放字符的数组
    char arr2[5];
    
    //指针数组-存放指针的数组
    int* arr3[5];//存放整型指针的数组
    char* arr4[6];//存放字符指针的数组
    
    return 0;
}

        利用指针数组将多个一维数组串联为一个二维数组

int main()
{
    //用一维数组模拟一个二维数组
    int arr1[] = { 1,2,3,4,5 };
    int arr2[] = { 2,3,4,5,6 };
    int arr3[] = { 3,4,5,6,7 };
    int arr4[] = { 4,5,6,7,8 };
    
    int* arr[4] = {arr1, arr2, arr3, arr4};//用指针数组管理一维数组
    int i = 0;
    for (i = 0; i < 4; i++)
    {
        int j = 0;
        for (j = 0; j < 5; j++)
        {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
    //1,2,3,4,5
    //2,3,4,5,6
    //3,4,5,6,7
    //4,5,6,7,8
    return 0;
}

 八、数组指针

        定义:type (*name) [num]        

        数组指针是用来存放数组的地址

        数组指针在进行+-整数时,一次将跳过整个数组的大小

        数组也是一种类型,因此在创建数组指针时要明确数组的类型也就是数组的大小

int main()
{
    int arr[10];
    //数组也是一种类型
    //因此在创建数组指针时要明确数组的类型也就是数组的大小
    int(*p)[10]=&arr;
    //p先与*结合,说p是一个指针变量
    //指针指向的是一个大小为10的整形数组,叫数组指针变量(存放数组的地址)
    
    return 0;
}

 九、函数指针

        定义:int (*name) (形参,形参.......)

        函数指针用来存放函数的地址

        对于函数而言,&函数名等价于函数名,这有区别于数组

        函数指针可以直接使用,不需要解引用

void print(int a, int b, int c)
{
    printf("%d %d %d\n", a, b, c);
}
int main()
{
    //对于函数而言,&函数名等价于函数名,这有区别于数组
    void (*p)(int, int, int) = &print;
    void (*p)(int, int, int) = print;
    
    (*p)(1, 2, 3);
    //函数指针可以直接使用,不需要解引用
    p(1, 2, 3);
    // 1 2 3
    return 0;
}

 十、函数指针数组

        定义:int (*name [数量] ) (形参,形参.......)

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

        实例:

                利用函数指针实现计数器

// 执行加法
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()
{
    menu();
    int input = 0;
    printf("请选择:");
    scanf("%d", &input);
    int ret = 0;
    //转移表
    //创建函数指针数组,依次放入函数
    // 1——加法
    // 2——减法
    // 3——乘法
    // 4——除法
    int(*pfarr[])(int, int) = { 0,Add,Sub,Mul,Div };
    do
    {
        // 判断是否合法
        if (input == 0)
        {
            printf("退出\n");
            break;
        }
        else if (input >= 1 && input <= 4)
        {
            int x = 0;
            int y = 0;
            printf("请输入两个操作数:");
            scanf("%d%d", &x, &y);
            // 返回选择对应运算的结果
            ret = pfarr[input](x, y);
            printf("结果是%d\n", ret);
            break;
        }
        else
        {
            printf("选择错误!");
        }

    } while (input);

    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值