C语言-指针_02

指针-02

1. 指针的指针

概念:指针变量中存储的是指针的地址,又名 二维指针

语法:

数据类型 **p;

示例:

c 复制代码
#include <stdio.h>
int main(int argc, char const *argv[])
{
    int num = 10;
    int *p1 = &num;
    int **p2 = &p1;

    printf("*p1是: %d\n", *p1);         //*p1是: 10
    printf("p1的值是: %p\n", p1);       //p1的值是: 0x7ffd1a6bff0
    printf("p1的地址是: %p\n", &p1);    //p1的地址是: 0x7ffd1a6bff08
    printf("p2的值是: %p\n", p2);       //p2的值是: 0x7ffd1a6bff08
    printf("p2的地址是: %p\n", &p2);    //p2的地址是: 0x7ffd1a6bff10
    return 0;
}

2. 指针与const

2.1 指针常量 - (int * const p)

概念:

本质是一个常量

  • 该指针常量 不能修改指向的地址(就是不能指向其他变量)
  • 但是可以修改指向地址中的值

注意:数组就是一个指针常量。

语法:

数据类型 * const 指针名称

例:

c 复制代码
void fun01()
{
    int num = 10;
    // int *p = &num;
    // int num02 = 20;
    // p = &num02;
    // printf("p = %p\n", p);

    //指针常量
    //p被const修饰,
    int * const p = &num;
    int num03 = 30;
    // p = &num03;  //指针指向的地址不可修改
    *p = 40;        //但是指针指向的地址的值可以修改
    printf("*p = %d\n", *p); //*p = 40
}

2.2 常量指针 - (const int *p)

概念:

指向常量(值不可修改)指针,本质是一个指针

  • 不能修改其指向地址的值 ,也就是 不能通过指针改变其指向的地址中的数据
  • 但是可以修改其指向的地址,也就是 可以改变其存储的地址

例:

c 复制代码
void fun02()
{
    int num = 10;
    //常量指针
    //指针被const修饰,
    // int const *p = &num;
    const int *p = &num;
    printf("p修改前的地址%p\n", p);  //p修改前的地址0x7fff0b95ff58
    int num04 = 30;
    p = &num04;  //指针指向的地址可以修改
    // *p = 40;  //但是指针指向的地址的值不可以修改
    printf("p修改后的地址%p\n", p);  //p修改后的地址0x7fff0b95ff5c
}

2.3 常量指针常量 - (const int * const p)

概念:

指向的地址 和 只想地址中的 都不可以修改

语法:

数据类型 const * const 指针名称
或 
const 数据类型 * const 指针名称

例:

c 复制代码
void fun03()
{
    int num = 10;
    //常量指针常量
    //指针和指针变量p都被const修饰,
    const int * const p = &num;
    int num02 = 40;
    // p = &num02; //指针指向的地址不可以被修改
    // *p = 50;    //指针指向的地址的值也不可以修改
}

3. 指针与数组元素

概述:

数组:是多个相同类型的变量的集合,每个变量占内存空间,都有 地址编号指针变量 当然 可以存放数组元素的地址
注意:

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

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

  3. 数组就是数组中存储的 第一个元素的地址

  4. c语言中数组的本质是一个 指针常量

    • 不可以修改指针的地址,
    • 但是可以修改该指针指向地址的值

示例1:

c 复制代码
void fun01()
{
    int nums[] = {1,3,5,7,9,2,4,6,8,10};
    int *p = &nums[3];

    printf("*p = %d\n", *p);       //*p = 7
    printf("*(p+1) = %d\n", *(p+1));       //*(p+1) = 9
    printf("*(p+2) = %d\n", *(p+2));       //*(p+2) = 2
    printf("*(p-1) = %d\n", *(p-1));       //*(p-1) = 5
    printf("*(p-2) = %d\n", *(p-2));       //*(p-2) = 3

    for (int i = 0; i < 10; i++)
    {
        printf("%p\n", &nums[i]);
    }
}

/*
0x7ffc007c1610
0x7ffc007c1614
0x7ffc007c1618
0x7ffc007c161c
0x7ffc007c1620
0x7ffc007c1624
0x7ffc007c1628
0x7ffc007c162c
0x7ffc007c1630
0x7ffc007c1634
*/

示例2:

c 复制代码
void fun02()
{
    int nums[] = {1,3,5,7,9,2,4,6,8,10};
    int *p = &nums[0];
    for (int i = 0; i < 10; i++)
    {
        printf("nums[%d] = %d\n", i, *(p++));
    }
}

/*
nums[0] = 1
nums[1] = 3
nums[2] = 5
nums[3] = 7
nums[4] = 9
nums[5] = 2
nums[6] = 4
nums[7] = 6
nums[8] = 8
nums[9] = 10
*/

示例3:

c 复制代码
void fun03()
{
    int nums[] = {1,3,5,7,9,2,4,6,8,10};
    int *p1 = &nums[0];
    int *p2 = &nums[1];
    if (p1 < p2)
    {
        printf("p1在p2的前面\n");
    }
    else{
        printf("p1在p2的后面\n");
    }
}
//p1在p2的前面

