IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 十六、C指针详解(六):指针知识点汇总 -> 正文阅读

[C++知识库]十六、C指针详解(六):指针知识点汇总

一、指针基础

一个变量,变量名,变量值,变量地址。指针变量存放变量的地址,同时,指针变量有自己的地址,指针必须与变量的类型一致
指针变量本质上是一个变量,只不过这个变量是用来存放其他变量的地址,这是其与其他变量的不同之处,需要用*加以标识,与其他变量一样,使用时也需要提前声明
在这里插入图片描述

1.1 符号说明

  • *:指针声明,取值
  • &:取址

1.2 代码举例

  • 代码实现:
    • 图片解释:
      在这里插入图片描述

      #include<stdio.h>
      void main()
      {
          //p存放a的地址,a为整型,所以p也为整型
          int *p;
          //p1存放p的地址,p为整型,所以p1也为整型
          //p是一个指针,p1是存放p地址的指针,也就是指针的指针
          int **p1;
          //p1是指针的指针,p2是存放p1地址的指针,也就是指针的指针的指针
          int ***p2;
          int a = 2;
          p = &a;
          p1 = &p;
          p2 = &p1;
          printf("a的地址:%p\n",p); //数据类型是一个指针,用p表示
          printf("p的地址:%p\n",p1); //数据类型是一个指针,用p表示
          printf("p1的地址:%p\n",p2); //数据类型是一个指针,用p表示
          printf("p指向地址存放的值:%d,即a的值\n",*p);//数据是a,整型
          //p1存放的是p的地址,值是a的值,所以数据类型为指针类型,用p表示
          printf("p1指向地址存放的值:%p,即p的值,a的地址\n",*p1);
          //p2存放的是p1的地址,值是p的地址,所以数据类型为指针类型,用p表示
          printf("p2指向地址存放的值:%p,即p1的值,p的地址\n",*p2);
          
      }
      
  • 运行结果:
    a的地址:0x7ffc00bf7ddc
    p的地址:0x7ffc00bf7de0
    p1的地址:0x7ffc00bf7de8
    p指向地址存放的值:2,即a的值
    p1指向地址存放的值:0x7ffc00bf7ddc,即p的值,a的地址
    p2指向地址存放的值:0x7ffc00bf7de0,即p1的值,p的地址
    

1.3 空指针

在C语言中,如果定义的指针没有确切指向的地址,则赋值为NULL,被称为空指针。在操作系统中,内存地址为0的内存是不可以访问的,如果指针为0,则假定它不指向任何东西

  • 代码实现:
    #include <stdio.h>
    
    int main()
    {
        int *ptr = NULL;
        //下面这两种地址指向不确定
        int *ptr1;
        int *ptr2;
        if (!ptr)
        {
            printf("ptr地址指向为空\n");
        }
    
        printf("ptr 的地址是 %p\n", ptr);
        printf("ptr1 的地址是 %p\n", ptr1);
        printf("ptr2 的地址是 %p\n", ptr2);
    
        return 0;
    }
    
  • 运行结果:
    ptr地址指向为空
    ptr 的地址是 (nil)
    ptr1 的地址是 0x7ffd516bdd40
    ptr2 的地址是 (nil)
    

二、指针的算数运算

C指针是一个用数值表示的地址。因此,可以对其进行有关的数值运算:+, -, *, /, <, >, ++, --。只不过,这种运算与指针的类型有关,以指针类型为单位进行运算。例如,一个指向整型变量的指针,初始值ptr为1000,则ptr++之后,其值变为1004,如果是字符,则为1001

  • 代码实现:
    #include <stdio.h>
    int array[] = {1,2,3,4};
    int main()
    {
        int a_size = sizeof(array)/sizeof(array[0]);
        // 指针递增
        printf("递增************\n");
        int i, *p;
        p = array;
        for(i = 0; i < a_size; i++){
            printf("array[%d]=%d,地址:%p\n",i,array[i],p);
            p++;
        }
        printf("递减************\n");
        int i1, *p1;
        p1 = &array[a_size-1];
        // 指针递减
        for(i1 = a_size - 1; i1 >= 0; i1--){
            printf("array[%d]=%d,地址:%p\n",i1,array[i1],p1);
            p1--;
        }
        printf("比较************\n");
        i = 0;
        p = array;
        while(p <= &array[a_size-1]){
            printf("array[%d]=%d,地址:%p\n",i,array[i],p);
            p++;
        }
        return 0;
    }
    
  • 运行结果:
    递增************
    array[0]=1,地址:0x55edd61c3010
    array[1]=2,地址:0x55edd61c3014
    array[2]=3,地址:0x55edd61c3018
    array[3]=4,地址:0x55edd61c301c
    递减************
    array[3]=4,地址:0x55edd61c301c
    array[2]=3,地址:0x55edd61c3018
    array[1]=2,地址:0x55edd61c3014
    array[0]=1,地址:0x55edd61c3010
    比较************
    array[0]=1,地址:0x55edd61c3010
    array[0]=1,地址:0x55edd61c3014
    array[0]=1,地址:0x55edd61c3018
    array[0]=1,地址:0x55edd61c301c
    

