指针(2)

不向前不知道路远,不学习不明白真理

指针的指针

别名二维指针

语法

数据类型  **指针变量名;

示例

void fun()
{
    int num =10;
    int *p = #
    int **p1 = &p;
    printf("p的地址:%p\n",p);
    printf("p1的地址:%p\n",p1);
    printf("p1的存储的地址是:%p\n",*p1);
}

 const与指针

指针常量

概念

本质是一个常量

不能改变其地址,但可以修改该指针指向的地址中的值

语法

数据类型  * const  指针名

void fun01()
{
    //const修饰的指针常量
    int a = 5;
    int b = 6;
    int * const p = &a;
    //可以改变其值
    *p = 10;
    printf("a = %d\n",a);
    printf("p的地址:%p\n",p);
    //不能改变其地址
    p = &b;
    printf("p的地址:%p\n",p);
}

常量指针

概念

本质是一个指针

指向常量的指针,不能改变其指针指向地址的值,但可以改变其存储地址地址

语法

数据类型  const * 指针名称

const  数据类型 * 指针名称

示例

void fun02()
{
    //const修饰的常量指针
    int a = 5;
    int b = 2;
    //可以修改其地址
    const int *p = &a;
    printf("p的地址:%p\n",p);
    p = &b;
    printf("p的地址:%p\n",p);
    //不能修改其指针指向地址的值
    printf("p = %d\n",*p);
    *p = b;
    printf("p = %d\n",*p);
}

 常量指针常量

概述

指向的地址与指向的地址中的数据都不能被修改

语法

数据类型 const  * const  指针名称

const  数据类型 *  const 指针名称

void fun03()
{
    int a = 10;
    int b = 20;
    const int * const p = &a;
    //常量指针常量不可以修改其指向的地址
    //p = &b;
    printf("%d\n",*p);
    //常量指针常量不能修改其指向的地址中的数据
    // *p = 30;
}

指针与数组元素

概述

数组是多个相同类型的变量集合。每个变量都占内存空间,都有地址编号

指针变量可以存放数组元素地址,可以使用关系运算符

本质是一个数组,存储的是指针

数组名是指针常量

数组名是首元素存储的地址        &num[0] = num

注意:

只有两个相同类型的指针指向同一个数组元素的时候,比较大小才有意义

指向前面元素的指针小于指向后面元素的指针

示例

void fun04()
{
    int num[] = {1,3,5,7,9,2,4,6,8,0};
    int *p = &num[3];
    printf("%d\n",*p);
    //因为数组在内存中是连续开辟的
    //所以我们可以通过对指针的加减使其指向数组中不同位置的元素
    printf("%d\n",*(p+2));
    printf("%d\n",*(p-2));
    //指针变量存储的地址编号,地址编号是一个十六进制的数
    printf("num[0]:%p\n",&num[0]);
    printf("num[1]:%p\n",&num[1]);
    //因为地址编号是一个十六进制的数所以我们可以对其使用关系运算符
    if(&num[0] < &num[1])
    {
        printf("num[0]在前\n");
    }
    else
    {
        printf("num[1]在前\n");
    }
    //两个相同类型的地址相差,使用十六进制数相差之后得到的数值 除以其数据类型的字节大小,为最终结果
    printf("%ld\n",&num[0] - &num[1]);
}

指针与数组

概述

1.数组名其实就是数组中首元素的地址

2.数组名可以赋值给一个指针变量,此时该指针指向一个数组,称之为数组指针。其本质是一个指针

        指向一维数组指针的语法

                数据类型  *指针名;

        指向二维数组指针的语法

                数组类型  (*指针名)[二维数组中一维数组的长度];

         指向三维数组指针的语法

                数组类型  (*指针名)[三维数组中二维数组的长度][二维数组中一维数组的长度];

3.数组名本质上是一个指针常量,故其指向的地址无法被修改

4.字符数组与字符串指针的区别

        字符数组:在内存(栈,静态全局区)中开辟了一段空间存放字符串,将其首元素地址赋值给数组名,是个指针常量

        字符串指针:

                如果指向在文字常量区中存放字符串,会将字符串的首地址赋给指针变量,此时该指针是个常量指针

                如果指向栈,静态全局区中存放的字符数组,那么则是一个普通的指针变量

