您现在的位置是:首页 >技术交流 >c/c++:指针p+p-p*p/,数组&a+1,指针减指针,指针实现strlen函数,指针的比较运算,指针数组,多级指针网站首页技术交流

c/c++:指针p+p-p*p/,数组&a+1,指针减指针,指针实现strlen函数,指针的比较运算,指针数组,多级指针

冰露可乐 2023-06-06 00:00:02
简介c/c++:指针p+p-p*p/,数组&a+1,指针减指针,指针实现strlen函数,指针的比较运算,指针数组,多级指针

c++:

2022找工作是学历、能力和运气的超强结合体,遇到寒冬,大厂不招人,此时学会c++的话,
我所知道的周边的会c++的同学,可手握10多个offer,随心所欲,而找啥算法岗的,基本gg
提示:系列c++学习的基础和高阶知识,用于公司生产实践中,实实在在的公司部署产品要用的,因为c++速度快,
而java和Python速度慢,自然往硬件里面部署算法啥的,都得用c++或者c,因此本科学的c很重要,后来的Python或者java就没有那么重要了,
c/c++系列文章:
【1】c++:c语言优缺点,visual studio2019如何新建项目,写hello world程序
【2】c/c++:gcc安装,gcc编译hello world文件,system函数调用系统命令,sleep函数
【3】linux下gcc的编译过程和功能,预处理,编译,汇编,链接,.c预处理为.i文件.s文件.o文件.exe文件
【4】c/c++:windows平台下依赖的动态库,c底层是汇编语言,程序断点调试,反汇编,vs快捷键
【5】c/c++:数据类型,常量变量,标识符,有符号整型,无符号unsigned,字符类型,字符串类型,实数类型,浮点型,科学计数法
【6】c/c++:2进制、8进制、10进制、16进制和进制之间的转换,c语言输出匹配格式%
【7】c/c++:原码,反码,补码和常见的数据类型取值范围,溢出
【8】c/c++:类型限定符,printf输出格式,putchar,scanf,getchar
【9】c/c++:算术运算符,赋值运算,逻辑运算,比较运算,三目运算,逗号运算,数据类型转换
【10】c/c++:顺序结构,if else分支语句,do while循环语句,switch case break语句
【11】c/c++:for循环语句,分号不可省略,表达式可以省略,猜数字游戏,跳转语句continue,break,避免写goto
【12】c/c++:一维数组,初始化数组,循环打印数组,计算数组存储空间,数组元素个数,数组逆序算法
【13】c/c++:二维数组,数组的行数和列数求法sizeof,数组初始化不同形式,5个学生,3门功课,求学生总成绩和功课总成绩
【14】c/c++:visual studio的代码快捷键,VS设置自定义默认代码,使用快捷键
【15】c/c++:三维数组,字符数组和字符串,统计字符串中字符出现的频次,scanf输入空格,正则匹配表达式
【16】c/c++:gets(),fgets(),puts(),fputs(),strlen(),字符串拼接函数
【17】c/c++:函数的作用,分类,随机数,函数定义,调用,申明,exit()函数,多文件编程,防止头文件重复
【18】c/c++:指针,指针定义和使用,指针大小4字节,野指针,空指针*p=NULL
【19】c/c++:万能指针,泛型指针,const int *p,int const *p,int *const p,const int *const p,指针与数组,p++,


c/c++:指针p+p-p*p/

在这里插入图片描述
在这里插入图片描述

void f67(void)
{
    int a = 10;
    int b = 20;

    int* p = &a;
    //printf("%p
", p/2);//不能乘除?
}

指针不能做乘除运算
你把地址乘除有啥意义
加减是可以的

前后挪动多少字节