三、指针数组和数组指针

3.1 指针数组

  • 定义:
    指针数组是一个数组,只不过这个数组中,存放的是指向int或char等其他类型的指针,在同一个数组中,只能存放指向同一个类型的指针
  • 格式:type *ptr[]
  • 代码实现:
    #include <stdio.h>
    
    int main()
    {
        //存放整数地址的数组
        printf("存放整数地址的数组\n");
        int array_int[] = {0, 1, 2};
        int *array_int_ptr[3];//必须声明指针数组的大小
        int array_int_size = sizeof(array_int) / sizeof(array_int[0]);
        for (int i = 0; i < array_int_size; i++)
        {
            array_int_ptr[i] = &array_int[i];
        }
        for (int i = 0; i < array_int_size; i++)
        {
            printf("array_int[%d] = %d,ptr=%p\n", i, *array_int_ptr[i], array_int_ptr[i]);
        }
        //存放字符地址的数组
        printf("存放字符地址的数组\n");
        char array_char[] = {'a', 'b', 'c'};
        char *array_char_ptr[3];
        int array_char_size = sizeof(array_char) / sizeof(array_char[0]);
        for (int i = 0; i < array_char_size; i++)
        {
            array_char_ptr[i] = &array_char[i];
        }
        for (int i = 0; i < array_char_size; i++)
        {
            printf("array_char[%d] = %c,ptr=%p\n", i, *array_char_ptr[i], array_char_ptr[i]);
        }
    }
    
  • 运行结果:
    存放整数地址的数组
    array_int[0] = 0,ptr=0x7ffc60d0c9c4
    array_int[1] = 1,ptr=0x7ffc60d0c9c8
    array_int[2] = 2,ptr=0x7ffc60d0c9cc
    存放字符地址的数组
    array_char[0] = a,ptr=0x7ffc60d0ca15
    array_char[1] = b,ptr=0x7ffc60d0ca16
    array_char[2] = c,ptr=0x7ffc60d0ca17
    

3.2 数组指针

  • 定义:
    数组指针是一个指针,其存放着一个数组的首地址,长度固定(系统不同,长度不同)
  • 代码实现:
    #include<stdio.h>
    
    void main(){
        int arr[] = {0,1,2};
        //表示p是一个指针,指针指向一个数组,数组中有三个元素,元素类型为整数
        int (*p)[3];
        p = &arr;//将数组的地址传递给指针,则*p == arr,p[i]为数组元素的地址
        for(int i = 0; i < 3; i++){
            printf("arr[%d]=%d,ptr=%p\n",i,(*p)[i],p[i]);
        }
        printf("p的大小:%ld\n",sizeof(p));
    }
    
  • 运行结果:
    arr[0]=0,ptr=0x7ffed0e7a53c
    arr[1]=1,ptr=0x7ffed0e7a548
    arr[2]=2,ptr=0x7ffed0e7a554
    p的大小:8
    

四、字符串与指针

4.1. 字符串的定义与遍历