5.指针变量是一个变量,数组可以存储多个类型相同的变量的容器,故数组可以存储多个指针变量,此时该数组称之为指针数组,其本质就是一个数组

        语法:

                数据类型  *  数组名[长度];

示例

void fun05()
{
    //指针与一维数组
    int nums[10] = {1,3,5,7,9,2,4,6,8,0};
    //数组名其实是数组中首元素的地址
    printf("%p\n",nums);
    printf("%p\n",&nums[0]);
    //数组名可以赋值给一个指针变量
    int * p1 = nums;
    char str[] = "hello";
    char * p2 = str;
    //此时我们可以理解为指针变量就等价与其指向的数组的数组名
    //顾我们可以通过指针变量名[下标]获取其指向的数组中指定位置的元素
    printf("%d\n",p1[1]);
    printf("%c\n",p2[1]);
    //指针与二维数组
    int numss[3][5] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
    int (*p3)[5] = numss;
    printf("%d\n",p3[1][2]);
    char strs[5][50] = {"hello","world","c++","python","c"};
    char (*p4)[50] = strs;
    printf("%c\n",p4[1][2]);
    printf("%s\n",p4[1]);
}

void fun06()
{
    int nums[10] = {1,3,5,7,9,2,4,6,8,0};
    //数组名就是数组第一个元素的的地址
    int *p = nums;
    for(int i = 0; i < 10; i++)
    {
        // printf("num[%d] = %d\n",i,*(p + i));
        // +i是向后移动的位数
        printf("num[%d] = %d\n",i,*(nums + i));
        // printf("num[%d] = %d\n",i,*(p++));

        //错误❌示范
        //注意数组的变量名是一个指针常量,地址不可以修改
        //++的含义是+=
        printf("num[%d] = %d\n",i,*(nums++));
    }
}

void fun07()
{
    char str01[] = "hello";
    char *str02 = "hello";
    //指针常量能修改其指向地址的元素,但是不可以修改其指向的地址
    str01[0] = 'H';
    printf("str01=%s\n",str01);
    //会产生段错误
    //常量指针不能修改其指向地址的元素,但是可以修改其指向的地址
    //str02[0] = 'H';
    //printf("str01=%s\n",str02);
    str02 = "world";
    printf("str02=%s\n",str02);
    //使指针变量指向栈中的字符数组
    char *str03 = str01;
    str03[0] = 'H';
    printf("str03=%s\n",str03);
    str03 = "world";
    printf("str03=%s\n",str03);
}

void fun08()
{
    int a = 1,b = 2,c = 3;
    int * p[] = {&a,&b,&c};
    printf("%d\n",*(p[0]));
    printf("%d\n",*(p[1]));
    printf("%d\n",*(p[2]));
}

 函数与指针

1.函数名本身就是函数在代码区存储该函数的地址,故可以赋值给一个指针变量,但因为其在代码区的地址不可修改,故该指针是一个指针常量

         语法:

                函数指针的定义与初始化

                        返回值类型  (*指针名称)(指向的函数的形参列表的数据类型)  =  函数名;

                        注意:

                                指向的函数的形参列表的数据类型可以忽略不写

                        调用其指向的函数

                                指针名(实参列表);

                                变量名 = 指针名(实参列表);

                                注意:

                                        实参列表可以忽略不写

2.指针变量就是一个变量,可以作为函数的形参,此时传递的是指针变量的地址

3.字符串作为实参,将指针指向常量区的内容传递函数。函数内部修改指针内容时,不会影响函数外部的值

4.将字符指针的指针作为函数的实参,函数的形参使用  **q(指针的指针),函数内部可以修改字符指针地址,即可以改函数的外部结果

5.数组作为实参传递可以用指针变量接收,传递的是地址‘

6.字符指针数组作为实参时,函数的形参的写法:  char *q[] 

7.函数中局部指针变量作为函数的返回值,函数执行完毕后,其局部指针变量指向的地址也将被释放,外部无法使用导致段错误。如需外部使用可以将局部指针变量修改为静态局部指针变量

