首页 > 其他分享 >C语言——指针全解

C语言——指针全解

时间:2024-10-14 12:17:25浏览次数:8  
标签:变量 int C语言 数组 printf 全解 pi 指针

目录

一、指针变量 

 二、指针和指针类型

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

 

 

标签:变量,int,C语言,数组,printf,全解,pi,指针
From: https://blog.csdn.net/LVZHUO_2022/article/details/142858841

相关文章

  • C语言——自定义类型
    目录一、结构体        1、结构体的定义与声明        2、结构体变量的定义和初始化        3、结构体的自引用         4、结构体的内存对齐         5、为什么要结构体的内存对齐                1、性能......
  • (C语言)算法数据结构
    王道数据结构以及本人上课的笔记             ......
  • 与C语言的旅程之分支与循环
                    C语⾔是结构化的程序设计语⾔,这⾥的结构指的是顺序结构、选择结构、循环结构,        C语⾔是能够实现这三种结构的,其实我们如果仔细分析,我们⽇常所⻅的事情都可以拆分为这三种结构或者这三种结构的组合。        我们......
  • C语言分支与循环的学习(小知识)
    学习目录 1.if 表达式成立(为真),则语句执行;表达式不成立(为假),这语句不执行。    注解:C语言中,非零表示真,0表示假。如果一个表达式的结果不是0,这语句执行。反之,则语句执行。实例:输入一个整数,判断是否为奇数该程序的执行逻辑是包含头文件#include<stdio.h>,输入主函数,......
  • 有关C语言中的数据类型(持续更新)
    有关计算机中的数据单位:计算机存储容量基本单位是字节(byte)字节byte:8个二进制位(bit)为一个字节(B),最常用的单位。一个英文字母(不分大小写)占一个字节的空间,一个中文汉字占两个字节的空间。计算机的最小存储单位:比特(bit)位bit(比特)(BinaryDigits):存放一位二进制数,即......
  • C语言中输入/输出缓冲区行为乱序的问题
    问题代码这一串代码就是输出提示,读取输入,这样做3遍但是看到运行结果会发现,第二个和第三个的提示字符串输出到同一行了,没法输入操作符原因这是因为输入缓冲区的缘故当我们输入第一个数字1的时候,按下回车确认,但同样的,回车的换行符也同样保留在输入缓冲区了,数字1被读取消耗掉......
  • 指针常量
    constchar* 是C和C++中的一种类型声明,表示一个指向常量字符的指针。具体来说,const 关键字用于修饰指针指向的数据,使得数据本身不可修改。以下是 constchar* 的详细解释:1.指针和常量指针(Pointer):指针是一个变量,用于存储内存地址。char* 表示一个指向字符类型的指针。......
  • C++中传指针和传引用的区别,各自的使用场景是什么
    在C++中,传指针和传引用都是将变量传递给函数的两种方式,但它们在语法、行为和使用场景上有一些区别。理解它们的区别和各自的适用场景是编写高效和安全代码的重要组成部分。1.传指针(PassbyPointer)指针是一种变量,它存储另一个变量的内存地址。在函数参数中使用指针,意味着将实......
  • 刷c语言练习题8(牛客网)
    1、如果有inta=5,b=3,在执行!a&&b++;后a和b的值分别是()A、5,3B、0,1C、0,3D、5,4答案:A解析:按照优先级顺序,先计算!a,得到0。由短路法则,b++不进行计算,又!a并没有改变a的值,所以a和b的值分别是5,3,选择选项A。2、以下程序的输出结果是()1234567main(){     ......
  • 【妙趣横生】01_C语言的指针是啥?为啥那么难懂?
      引入:C语言的指针是啥?为啥那么难懂?C语言中的指针是C语言的一个核心概念,也是其强大和灵活性的重要来源之一。然而,对于初学者来说,指针确实可能是一个难以理解的概念。下面我会尽量用简单的语言来解释什么是C语言中的指针,以及为什么它可能会让人觉得难懂。趣味解释:C语言......