示例4:

c 复制代码
void fun04()
{
    int nums[] = {1,3,5,7,9,2,4,6,8,10};
    //数组名就是数组中存储的第一个元素的地址
    printf("nums = %p\n",nums);         //nums = 0x7ffe6b558c30
    printf("nums[0]= %p\n",&nums[0]);   //nums[0]= 0x7ffe6b558c30
}

示例5:

c 复制代码
void fun05()
{
    int nums[] = {1,3,5,7,9,2,4,6,8,10};
    int *p = nums;

    for (int i = 0; i < 10; i++)
    {
        // printf("nums[%d] = %d\n", i, *(p++));
        // printf("nums[%d] = %d\n", i, *(p+i));
        // printf("nums[%d] = %d\n", i, nums[i]);
        // printf("nums[%d] = %d\n", i, p[i]);
        printf("nums[%d] = %d\n", i, *(nums+i));

        //c语言中数组的本质是一个 指针常量
        //不可以修改指针的地址,但是可以修改该指针指向地址的值
        // printf("nums[%d] = %d\n", i, *(nums++));  //报错
    }
    
}

4. 指针与数组

4.1 数组名

数组名 其实是数组中 首元素的地址 。

例:

c 复制代码
void fun04()
{
    int nums[] = {1,3,5,7,9,2,4,6,8,10};
    //数组名就是数组中存储的第一个元素的地址
    printf("nums = %p\n",nums);         //nums = 0x7ffe6b558c30
    printf("nums[0]= %p\n",&nums[0]);   //nums[0]= 0x7ffe6b558c30
}

4.2 数组指针

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

语法:

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

示例:

c 复制代码
void fun06()
{
    //数组指针
    //概念:指向数组的指针,本质是一个指针
    int nums[] = {1,3,5,7,9,2,4,6,8,10};
    int *p1 = nums;

    int nums02[2][4] = {1,2,3,4,5,6,7,8};
    int (*p2)[4] = nums02;

    int nums03[2][3][4] = {0};
    int (*p3)[3][4] = nums03;
}

4.3 指针数组

指针变量是一个变量,数组是可以存储多个类型形容的变量的勇容器,故 数组可以存储多个指针变量,此时该数组 被称为 指针数组,其本质是一个 数组

语法:

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

例:

c 复制代码
void fun07()
{
    int a = 10,b = 20,c = 30;
    int *p1 = &a;
    int *p2 = &b;
    int *p3 = &c;
    //指针数组
    //存储指针的数组,本质是一个数组
    int *nums[3] = {p1, p2, p3};
}

4.4 字符数组与字符串指针的区别

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

字符串指针:

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

  • 如果指向 栈、静态全局区中存放的字符数组,那么则是一个 普通的指针变量,值和地址都可以修改。

    示例1:

c 复制代码
void fun08()
{
    //str01的内存地址在栈区
    //str01是一个数组,所以是一个指针常量
    //指针常量可以修改其地址中的值
    //指针常量不可以修改其指向的地址
    char str01[] = "hello";
    str01[0] = 'H';
    printf("str01=%s\n",str01);     //str01=Hello
    //str01 = "world";			//报错,不能指向另外的地址
    
    //str02的内存地址在文字常量区
    //常量指针
    //指针指向的地址可以修改
    //指针指向的地址中的元素不可修改
    char * str02 = "hello";
    printf("str02修改前的地址%p\n",str02);      //str02修改前的地址0x400d28
    str02 = "world";
    printf("str02修改后的地址%p\n",str02);      //str02修改后的地址0x400d4
    //str02[0] = 'W';     //段错误 (核心已转储)
}

示例2:

c 复制代码
void fun09()
{
    //字符数组
    char str01[] = "hello";
    //指向 栈、静态全局 区中存放的字符数组
    char *str03 = str01;
    printf("str03修改内容前%s\n",str03);  //tr03修改内容前hello
    str03[0] = 'H';
    printf("str03修改内容后%s\n",str03);  //str03修改内容后Hello

    str03 = "world";  
    printf("str03修改地址后%s\n",str03);  //str03修改地址后world
}

示例3:

c 复制代码
void fun10()
{
    char names[][50] = {"boboy","tom","jerry","zhangsan","lisi"};
    //指针数组:存储的是这些值的首地址
    char *ns[50] = {"boboy","tom","jerry","zhangsan","lisi"};
    //数组指针
    // char (*nss)[50] = names;
    char (*nss)[50] = {"boboy","tom","jerry","zhangsan","lisi"};
}

5. 函数与指针

5.1 函数名就是函数地址

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

语法:

  • 函数指针的定义初始化

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

    注意:

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

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

    注意:

    • 实参列表可以忽略不写

示例1:函数名本身就是函数在代码区存储 该函数的地址

c 复制代码
#include <stdio.h>
void test01()
{
    printf("test01被调用了!");
}
void fun01()
{
    printf("%p\n", test01);  //0x400526
}

int main(int argc, char const *argv[])
{
    fun01();
    return 0;
}