示例

void test01()
{
    printf("test01函数被调用\n");
}
void test02(int a,int b)
{
    printf("test02函数被调用\n");
}
void test03(int a,int b)
{
    printf("test03函数被调用\n");
    printf("a=%d\n",a);
    printf("b=%d\n",b);
}
int test04(int a,int b)
{
    printf("test04函数被调用\n");
    printf("a=%d\n",a);
    printf("b=%d\n",b);
    return a+b;
}

void fun09()
{
    void (*p1)() = test01;
    p1();
    void (*p2)() = test02;
    p2();
    void (*p3)(int,int) = test03;
    p3(10,12);
    int (*p4)(int,int) = test04;
    int num = p4(10,12);
    printf("num=%d\n",num);
}

void changNum(int *p)
{
    *p = 20;
}
void fun10()
{
    int num = 10;
    changNum(&num);
    printf("num=%d\n",num);
}

void changName(char *p)
{
    p = "江东鼠辈";
}
void fun11()
{
    //常量指针
    char *name = "吕蒙";
    changName(name);
    printf("name=%s\n",name);
}

void changName02(char **p)
{
    *p = "江东鼠辈";
}
void fun12()
{
    char *name = "吕蒙";
    changName02(&name);
    printf("name=%s\n",name);
}

void set(int*nums)
{
    nums[0] = 10;
}
void fun13()
{
    int nums[5] = {0};
    printf("set 前 nums[0]=%d\n",nums[0]);
    set(nums);
    printf("set 后 nums[0]=%d\n",nums[0]);
}

void getName01(char * ns[],int i)
{
    //%s打印地址时可直接打印其值
    printf("%s\n",ns[i]);
}
void getName02(char ** ns,int i)
{
    printf("%s\n",*(ns+i));
}
void setName(char ** ns,int i,char * newName)
{
    *(ns+i) = newName;
}
void fun14()
{
    //数组指针
    char * names[3] = {"c","c++","sql"};
    getName01(names,1);
    getName02(names,2);
    char * name = "cplusplus";
    setName(names,1,name);
    getName01(names,1);
}
int *getNums01()
{
    //局部变量
    int nums[3] = {1,2,3};
    return nums;
}
int *getNums02()
{
    //静态局部变量
    static int nums[3] = {1,2,3};
    return nums;
}
void fun15()
{
    //相当于野指针没有地址
    // int *p01 = getNums01();
    // printf("%d\n",*(p01+1));
    int *p02 = getNums02();
    printf("%d\n",*(p02+1));
}

 总结

野指针

        局部变量定义的指针没有赋初值

        如:

                int *p;

空指针

        指针的值为NULL

        如:

                int *p = NULL;

空类型指针(万能指针)

        指针类型为void *的指针

        如:

                int num = 0;

                void *p = &num;

指针的指针

        存储指针地址的指针

         如:

                int num = 0;

                int *p = &num;

                int **p1 = &p;

指针常量

        本质是一个常量,该指针变量不能修改指向的地址,但是可以修改地址中的值

如:

        int num = 0;

        int * const p =&num;

常量指针

        本质上是一个指针,指向常量,所以可以修改其指向的地址,但是不能修改其指向地址的值

如:

        const  char *p = "name";

常量指针常量

        指针常量与常量指针的结合体,既不能修改地址,也不能修改其值

如:

        const char * const p = "name";

数组指针

        数组即指针,指针即数组。

        指向数组的指针,本质上是一个指针

如:

        一维数组指针

        int *nums;

        二位指针数组

        int (*nums) [5];

指针数组

        本质是一个数组,存储的元素的数据类型叫指针

如:

        int a = 1,b = 2,c = 3;

        int *nums = {&a,&b,&c};

        char *name[3] = {"hello","world","c"};

函数指针

        本质是一个指针,存储函数在代码区的位置

 如:

        void test()

        {

        }

        void (*p)();

返回值为指针的函数声明

        声明一个返回值为指针的函数

        extern int *test();

        int *test()

        {

                static int num = 10;

                return &num;

        }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值