筑基九层 —— 指针详解

这篇具有很好参考价值的文章主要介绍了筑基九层 —— 指针详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

前言:

指针详解


前言:

1.CSDN由于我的排版不怎么好看,我的有道云笔记比较美观,请移步有道云笔记

2.修炼必备

  1)入门必备:VS2019社区版,下载地址:Visual Studio 较旧的下载 - 2019、2017、2015 和以前的版本 (microsoft.com)

  2)趁手武器:印象笔记/有道云笔记

  3)修炼秘籍:牛客网 - 找工作神器|笔试题库|面试经验|实习招聘内推,求职就业一站解决_牛客网 (nowcoder.com)

  4)雷劫必备:leetcode 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 

  注:遇到瓶颈怎么办?百度百科_全球领先的中文百科全书 (baidu.com)

指针详解

        ——指针是C语言中最核心的部分,不了解指针,就掌握不了C语言的精髓

1.指针是什么?

1)指针是内存中一个最小单元的编号,即指针就是地址

2)指针变量是用来存放内存地址的变量

        图解:

筑基九层 —— 指针详解

2.我们如何取出变量的地址?

        ——使用&符号取出变量的地址,使用相应数据类型的指针变量保存该地址

#include <stdio.h>

int main()
{
    int num = 10;
    int* p = &num;//取出了num的地址赋给了p
    
    //打印查看地址
    printf("&num = %p\n", &num);
    printf("p = %p\n", p);
    return 0;
}

         运行结果:

筑基九层 —— 指针详解

3.指针的大小

        ——32位平台下的指针大小是4个字节,64位的平台下指针的大小是8个字节

#include <stdio.h>

int main()
{
    printf("%d\n", sizeof(char*));
    printf("%d\n", sizeof(short*));
    printf("%d\n", sizeof(int*));
    printf("%d\n", sizeof(long*));
    printf("%d\n", sizeof(long long*));
    printf("%d\n", sizeof(float*));
    printf("%d\n", sizeof(double*));
    return 0;
}

        32位平台运行结果:

筑基九层 —— 指针详解

        64位平台运行结果: 

筑基九层 —— 指针详解

 

4.指针和指针类型

        1)指针的定义的方式

a.指针的定义方式:数据类型*;

        char*:字符指针

        int*:整型指针

        long long*:长长整型指针

        float*:单精度指针

        double*:双精度指针

b.一般情况下,那种类型的指针则存储那种类型变量的地址

#include <stdio.h>

int main()
{
    char c = 'a';
    int num = 10;
    float f = 1.342;
    double data = 13.14;
    
    //一般情况,那种类型的指针变量存储那种类型变量的地址
    char* ch = &c;
    int* p = &num;
    float* p1 = &f;
    double* p2 = &data;
    return 0;
}

        2)指针的类型决定了指针向前或向后走一步有多大的字节距离

#include <stdio.h>

int main()
{
    char c = 'c';
    int num = 10;
    
    char* p1 = &c;
    int* p2 = &num;
    
    printf("%p\n", p1);
    printf("%p\n", p1+1);
    printf("%p\n", p2);
    printf("%p\n", p2+1);
    return 0;
}

       运行结果:

筑基九层 —— 指针详解

        3)指针的类型决定了指针在解引用的时候能操作几个字节【访问权限多大】 

#include <stdio.h>

int main()
{
    int num = 0x44332211;
    
    char* p1 = &num;
    int* p2 = &num;
    printf("%d\n", *p1);
    printf("%d\n", *p2);
    
    *p1 = 0;
    *p2 = 0;
    return 0;
}

        调试查看结果:

筑基九层 —— 指针详解

筑基九层 —— 指针详解

5. 野指针的问题

野指针就是指针指向的位置是不可知的【随机、不正确、无限制】

        1)指针未初始化

#include <stdio.h>

int main()
{
    int* p;
    printf("%p\n", p);
    return 0;
}

        运行结果: 

筑基九层 —— 指针详解

        2)指针越界访问 

#include <stdio.h>

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int* p = arr;
    
    for (int i = 0; i <= 11; i++)
    {
        //p的访问范围超过了数组的下标范围,p就是野指针
        printf("%d ", *p);
        p++;
    }
    return 0;
}

        3)返回局部变量的地址

#include <stdio.h>
int test()
{
    int a = 10;
    return &a;
}

int main()
{
    int *p = test();
    return 0;
}

        4)指针指向的空间未释放

6.防止野指针的问题

1)指针初始化

2)小心指针越界

3)指针指向空间释放,及时置为NULL

4)避免返回局部变量的地址