字符串其实就是字符数组,归根结底是一个数组

  • 代码实现:
    #include<stdio.h>
    #include<string.h>
    
    void main(){
        char str[] = "hello world!";
        int len = strlen(str);//计算字符串长度,不包括末尾的\0
        int size = sizeof(str);//计算字符串的长度,双引号之间的都包括,包括\0
        printf("字符串长度(strlen):%d\n",len);
        printf("字符串长度(sizeof):%d\n",size);
        printf("每次输出一个字符:");
        for(int i = 0; i < len; i++){
            printf("%c",str[i]);
        }
        printf("\n");
        printf("直接输出字符串:%s\n\n",str);
        //在C语言中,通过判断末尾的\0,来判断字符串是否结束
        char str1[] = "hello worl\0d!";
        int len1 = strlen(str1);//计算字符串长度,不包括末尾的\0
        int size1 = sizeof(str1);//计算字符串的长度,双引号之间的都包括,包括\0
        printf("字符串长度(strlen):%d\n",len1);
        printf("字符串长度(sizeof):%d\n",size1);
        printf("每次输出一个字符:\n");
        printf("通过len1:");
        for(int i = 0; i < len1; i++){
            printf("%c",str1[i]);
        }
        printf("\n");
        printf("通过size1:");
        for(int i = 0; i < size1; i++){
            printf("%c",str1[i]);
        }
        printf("\n");
        printf("直接输出字符串:%s\n",str1);
    }
    
  • 运行结果:
    d102@d102-W65KJ1-KK1:/media/d102/EPAN/Desktop/c++_ubuntu/c++_01/workspace$ gcc test2.c -o t2.o
    d102@d102-W65KJ1-KK1:/media/d102/EPAN/Desktop/c++_ubuntu/c++_01/workspace$ ./t2.o 
    字符串长度(strlen):12
    字符串长度(sizeof):13
    每次输出一个字符:hello world!
    直接输出字符串:hello world!
    
    字符串长度(strlen):10
    字符串长度(sizeof):14
    每次输出一个字符:
    通过len1:hello worl
    通过size1:hello world!
    直接输出字符串:hello worl
    

4.2 使用指针输出字符数组

  • 代码实现:
    #include <stdio.h>
    #include <string.h>
    
    int main()
    {
        char str[] = "hello world";
        //直接指向字符串的指针
        char *str_p = "hello world";
        char *p = str; //存放字符串首个元素的地址,str代表数组首个元素的地址
        printf("指针的内存大小:%ld\n", sizeof(str_p));
    
        printf("str_p:%s\n", str_p);
        printf("str:%s\n", str);
    
        printf("str_p首地址:%p\n", str_p);
        printf("str首地址:%p\n", str);
    
        printf("str_p str_p[4]:%c\n", str_p[4]);
        printf("str str[4]:%c\n", str[4]);
    
        printf("str_p str_p[4]:%c\n", *(str_p + 4));
        printf("str str[4]:%c\n", *(str + 4));
        
        int len = strlen(str);
        // 直接打印,在printf函数中,%s格式的输出
        // 当遇到\0,即认为字符串结束,才会停止打印
        
        //单个字符打印,*(p+i)
        printf("单个字符打印 *(p+i) :");
        for (int i = 0; i < len; i++)
        {
            printf("%c", *(p + i));
        }
        printf("\n");
    
        //单个字符打印,*(str+i)
        printf("单个字符打印 *(str+i):");
        for (int i = 0; i < len; i++)
        {
            printf("%c", *(str + i));
        }
        printf("\n");
                
        //使用p[i]
        printf("使用p[i]打印:");
        for (int i = 0; i < len; i++)
        {
            printf("%c", p[i]);
        }
        printf("\n");
    
        return 0;
    }
    
  • 运行结果:
    指针的内存大小:8
    str_p:hello world
    str:hello world
    str_p首地址:0x563c2f0bb008
    str首地址:0x7ffdf7a46fdc
    str_p str_p[4]:o
    str str[4]:o
    str_p str_p[4]:o
    str str[4]:o
    单个字符打印 *(p+i) :hello world
    单个字符打印 *(str+i):hello world
    使用p[i]打印:hello world
    

4.3 字符数组(字符串变量)与字符指针(字符串常量)

4.3.1 区别与联系

