• char *, char **,char a[] ,char *a[]啥啥分不清楚?


    深入讲解前

    1.数组的本质和字符数组

    数组的本质:数组是多个元素的集合,在内存中分布在地址相连的单元中,所以可以通过其下标访问不同单元的元素。
    字符数组:字面意思是数组,数组中的元素是字符。(这就是它的本质意义。。)
    char str[10];
    定义了一个有十个元素的数组,元素类型为字符。c语言中定义一个变量可以初始化:
    char str[10] = {“hello world”} ;
    当编译器遇到这句话时,会把str数组中从第一个元素把hello world\0 逐个填入。由于c语言中没有真正的字符串类型,可以通过字符数组表示字符串,因为它的元素地址是连续的。
    c语言中规定数组代表数组所在内存位置的首地址,也就是str[0]的地址,即str = &str[0];
    举例:

    char *s;
    s = "China";
    
    • 1
    • 2

    为什么可以把一个字符串赋值给一个指针变量?这不是类型不一致嘛???
    这就是上面提到的关键
    c语言中编译器会给字符串常量分配地址如果"China",存储在内存中的0x3000 0x3001 0x3002 0x3003 0x3004 0x3005
    s=“China”,意思是什么,是地址。
    其实真正的意义是 s = “China” = 0x3000。你把China看做是字符串,但是编译器把它看做是地址0x3000,即字符串常量的本质表现是代表它的第一个字符的地址。
    那么%s,它的原理其实也是通过字符串首地址输出字符串,printf(“%s”,s); 传给它的其实是s所保存的字符串的地址
    比如:

    %p是打印地址(指针地址)的,是十六进制的形式,但是会全部打完,即有多少位打印多少位。
    32位编译器的指针变量为4个字节(32位),64位编译器的指针变量为8个字节(64位)。

         #include 
          int main()
          {
             char *s;
             s = "hello";
             printf("%p\n",s);
             return 0;
          }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述
    可以看到 s = 0x000000000404000 , 这也是"China"的首地址,所以printf(“%s\n”,0x000000000404000); 也等效。

    #include 
    int main()
    {
        char *s;
        s = "hello world";
        printf("%p\n",s);
        printf("%s\n",0x000000000404000);//printf("%s\n",s)
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    c语言中操作字符串是通过它在内存中的存储单元的首地址进行的,这是字符串的终极本质。

    #include 
    #include 
    int main()
    {
       char *a , *b;
       a = "hello" ;
       b = "world" ;
       printf("%p\n" , a);
       printf("%p\n" , b);
    }
    ![请添加图片描述](https://img-blog.csdnimg.cn/3752963a3e29434bb635b84cba3e7461.png)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    请添加图片描述

    2.指针的概念和指针的指向

    指针:指针也是一种变量,只不过它的内存单元中保存的是一个标识其他位置的地址。由于地址也是整数,在32位平台下,指针默认为32位
    指针指向:指向的直接意思就是指针变量所保存的其他的地址单元中所存放的数据类型
    int *p ; p变量保存的地址所在内存单元中的数据类型为整型
    不论指向的数据类型为哪种,指针变量其本身永远为整型,因为它保存的地址。

    char * 与 char a[]

    char *s;
    char a[];
    前面说到a代表字符串的首地址,而s这个指针也保存的字符串的首地址,即第一个字符的地址,这个地址单元中的数据是一个字符,这也与s所指向的char一致。
    因此可以s = a ,但是不能 a = s。(c语言中数组名可以复制给指针表示地址,但是却不能赋指针数组名,它是一个常量类型,所以不能修改)

    #include 
    #include //strlen的头文件
    int main()
    {
        char  a [ ] = "hello";
        char *s = a;
        for(int i= 0; i < strlen(a) ; i++)
              //printf("%c", s[i]);
                printf("%c",*s++);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    字符指针可以用 间接操作符*取其内容,也可以用数组的下标形式【】, 数组名也可以用 * 操作,因为它本身表示一个地址。比如 printf(“%c”,*a) 将会打印出’h’
    char * 与 char a[]的本质区别:
    当定义 char a[10]时,编译器会给数组分配十个单元,每个单元的数据类型为字符。
    而定义 char *s 时, 这是一个指针变量,占8个字节,64位,用来保存一个地址。
    sizeof(a)= 10;
    sizeof(s) = ?;(当然是8,编译器分配8个字节64位的空间,这个空间中将要保存地址)
    printf(“%p”,s) 这个表示s单元中所保存的地址
    printf(“%p”,&s)这个表示变量本身所在内存单元地址

    #include 
    #include 
    int main()
    {
        char  a [ ] = "hello";
        char *s ;
        s = a ;
        printf("%d\n",sizeof(s));
        for(int i= 0; i < strlen(s) ; i++)
              printf("%c", s[i]);
        printf("\n");
        printf("%p\n", s);
        //printf("%c",*s++);
        printf("%p", &s);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述

    char ** 与 char *a[]

    先看char * a【】;
    由于【】的优先级高于*所以a先和【】结合,它还是一个数组,数组中的元素才是char *, 前面说到char *是一个变量,保存的地址。
    char *a[ ] = {“China”,“French”,“America”,“German”};

    #include 
    int main()
    {
     char *a [ ] = {"China","French","America","German"};
    printf("%p %p %p %p\n",a[0],a[1],a[2],a[3]);
    return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述
    可以看到数组中的四个元素保存了四个内存地址,这四个地址中就代表了四个字符串的首地址,而不是字符串本身

    #include 
    int main()
    {
    char *a [ ] = {"China","French","America","German"};
    printf("%p %p %p %p\n",a[0],a[1],a[2],a[3]); //数组元素中保存的地址
    printf("%p %p %p %p\n",&a[0],&a[1],&a[2],&a[3]);//数组元素单元本身的地址
    return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述
    可以看到四个元素单元所在地址相差八个字节,这是由于每个元素是一个指针变量占八个字节
    char s;
    chars为二级指针,s保存一级指针char * 的地址。
    同样注意,不能a=s,前面已经说到,a是一个常量

    #include 
    int main()
    {
    char *a [ ] = {"China","French","America","German"};
    char **s =   a;
    printf("%s\n",*s);//s是指针数组的首地址,*s就是指针数组下标为0的地址
    printf("%s\n",a[0]);//a【0】就是China的地址,字符串本质
    printf("%s\n",*a);//a保存的就是指针数组的首地址,和*s一样,都是指针数组下标为0的地址
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述
    请添加图片描述
    最后,希望这篇文章能够帮助大家的话可以点点赞支持一下,如果有什么疑问的话,评论区或者私信都可以的

  • 相关阅读:
    Linux应用开发 - 读写锁
    PyTorch 被大量网友反馈,TorchRec 这一新库“诞生”且规模宏大
    面试突击25:sleep和wait有什么区别
    Android ViewPager2 + Fragment + BottomNavigationView 联动
    游戏类app有哪些变现方式?
    路飞项目多方式登录、手机号短信验证注册接口
    SSM框架17问--面试必备
    3.机器学习-十大算法之一线性回归算法(LinearRegression)原理讲解
    SpringBoot Mybatis 多数据源 MySQL+Oracle+Redis
    【Vue项目复习笔记】Home离开时记录状态和位置
  • 原文地址:https://blog.csdn.net/weixin_51658930/article/details/126018140