5)使用指针之前检查指针的有效性

#include <stdio.h>

void test(int* p)
{
    //检查指针的有效性
    if(p == NULL){}
}

int main()
{
    int* p = NULL;//不知道指针指向哪里的时候置为NULL
    test(p);
    return 0;
}

 

7.指针运算

        1)指针+-整数

#include <stdio.h>

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int* p;
    for (p = arr; p < &arr[10]; p++)
    {
        printf("%d ", *p);
    }
    return 0;
}

        2)指针-指针【地址-地址】

两个指针指向的是同一块空间且类型是一致的,两者相减得到是元素个数

#include <stdio.h>

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int* p1 = arr;
    int* p2 = &arr[10];
    
    //指针-指针
    printf("%d\n", p2 - p1);//10
    return 0;
}

        3)指针的关系运算

#include <stdio.h>

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int* p;
    for (p = &arr[10]; p > &arr[0];)
    {
        *--p = 0;
    }
    return 0;
}

        为什么这种方法也可以,但是不使用呢?

#include <stdio.h>

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int* p;
    for (p = &arr[9]; p >= &arr[0]; p--)
    {
        *p = 0;
    }
    return 0;
}

C语言中的标准规定,允许指向数组元素的指针能与指向数组最后一个元素的后面那个内存位置的指针比较,但不允许与数组元素第一个元素前面的那一个内存地址的指针比较

8.指针与数组

        1)数组名是数组首元素的地址,&数组名是取出整个数组的地址

#include <stdio.h>

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int* p = arr;
    
    printf("arr = %p\n", arr);
    printf("arr + 1 = %p\n", arr + 1);
    printf("&arr = %p\n", &arr);
    printf("&arr + 1 = %p\n", &arr + 1);
    return 0;
}

         运行结果:

筑基九层 —— 指针详解

        2)指针能指向数组的任意一个元素,即数组每个元素的地址指针均能获取 

#include <stdio.h>

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int* p = arr;
    
    for (int i = 0; i < 10; i++)
    {
        printf("%p == %p\n", &arr[i], p+i);
    }
    return 0;
}

        运行结果:

筑基九层 —— 指针详解

9.二级指针【指针的指针】 

int num = 10;

int* p = &num;//*p代表这是指针,int表示指向的类型是int类型

int**pp = &p;//*pp代表是指针,int*表示指向的类型是int*类型

#include <stdio.h>

int main()
{
    int num = 10;
    int* p = &num;//*p表示这是一个指针,指向的类型是int
    int** pp = &p;//*pp表示这是一个指针,指向的类型是int*
    
    printf("%d\n", num);//10
    printf("%d\n", *p);//10
    printf("%d\n", **pp);//10
    return 0;
}

 

10.字符指针的使用方式

1)字符指针指向一个字符变量

2)字符指针指向一个常量字符串【常量字符串的首元素地址赋给字符指针】

#include <stdio.h>

int main()
{
    char ch = 'a';
    //字符指针指向一个字符变量
    char* p1 = &ch;
    printf("%c\n", *p1);
    
    //字符指针指向一个常量字符串
    char* p2 = "abcdef";
    printf("%c\n", *p2);
    printf("%s\n", p2);
    return 0;
}

        运行结果: 

筑基九层 —— 指针详解

        一道简单的笔试题:

#include <stdio.h>

int main()
{
    char str1[] = "abcdef";
    char str2[] = "abcdef";
    const char* arr1 = "abcdef";
    const char* arr2 = "abcdef";
    
    if (str1 == str2)
    {
        printf("str1 and str2 are same\n");
    }
    else
    {
        printf("str1 and str2 are not same\n");
    }
    
    if (arr1 == arr2)
    {
        printf("arr1 and arr2 are same\n");
    }
    else
    {
        printf("arr1 and arr2 are not same\n");
    }
    return 0;
}

        运行结果:

筑基九层 —— 指针详解

 为什么?当const char*是存储字符串常量的时候,两个指针均指向字符串常量首元素地址

11.指针数组【数组】

整型数组:数据是整型的数组

浮点数组:数据是浮点型的数组

指针数组:数据是指针的数组【即数组中的元素是指针类型】

        图解三种数组类型:

筑基九层 —— 指针详解

#include <stdio.h>

int main()
{
    int arr1[5] = { 1,2,3,4,5 };
    int arr2[5] = { 2,3,4,5,6 };
    int arr3[5] = { 3,4,5,6,7 };
    
    int* arr[3] = { arr1,arr2,arr3 };//存储了三个数组的首元素地址
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 5; j++)
        {
            printf("%d ", *(arr[i] + j));//arr[i][j]
        }
        printf("\n");
    }
    return 0;
}

        运行结果:

筑基九层 —— 指针详解

12.数组指针【指针】

        ——指向数组的类型 (*p)[大小] = &所指数组名

int num = 10; int* p = &num;//指向int的指针

double data = 13.14; double* p = &data;//指向double的指针

int arr[10]; int (*p)[10] = &arr;//指向数组的指针

        ——如何判断数组指针

()的结合性比[]高,所以先与*()里面的*号结合,再与[]结合 -> 指针

#include <stdio.h>

void printArr(int(*p)[5], int row, int col)
{
    for (int i = 0; i < row; i++)
    {
        for (int j = 0; j < col; j++)
        {
            //printf("%d ", p[i][j]);
            //printf("%d ", *(*(p + i) + j));
            printf("%d ", *(p[i] + j));
        }
        printf("\n");
    }
}

int main()
{
    int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
    int row = sizeof(arr) / sizeof(arr[0]);
    int col = sizeof(arr[0]) / sizeof(arr[0][0]);
    printArr(arr, row, col);
    return 0;
}

 

13.数组传参和指针传参

        1)一维数组传参

#include <stdio.h>

//一维数组传参
//传参方式1
void test(int arr[]){}
//传参方式2
void test1(int arr[10]){}
//传参方式3
void test2(int* arr){}

//一维指针数组传参
//传参方式1
void demo1(int* p[10]){}
//传参方式2
void demo2(int** arr){} 
//解释:*arr接收str数组,另一个*表示元素是指针

int main()
{
    int arr[5] = { 0 };
    test(arr);
    test1(arr);
    test2(arr);
    
    //一维指针数组传参
    int* str[10] = { 0 };
    demo1(str);
    demo2(str);
    return 0;
}

        2)二维数组的传参方式

#include <stdio.h>

//传参方式1
void test(int arr[3][3]){}
//传参方式2
void test1(int arr[][3]){}
//传参方式3
void test2(int(*arr)[3]){}

int main()
{
    int arr[3][3] = { 0 };
    test(arr);
    test1(arr);
    test2(arr);
    return 0;
}

        3)一级指针传参

#include <stdio.h>
//传参方式1
void test(int* p){}
//传参方式2
void test1(int* *p){}

int main()
{
    int* p = NULL;
    test(p);
    return 0;
}

        4)二级指针传参

#include <stdio.h>

void test(int** p) {}

int main()
{
    int** p = NULL;
    test(p);
    return 0;
}

14.函数指针

        ——指向的函数返回值 (*p)(指向函数的形参) = &所指函数名

int* p; //指向int的指针

double* p; //指向double的指针

int (*p)(int,int);//指向函数的指针

//解答:*和p结合,说明是指针,然后和(int,int)结合,说明是函数,int是返回值

#include <stdio.h>

int add(int x, int y)
{
    return x + y;
}

int main()
{
    //函数是有地址的
    //printf("%p\n", &add);
    
    int (*p)(int, int) = &add;
    int ret = p(5, 3);
    printf("%d\n", ret);//8
    return 0;
}

         思考以下代码

//代码1

(*(void (*)())0)();

//代码2

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

解释
代码1:把0强制转为为void(*)()的函数指针,在0地址处有一个函数,
函数无返回值,无形参,这个地方表调用

代码2:函数名是signal,参数为int和函数指针void(*)(int),
signal的返回类型是函数指针,该函数指针的函数返回值是void,参数是int

 

15.函数指针数组

        ——把函数地址存储在数组里面,这个数组就叫做函数指针数组

int (*p[])(形参);

        ——使用途径:转移表文章来源地址https://www.toymoban.com/news/detail-401237.html

#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) = { NULL, Add, Sub, Mul, Div };
                //0    1    2    3    4

    do
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        if (input == 0)
        {
            printf("退出计算器\n");
            break;
        }
        else if (input >= 1 && input <= 4)
        {
            printf("请输入两个操作数:>");
            scanf("%d %d", &x, &y);
            ret = pfArr[input](x, y);
            printf("%d\n", ret);
        }
        else
        {
            printf("选择错误\n");
        }
    } while (input);
    return 0;
}