示例2:调用其指向的函数

c 复制代码
#include <stdio.h>
void test01()
{
    printf("test01被调用了!\n");
}

void fun02()
{
    /*
        函数指针的书写方式
        返回值类型 (*指针变量名)(形参列表)
    */
    //p1 == test01
    //调用函数:test01(实参列表);
    void (*p1)() = test01; 
    p1();       //test01被调用了!
}

int main(int argc, char const *argv[])
{
    fun02();
    return 0;
}

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

c 复制代码
void add(int a, int b)
{
    printf("sum = %d\n", a+b);
}

void fun03()
{
    void (*p1)(int, int) = add;
    p1(10, 20);     //sum = 30
}

示例4:

c 复制代码
int sub(int a, int b)
{
    return a-b;
}

void fun04()
{
    int (*p1)(int, int) = sub;
    int num = p1(10,3);
    printf("num = %d\n", num);  //num = 7
}

5.2 指针变量作为函数的参数 (传递指针变量的地址 )

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

示例:

  • 指针变量作为形参在函数中定
  • 指针变量作为实参,在调用函数时传递
c 复制代码
//指针变量作为形参在函数中定义
void setNum(int *num, int i)
{
    num[i] = 10;
}

void fun05()
{
    int ns[5] = {0};
    // int *p = ns;
    //指针变量作为实参,在调用函数时传递
    //本次传递的是指针变量的地址
    setNum(ns, 0);
    printf("ns[0] = %d\n", ns[0]);      //ns[0] = 10
}

5.3 字符串指针作为实参

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

示例1:函数内部修改指针内容时,不会影响函数外部的值

c 复制代码
void setStr(char *str)
{
    str = "world";
}

void fun06()
{
    char *s = "hello";
    setStr(s);
    printf("s=%s\n", s);     //s=hello
}

示例2:

c 复制代码
void setchar(char *str, int i)
{
    str[0] = 'H';
}

void fun07()
{
    //字符数组是指针常量,可改值不可该地址
    char str[] = "hello";
    char *p = str;
    //setchar函数修改的是地址中的值,所以传入该指针会出现段错误
    // char *p = "hello";  //常量指针,可改地址不可改值
    setchar(p, 0);
    printf("str=%s\n", str);    //str=Hello
}

5.4 字符串指针的指针作为函数参数

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

示例:函数内部修改字符指针地址,就可以 修改 函数外部的结果

c 复制代码
void setName(char **name)
{
    *name = "李四";
}
void fun09()
{
    char *n = "张三";
    char **p = &n;
    setName(p);
    printf("n=%s\n",n);
}

打印出对应的地址:

c 复制代码
void setName(char **name)
{
    printf("name---->%p\n", name);      //name---->0x7ffd94285208
    *name = "李四";
    printf("*name改变后---->%p\n", *name);    //*name改变后---->0x400a53
}
void fun09()
{
    char *n = "张三"; 
    printf("n---->%p\n", n); //n---->0x400a71
    printf("*n---->%p\n", &n); //*n---->0x7ffd94285208
    char **p = &n;
    printf("p---->%p\n", p); //p---->0x7ffd94285208
    printf("*p---->%p\n", *p); //*p---->0x400a71
    setName(p); 
    printf("n修改后---->%p\n", n); //n修改后---->0x400a53
    printf("*p修改后---->%p\n", *p); //*p修改后---->0x400a53
    
    printf("n=%s\n",n);		//n=李四
}

图示:

5.5 指针作为返回值

函数中局部指针变量做为函数的返回值函数执行完毕后,其局部指针变量指向的地址也将被释放,外部无法使用,导致段错误

如需外部使用可以将 局部指针变量修改为 静态局部指针变量

示例:

c 复制代码
int* my01()
{
    /*
        静态局部变量生命周期:
        随着所在函数第一次调用而生成,随着所在进程的执行完毕而销毁
     */ 
    static int num[5] = {1,2,3,4,5};
    return num;
}

void fun11()
{
    int *ns = my01();
    printf("%d\n",ns[0]);   //1
}
相关推荐
sp_wxf几秒前
Lambda表达式
开发语言·python
l1x1n07 分钟前
网络安全概述:从认知到实践
网络
鄃鳕7 分钟前
HTTP【网络】
网络·网络协议·http
Fairy_sevenseven12 分钟前
【二十八】【QT开发应用】模拟WPS Tab
开发语言·qt·wps
蜡笔小新星20 分钟前
Python Kivy库学习路线
开发语言·网络·经验分享·python·学习
凯子坚持 c20 分钟前
C语言复习概要(三)
c语言·开发语言
无限大.32 分钟前
c语言200例 067
java·c语言·开发语言
余炜yw33 分钟前
【Java序列化器】Java 中常用序列化器的探索与实践
java·开发语言
无限大.34 分钟前
c语言实例
c语言·数据结构·算法
篝火悟者34 分钟前
问题-python-运行报错-SyntaxError: Non-UTF-8 code starting with ‘\xd5‘ in file 汉字编码问题
开发语言·python