void f67(void)
{
    int a = 10;
    int b = 20;

    int* p = &a;
    //printf("%p
", p/2);//不能乘除?
    printf("%p
", p + 2);//不能乘除?
    printf("%p
", p - 2);//不能乘除?
}


int main(void)
{
    f67();

    system("pause");
    return 0;
}

在这里插入图片描述
数组中加减整数也是前后挪动一个位置
数组中说偏字节没用
就是挪动一个位置

在这里插入图片描述
在这里插入图片描述
左右移动地址
好说

数组名取地址+1

是啥?


void f68(void)
{//指针和数组
    int a[] = { 1,2,4 };
    int* p = a;//b本身就是地址

    printf("%p
", a);
    printf("%p
", &a[0]);
    printf("%p
", p+1);//这是取地址
    printf("%p
", a+1);//这是取地址

    printf("%p
", &a);//这是取地址
    printf("%p
", &a+1);//这是取地址---很猛
}


int main(void)
{
    f68();

    system("pause");
    return 0;
}

在这里插入图片描述
发现了吗,&a+1,挪动了12个字节【即a数组大小】
a+1=p+1,是p+1挪动了一个字节而已,到下一个元素
而&a+1
是加整个数组的字节,p挪动到整个数组后面了

牛了这个
在这里插入图片描述
&a不是取数组首个地址【因为a=&a[0]】
而是整个数组的地址
所以你+1
加的是数组类型整个这么大的空间

指针+指针



void f69(void)
{
    int a = 10;
    int b = 20;

    int* p = &a;
    int* q = &b;

    //printf("%p
", p+q);//不可以操作

    printf("%p
", p);//可以操作
    printf("%p
", q);//可以操作
    printf("%p
", p-q);//可以操作
    
}

在这里插入图片描述
对于普通变量
指针可以减,不可以加
但是没意义

那要是数组呢?



void f70(void)
{//指针和数组
    int a[] = { 1,2,3,4,5,6,7,8,9,0 };
    int* p = &a[3];
    printf("%p
", p);//可以操作
    printf("%p
", a);//可以操作
    printf("%p
", p - a);//可以操作
}

int main(void)
{
    f70();

    system("pause");
    return 0;
}

在这里插入图片描述
C=12=3个int自己

实际也是类型这么个差距
int是3个这里

再看

void f70(void)
{//指针和数组
    int a[] = { 1,2,3,4,5,6,7,8,9,0 };
    int* p = &a[2];
    printf("%p
", p);//可以操作
    printf("%p
", a);//可以操作
    printf("%p
", p - a);//可以操作
}

偏移过的元素个数
在这里插入图片描述
数组中指针相减
是俩指针之间的元素的偏移个数

相加不允许哦

在这里插入图片描述
数组读完后,野指针p-a的话
就是整个数组的偏移个数,也是整个数组的大小
懂???

用指针实现strlen函数

非指针形式


int mystrlen(char str[]) {
    //传入字符数组
    //原来是
    int i = 0;
    while (str[i] != '') i++;

    return i;//统计结果返回
}

void f71()
{
    char str[] = "hello";
    int len = mystrlen(str);
    printf("%d
", len);
}

int main(void)
{
    f71();

    system("pause");
    return 0;
}

在这里插入图片描述

现在需要你用指针实现


int mystrlen2(char str[]) {
    //传入字符数组
    //指针实现
    char* p = str;
    while (*p != '') p++;

    return p-str;//统计结果返回个数
}

void f71()
{
    char str[] = "hello";
    int len = mystrlen(str);
    int len2 = mystrlen2(str);
    printf("%d
", len);
    printf("%d
", len2);
}

int main(void)
{
    f71();

    system("pause");
    return 0;
}

字符串本质是字符数组
char类型的数组

我们把str的首地址赋给指针p
然后判断*p是否为
是则说明p已经来到了野指针的地方,p-str地址就是间隔char的个数,这就是我们要的字符串的长度
在这里插入图片描述

否则说明p还没有到字符串结尾,p++,挪动char的1个字节长度
在这里插入图片描述
如果这个代码写出来了
说明我们对指针的掌握已经很透了

美滋滋

函数的形参,在调用是,都是copy一份东西给它
函数内部就不用直接用实参

*p是解引用
左值是赋值,右值是获取内容
反正理解透了就好办了
p++在数组中是挪动类型个字节

指针的比较运算


void f72()
{

    int a[] = { 1,2,3,4,5,6,7,8,9,0 };
    int* p = a;
    if (p > a)
    {
        printf("p>a
%p
", p);//可以操作
        printf("%p
", a);//可以操作
    }
    else if(p<a)
    {
        printf("p<a
%p
", p);//可以操作
        printf("%p
", a);//可以操作
    }
    else
    {
        printf("p=a
%p
", p);//可以操作
        printf("%p
", a);//可以操作
    }
}

p指向一个地址
a是一个地址
反正地址大小是16进制的比较

在这里插入图片描述


    int a1 = 10;
    int b = 20;
    //俩变量之间的地址没用没意义

int*指针类型
p指针变量
在这里插入图片描述
NULL等价于0地址
这个地址其实是操作系统不允许操作的地址

指针数组

之前我们搞的都是整型int数组
存整数

如果是指针数组呢?
存的是地址呗


void f73()
{
    int a = 10;
    int b = 20;
    int c = 30;

    int* p1 = &a;
    int* p2 = &b;
    int* p3 = &c;

    int* arr[] = { p1,p2,p3 };//整型地址
    //这就是指针数组,说白了就是房间号的地址
    for (size_t i = 0; i < 3; i++)
    {
        printf("%p ", *arr[i]);//解引用
    }
}

int main(void)
{
    f73();

    system("pause");
    return 0;
}

在这里插入图片描述
注意中,这个解引用是abc的16进制数哦,就是abc
地址p123我们下面打印

申明p和a们
在这里插入图片描述
然后定义指针数组arr

低地址到高地址
分别存p1,2,3
实际存的就是地址咯
在这里插入图片描述


void f73()
{
    int a = 10;
    int b = 20;
    int c = 30;

    int* p1 = &a;
    int* p2 = &b;
    int* p3 = &c;

    int* arr[] = { p1,p2,p3 };//整型地址
    //这就是指针数组,说白了就是房间号的地址
    for (size_t i = 0; i < 3; i++)
    {
        printf("%d ", *arr[i]);//解引用
        printf("%p ", arr[i]);//arr指针地址本身
    }
}

int main(void)
{
    f73();

    system("pause");
    return 0;
}

arr[i]是p123这仨地址
而*arr[i]是对这仨地址的解引用,取出这三地址内部的abc变量
懂?
在这里插入图片描述
这里引出了二级指针
为啥呢,你数组里面存了指针,指针又是别的地址
所以是一个间接过程,二级地址
即二级指针

之前说了
arr[0]==*(arr+0)
arr名就是一个指针地址,偏移0之后解引用
这是数组取东西的本质


void f73()
{
    int a = 10;
    int b = 20;
    int c = 30;

    int* p1 = &a;
    int* p2 = &b;
    int* p3 = &c;

    int* arr[] = { p1,p2,p3 };//整型地址
    //这就是指针数组,说白了就是房间号的地址
    for (size_t i = 0; i < 3; i++)
    {
        printf("%d ", *arr[i]);//解引用
        printf("%p ", arr[i]);//arr指针地址本身
    }

    //看arr[0]
    printf("
%d ", *arr[0]);//解引用
    printf("%d ", *(*(arr+0)));//解引用
    printf("%d ", *(*(arr)));//解引用
    printf("%d ", *(*arr));//解引用
    printf("%d ", **arr);//解引用
}

一通变化之后
你发现是**arr
啥,2级指针
*arr是解引用内部地址
**arr是解引用这个地址代表的变量
2级指针

懂?

在这里插入图片描述
再看二维数组


void f74()
{
    int a[] = { 10,10 };
    int b[] = { 20 };
    int c[] = { 30 };

    int* p1 = a;
    int* p2 = b;
    int* p3 = c;

    int* arr[] = { p1,p2,p3 };//整型地址
    //这就是指针数组,说白了就是房间号的地址
    for (size_t i = 0; i < 3; i++)
    {
        printf("%d ", *arr[i]);//解引用
        printf("%p ", arr[i]);//arr指针地址本身
    }

    //看arr[0]
    printf("
%d ", *arr[0]);//解引用
    printf("%d ", *(*(arr + 0)));//解引用
    printf("%d ", *(*(arr)));//解引用
    printf("%d ", *(*arr));//解引用
    printf("%d ", **arr);//解引用
}

int main(void)
{
    f74();

    system("pause");
    return 0;
}

在这里插入图片描述
在这里插入图片描述
说明二维数组也是二级指针
懂?

多级指针

一级指针是变量的地址

int a=10;
int*p=&a;//一级指针
int **p1= &p;//一级指针的地址,是二级指针
int ***p2=&p1;//二级指针的地址,是三级指针

二级指针是一级指针的地址
三级指针是二级指针的地址
在这里插入图片描述
int** p[]就是一个三级指针
存的是二级指针的地址,懂吧

在这里插入图片描述
三级指针解引用就是变量a本身
不管如何就是地址寻下一个地址
继续寻找下一个地址
最后找到真的东西即可

美滋滋

在这里插入图片描述


void f75()
{
    int a = 10;
    int* p = &a;
    int** pp = &p;
    int*** ppp = &pp;


    printf("%p
", ppp);
    printf("%p
", &pp);
    printf("%p
", pp);
    printf("%p
", &p);
    printf("%p
", p);
    printf("%p
", &a);
    printf("%d
", ***ppp);
    printf("%d
", **pp);
    printf("%d
", *p);
    printf("%d
", a);
}

int main(void)
{
    f75();

    system("pause");
    return 0;
}

在这里插入图片描述
懂了吧,反正几级指针
你就几级引用即可

多级指针,不能跳跃定义
因为要一连串找地址,就像链表一样,懂了吧


总结

提示:重要经验:

1)
2)学好c++,即使经济寒冬,手握10个大厂offer绝对不是问题!
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。