到了这里,关于筑基九层 —— 指针详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 算法修炼之练气篇——练气十九层

    博主:命运之光 专栏:算法修炼之练气篇 前言:每天练习五道题,炼气篇大概会练习200道题左右,题目有C语言网上的题,也有洛谷上面的题,题目简单适合新手入门。(代码都是命运之光自己写的,练完这200多道题就考了今年第十四届的B组蓝桥杯C/C++获得了省一,后面还会

    2024年02月05日
    浏览(73)
  • 六个思考维度:DDD + SpringBoot工程九层结构图解与实战

    欢迎大家关注公众号「JAVA前线」查看更多精彩分享文章,主要包括源码分析、实际应用、架构思维、职场分享、产品思考等等,同时欢迎大家加我微信「java_front」一起交流学习 计算机领域有一句话:计算机中任何问题都可通过增加一个虚拟层解决。这句体现了分层思想重要

    2024年02月05日
    浏览(31)
  • 算法修炼之筑基篇——筑基二层后期(初步理解解决贪心算法)

    ✨ 博主: 命运之光 🦄 专栏: 算法修炼之练气篇 🍓 专栏: 算法修炼之筑基篇 ✨ 博主的其他文章: 点击进入博主的主页 前言: 学习了算法修炼之练气篇想必各位蒟蒻们的基础已经非常的扎实了,下来我们进阶到算法修炼之筑基篇的学习。筑基期和练气期难度可谓是天差

    2024年02月11日
    浏览(35)
  • 算法修炼之筑基篇——筑基一层中期(解决01背包,完全背包,多重背包)

    ✨ 博主: 命运之光​​​​​​ 🦄 专栏: 算法修炼之练气篇​​​​​ 🍓 专栏: 算法修炼之筑基篇 ✨ 博主的其他文章: 点击进入博主的主页​​​​​​ 前言: 学习了算法修炼之练气篇想必各位蒟蒻们的基础已经非常的扎实了,下来我们进阶到算法修炼之筑基篇的

    2024年02月08日
    浏览(32)
  • C语言--指针详解(下)--字符指针、数组指针、指针数组、函数指针、函数指针数组(转移表)

    在C语言中有一种指针类型为字符指针 char*,常用其来表示字符,使用如下: 除了上述用法之外,还可以有以下的用法: 在上面的代码中,字符 \\\" hello word \\\"是常量字符串,将\\\"hello word\\\"放入 pstr 指针的实质是将第一个字符 “ h \\\" 的地址传递给了 pstr ,通过首字符 ” h \\\" 就可以访问

    2024年02月03日
    浏览(47)
  • 【C/C++】 常量指针,指针常量、指向常量的常指针详解

    指针就是指向变量在内存中的地址 数据是存放在内存中的,每一个变量都有一个内存地址,假设是一个int类型变量 a ,占4个字节的内存区,那么在内存中如果是小端方式存储,我们创建指针p,把a的地址赋值给 p ,就是把a的首地址0x1100赋值给指针 p ,这个时候p的值就是变量

    2024年02月13日
    浏览(42)
  • 【蓝桥杯-筑基篇】排序算法

    🍓系列专栏:蓝桥杯 🍉个人主页:个人主页 目录 前言: 一、冒泡排序 二、选择排序 三、插入排序 四、图书推荐 算法工具推荐:  还在为数据结构发愁吗?这款可视化工具,帮助你更好的了解其数据结构数据结构和算法动态可视化 (Chinese) - VisuAlgo ​ 1.什么是冒泡排序? 冒

    2024年02月02日
    浏览(42)
  • 音频筑基:算法时延分析

    音频算法中,经常遇到时延分析的问题,刚开始接触大多都比较迷惑,这里将自己对时延的学习思考梳理总结于此。 音频领域中,时延(delay/latency)主要指声音从源端发出,经链路传输,再到对端接收到声音,所经过的总时间延迟。一般人耳无法感知的蓝牙段链路时延是25-30

    2024年01月17日
    浏览(35)
  • 【蓝桥杯-筑基篇】贪心

    🍓系列专栏:蓝桥杯 🍉个人主页:个人主页 目录 1.找零问题 ①暴力枚举 ②贪心 2.人性总是贪婪的 3.堆果子 4.图书推荐 有币种 1 、 2 、 4 、 5 、 10 若干张,找零 n 元,输出找零方案。 ①暴力枚举 这是一个找零问题,我们需要找到一种方案,使得用给定的硬币找零时,所需的

    2024年01月18日
    浏览(39)
  • 【蓝桥杯-筑基篇】搜索

    🍓系列专栏:蓝桥杯 🍉个人主页:个人主页 目录 递归树 1.递归构建二进制串  2.全排列的 DFS 解法 3.全排列的 BFS 解法 4.数的划分法 5.图书推荐 递归树 递归树是一种用于分析递归算法时间复杂度的工具。它可以将递归算法的执行过程可视化,从而更好地理解算法的时间复杂度

    2024年01月16日
    浏览(77)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包