从4.2小结可以看出,字符数组和字符指针在使用上没有任何区别,都是字符串的两种表示形式,但是它们并不完全一样。字符数组是可以读写的,而字符指针只可以读,不可以写

  • 字符数组形式:
    char str[] = "hello world";
    
  • 字符指针形式:
    char *str = "hello world";
    
  • 代码实现:
    #include<stdio.h>
    
    int main(){
    
        char str[] = "hello world";
        str[0] = 'x';
        str[1] = 'x';
        printf("str:%s\n",str);
    
        char *str_p = "hello world";
        str_p = "xiao ma ge";
        // str_p[0] = 'x'; //报错:Segmentation fault
        printf("str_p:%s\n",str_p);
    
    }
    
  • 运行结果:
    str:xxllo world
    str_p:xiao ma ge
    

4.3.2 存储方式

  • 字符数组由若干元素组成,每个元素存放一个字符
  • 字符指针只存放字符串的首地址,不是整个字符串

4.3.3 赋值方式

  • 字符数组下面的赋值方式错误,只能一个元素一个元素赋值:
    char str[20];
    str = "hello";
    
  • 字符指针可以采用下面的赋值方式:
    char *str;
    str = "hello";
    

4.3.4 可否被修改

字符指针可以改变其指向的内容,也可以改变其指向,但是不能更改其指向内容里的某一元素

  • 代码实现:
    #include<stdio.h>
    
    int main(){
    
        char str[] = "hello world";
        str[0] = 'x';
        str[1] = 'x';
        printf("str:%s\n",str);
    
        char *str_p = "hello world";
        str_p = "xiao ma ge";//改变指向内容
        // str_p[0] = 'x'; //报错:Segmentation fault
        printf("str_p:%s\n",str_p);
    
    
        char c[] = "abc";
        str_p = c;//改变其指向
        printf("str_p:%s\n",str_p);
    }
    
  • 运行结果:
    str:xxllo world
    str_p:xiao ma ge
    str_p:abc
    

4.3.5 初始化

  • 字符数组:
    char str[10];
    scanf("%s",str);
    
  • 字符指针:
    char *p = NULL;//防止出现野指针
    p = (char *)malloc(10);
    

五、指针的指针

指针用来存放变量的地址,同时,指针也有自己的地址,因此,就可以设置一个指针变量,用来存放指针的地址,也就是指针的指针,他存放的是一个地址,通过取值符*可以取出相应位置的值

  • 格式:**p

  • 图片解释:
    在这里插入图片描述

  • 代码实现:

    #include<stdio.h>
    
    int main(){
        int a = 10;
        int *p1;
        int **p2;
        int ***p3;
        p1 = &a;
        p2 = &p1;
        p3 = &p2;
        printf("a的值:%d\n",a);
        printf("p1地址的值(a的地址):%p\n",p1);
        printf("p1地址存放的值(a的值):%d\n",*p1);
        printf("p2地址的值(p1的地址):%p\n",p2);
        printf("p2地址存放的值(p1的值,a的地址):%p\n",*p2);
        printf("p2地址存放的整数的值(a的值):%d\n",**p2);
        printf("p3地址的值(p2的地址):%p\n",p3);
        printf("p3地址存放的值(p2的值,p1的地址):%p\n",*p3);
        printf("p3地址存放的整数的值(a的值):%d\n",***p3);
        return 0;
    }
    
  • 运行结果:

    a的值:10
    p1地址的值(a的地址):0x7ffd25ad858c
    p1地址存放的值(a的值):10
    p2地址的值(p1的地址):0x7ffd25ad8590
    p2地址存放的值(p1的值,a的地址):0x7ffd25ad858c
    p2地址存放的整数的值(a的值):10
    p3地址的值(p2的地址):0x7ffd25ad8598
    p3地址存放的值(p2的值,p1的地址):0x7ffd25ad8590
    p3地址存放的整数的值(a的值):10
    

六、传递指针给函数

C语言允许传递指针给函数

  • 代码实现:
    #include<stdio.h>
    #include<time.h>
    // 指针作为函数参数
    void getSeconds(unsigned long *p){
        *p = time(NULL);
    }
    // 指向数组的指针作参数
    double getAverage(int *arr, int size){
        int i, sum;
        double avg;
        for(i = 0; i < size; i++){
            sum += arr[i];
        }
        avg = (double)sum / size;
        return avg;
    }
    
    int main(){
        unsigned long second;
        int balance[5] = {1,2,3,4,5};
        getSeconds(&second);
        printf("当前时间:%ld\n",second);
        double avg;
        avg = getAverage(balance,5);
        printf("平均值:%f\n",avg);
        return 0;
    }
    
  • 运行结果:
    当前时间:1657786996
    平均值:3.000000
    

七、从函数返回指针

C语言支持从函数返回指针

  • 代码实现:
    #include<stdio.h>
    #include<time.h>
    #include<stdlib.h>
    
    int *getRandom(){
        /*
            C语言不支持在调用函数时,返回局部变量的地址
            除非定义局部变量为static
        */
        static int r[10];
        int i;
        srand((unsigned)time(NULL));//设置种子
        for(i = 0; i < 10; i++){
            r[i] = rand();
            // printf("%d\n",r[i]);
        }
        return r;//返回数组首地址
    }
    int main(){
        int *p;
        int i;
        // 函数执行后,其局部变量会被释放,那么局部变量就不能访问了
        // 为了解决这一问题,使用static关键字,使局部变量保存整个程序周期
        p = getRandom();
        for(i = 0; i < 10; i++){
            printf("*(p + %d):%d\n",i,*(p+i));
        }
        return 0;
    }
    
  • 运行结果:
    *(p + 0):406451966
    *(p + 1):1274401563
    *(p + 2):1286473475
    *(p + 3):95700003
    *(p + 4):1434496087
    *(p + 5):1913921399
    *(p + 6):1648156783
    *(p + 7):475047123
    *(p + 8):1060638060
    *(p + 9):378428022
    

八、指针的一些复杂说明

  • int p; – 这是一个普通的整型变量
  • int * p; – 首先从 p 处开始,先结合,所以说明 p 是一个指针, 然后再与 int 结合, 说明指针所指向的内容的类型为 int 型。所以 p 是一个返回整型数据的指针。
  • int p[3] – 首先从 p 处开始,先与[] 结合,说明 p 是一个数组, 然后与 int 结合, 说明数组里的元素是整型的, 所以 p 是一个由整型数据组成的数组。
  • int *p[3]; – 首先从 p 处开始, 先与 [] 结合, 因为其优先级比 * 高,所以 p 是一个数组, 然后再与 * 结合, 说明数组里的元素是指针类型, 然后再与 int 结合, 说明指针所指向的内容的类型是整型的, 所以 p 是一个由返回整型数据的指针所组成的数组。
  • int (*p)[3]; – 首先从 p 处开始, 先与 * 结合,说明 p 是一个指针然后再与 [] 结合(与"()"这步可以忽略,只是为了改变优先级), 说明指针所指向的内容是一个数组, 然后再与int 结合, 说明数组里的元素是整型的。所以 p 是一个指向由整型数据组成的数组的指针。
  • int **p; – 首先从 p 开始, 先与 * 结合, 说是 p 是一个指针, 然后再与 * 结合, 说明指针所指向的元素是指针, 然后再与 int 结合, 说明该指针所指向的元素是整型数据。由于二级指针以及更高级的指针极少用在复杂的类型中, 所以后面更复杂的类型我们就不考虑多级指针了, 最多只考虑一级指针。
  • int p(int); – 从 p 处起,先与 () 结合, 说明 p 是一个函数, 然后进入 () 里分析, 说明该函数有一个整型变量的参数, 然后再与外面的 int 结合, 说明函数的返回值是一个整型数据。
  • int (*p)(int); – 从 p 处开始, 先与指针结合, 说明 p 是一个指针, 然后与()结合, 说明指针指向的是一个函数, 然后再与()里的 int 结合, 说明函数有一个int 型的参数, 再与最外层的 int 结合, 说明函数的返回类型是整型, 所以 p 是一个指向有一个整型参数且返回类型为整型的函数的指针。
  • int *(*p(int))[3]; – 可以先跳过, 不看这个类型, 过于复杂从 p 开始,先与 () 结合, 说明 p 是一个函数, 然后进入 () 里面, 与 int 结合, 说明函数有一个整型变量参数, 然后再与外面的 * 结合, 说明函数返回的是一个指针, 然后到最外面一层, 先与[]结合, 说明返回的指针指向的是一个数组, 然后再与 * 结合, 说明数组里的元素是指针, 然后再与 int 结合, 说明指针指向的内容是整型数据。所以 p 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数。
    参考链接
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章           查看所有文章
加:2022-07-21 21:19:47  更:2022-07-21 21:22:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:30:32-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码