深入 理解char * ,char ** ,char a[ ] ,char *a[] 的区别

C语言中由于指针的灵活性,导致指针能代替数组使用,或者混合使用,这些导致了许多指针和数组的迷惑,因此,刻意再次深入探究了指针和数组这玩意儿,其他类型的数组比较简单,容易混淆的是字符数组和字符指针这两个。。。下面就开始剖析一下这两位的恩怨情仇。。。

 1 数组的本质

   数组是多个元素的集合,在内存中分布在地址相连的单元中,所以可以通过其下标访问不同单元的元素。。

 2 指针。

   指针也是一种变量,只不过它的内存单元中保存的是一个标识其他位置的地址。。由于地址也是整数,在32位平台下,指针默认为32位。。

 3 指针的指向?

   指向的直接意思就是指针变量所保存的其他的地址单元中所存放的数据类型。

   int  * p ;//p 变量保存的地址所在内存单元中的数据类型为整型

          float *q;// ........................................浮点型

          不论指向的数据类型为那种,指针变量其本身永远为整型,因为它保存的地址。

    4  字符数组。。。

        字面意思是数组,数组中的元素是字符。。确实,这就是它的本质意义。

        char  str[10];

        定义了一个有十个元素的数组,元素类型为字符。

        C语言中定义一个变量时可以初始化。

        char  str[10] = {"hello world"};

        当编译器遇到这句时,会把str数组中从第一个元素把hello world\0 逐个填入。。

        由于C语言中没有真正的字符串类型,可以通过字符数组表示字符串,因为它的元素地址是连续的,这就足够了。

        C语言中规定数组代表数组所在内存位置的首地址,也是 str[0]的地址,即str = &str[0];

        而printf("%s",str); 为什么用首地址就可以输出字符串。。

          因为还有一个关键,在C语言中字符串常量的本质表示其实是一个地址,这是许多初学者比较难理解的问题。。。

          举例:

          char  *s ;

          s = "China";

          为什么可以把一个字符串赋给一个指针变量。。

          这不是类型不一致吗???

          这就是上面提到的关键 。。

          C语言中编译器会给字符串常量分配地址,如果 "China", 存储在内存中的 0x3000 0x3001 0x3002 0x3003 0x3004 0x3005 .

          s = "China" ,意识是什么,对了,地址。

          其实真正的意义是 s ="China" = 0x3000;

          看清楚了吧 ,你把China 看作是字符串,但是编译器把它看作是地址 0x3000,即字符串常量的本质表现是代表它的第一个字符的地址。。。。。。。。。。

          s = 0x3000

          这样写似乎更符合直观的意思。。。

          搞清楚这个问题。。

          那么 %s ,它的原理其实也是通过字符串首地址输出字符串,printf("%s ", s);  传给它的其实是s所保存的字符串的地址。。。

          比如

        #include <stdio.h>

      int main()

      {

        char *s;

        s = "hello";

        printf("%p\n",s);

        return 0;

      }


其实做为一个开发者,有一个学习的氛围跟一个交流圈子特别重要这里我推荐一个C语言C++交流群583650410,不管你是小白还是转行人士欢迎入驻,大家一起交流成长。免费的公开课供你学习!

可以看到 s = 0x00422020 ,这也是"China"的首地址

      所以,printf("%s",0x00422020);也是等效的。。


      字符数组:

      char  str[10] = "hello";

      前面已经说了,str = &str[0] , 也等于 "hello"的首地址。。

      所以printf("%s",str); 本质也是 printf("%s", 地址");

        C语言中操作字符串是通过它在内存中的存储单元的首地址进行的,这是字符串的终极本质。。。

    5  char *  与 char  a[ ];

      char  *s;

      char  a[ ] ;

      前面说到 a代表字符串的首地址,而s 这个指针也保存字符串的地址(其实首地址),即第一个字符的地址,这个地址单元中的数据是一个字符,

  这也与 s 所指向的 char 一致。

      因此可以 s = a;

      但是不能 a = s;

      C语言中数组名可以复制给指针表示地址, 但是却不能赋给给数组名,它是一个常量类型,所以不能修改。。

      当然也可以这样:

        char  a [ ] = "hello";

        char *s =a;

        for(int i= 0; i < strlen(a) ; i++)

            printf("%c", s[i]);

        或  printf("%c",*s++);

        字符指针可以用 间接操作符 *取其内容,也可以用数组的下标形式 [ ],数组名也可以用 *操作,因为它本身表示一个地址 。。

      比如 printf("%c",*a);  将会打印出 'h'

      char * 与 char a[ ] 的本质区别:

      当定义 char a[10 ]  时,编译器会给数组分配十个单元,每个单元的数据类型为字符。。

      而定义 char *s 时,  这是个指针变量,只占四个字节,32位,用来保存一个地址。。

      sizeof(a) = 10 ;

      sizeof(s)  = ?

      当然是4了,编译器分配4个字节32位的空间,这个空间中将要保存地址。。。

        printf("%p",s);

        这个表示 s 的单元中所保存的地址。。

        printf("%p",&s);

        这个表示变量本身所在内存单元地址。。。。,不要搞混了。。

        用一句话来概括,就是 char *s 只是一个保存字符串首地址的指针变量, char a[ ] 是许多连续的内存单元,单元中的元素为char ,之所以用 char *能达到

char a  [ ] 的效果,还是字符串的本质,地址,即给你一个字符串地址,便可以随心所欲的操所他。。但是,char* 和 char a[ ] 的本质属性是不一样的。。


    6      char **  与char  * a[ ] ;

            先看 char  *a [ ] ;

            由于[ ] 的优先级高于* 所以a先和 [ ]结合,他还是一个数组,数组中的元素才是char * ,前面讲到char * 是一个变量,保存的地址。。

            所以 char *a[ ] = {"China","French","America","German"};

            同过这句可以看到, 数组中的元素是字符串,那么sizeof(a) 是多少呢,有人会想到是五个单词的占内存中的全部字节数 6+7+8+7 = 28;

            但是其实sizeof(a) = 16;

            为什么,前面已经说到, 字符串常量的本质是地址,a 数组中的元素为char * 指针,指针变量占四个字节,那么四个元素就是16个字节了

            看一下实例:

            #include <stdio.h>

   int main()

   {

   char *a [ ] = {"China","French","America","German"};

   printf("%p %p %p %p\n",a[0],a[1],a[2],a[3]);

   return 0;

   }


其实做为一个开发者,有一个学习的氛围跟一个交流圈子特别重要这里我推荐一个C语言C++交流群583650410,不管你是小白还是转行人士欢迎入驻,大家一起交流成长。免费的公开课供你学习!

可以看到数组中的四个元素保存了四个内存地址,这四个地址中就代表了四个字符串的首地址,而不是字符串本身。。。

      因此sizeof(a)当然是16了。。

      注意这四个地址是不连续的,它是编译器为"China","French","America","German" 分配的内存空间的地址, 所以,四个地址没有关联。

        #include <stdio.h>

   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;

   }


可以看到 0012FF38 0012FF3C 0012FF40 0012FF44,这四个是元素单元所在的地址,每个地址相差四个字节,这是由于每个元素是一个指针变量占四个字节。。。

      char **s;

      char **为二级指针, s保存一级指针 char *的地址,关于二级指针就在这里不详细讨论了 ,简单的说一下二级指针的易错点。 

      举例:

      char *a [ ] = {"China","French","America","German"};

      char **s =  a;

      为什么能把 a赋给s,因为数组名a代表数组元素内存单元的首地址,即 a = &a[0] = 0012FF38;

      而 0x12FF38即 a[0]中保存的又是 00422FB8 ,这个地址, 00422FB8为字符串"China"的首地址。

      即 *s = 00422FB8 = "China";

        这样便可以通过s 操作 a 中的数据

      printf("%s",*s);

      printf("%s",a[0]);

      printf("%s",*a);

      都是一样的。。。

      但还是要注意,不能a = s,前面已经说到,a 是一个常量。。

      再看一个易错的点:

      char **s = "hello world";

      这样是错误的,

      因为  s 的类型是 char **  而 "hello world "的类型是 char *

      虽然都是地址, 但是指向的类型不一样,因此,不能这样用。,从其本质来分析,"hello world",代表一个地址,比如0x003001,这个地址中的内容是 'h'

  ,为 char 型,而 s 也保存一个地址 ,这个地址中的内容(*s) 是char * ,是一个指针类型, 所以两者类型是不一样的。 。。

  如果是这样呢?

  char  **s;

      *s = "hello world";

      貌似是合理的,编译也没有问题,但是 printf("%s",*s),就会崩溃

      why??

      咱来慢慢推敲一下。。

      printf("%s",*s); 时,首先得有s 保存的地址,再在这个地址中找到 char *  的地址,即*s;

      举例:

      s = 0x1000;

      在0x1000所在的内存单元中保存了"hello world"的地址 0x003001 , *s = 0x003001;

      这样printf("%s",*s);

      这样会先找到 0x1000,然后找到0x003001;

      如果直接 char  **s;

      *s = "hello world";

      s 变量中保存的是一个无效随机不可用的地址, 谁也不知道它指向哪里。。。。,*s 操作会崩溃。。

      所以用 char **s 时,要给它分配一个内存地址。

      char  **s ;

      s = (char **) malloc(sizeof(char**));

      *s =  "hello world";

      这样 s 给分配了了一个可用的地址,比如 s = 0x412f;

      然后在 0x412f所在的内存中的位置,保存 "hello world"的值。。

    再如:

    #include  <stdio.h>

  void  buf( char **s)

    {

          *s = "message";

    }

    int main()

    {

        char *s ;

        buf(&s);

        printf("%s\n",s);

    }

    二级指针的简单用法。。。。,说白了,二级指针保存的是一级指针的地址,它的类型是指针变量,而一级指针保存的是指向数据所在的内存单元的地址,虽然都是地址,但是类型是不一样的。。。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,919评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,567评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,316评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,294评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,318评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,245评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,120评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,964评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,376评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,592评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,764评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,460评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,070评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,697评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,846评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,819评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,665评论 2 354

推荐阅读更多精彩内容

  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,341评论 0 2
  • 一、框架 1、Mac系统及常用工具、进制;C数据类型、常量变量、运算符、表达式、格式化输入输出 2、关系运算符、逻...
    师景福阅读 689评论 0 1
  • 数组在程序设计中,为了处理方便, 把具有相同类型的若干变量按有序的形式组织起来。这些按序排列的同类数据元素的集合称...
    朱森阅读 3,919评论 2 13
  • 1.基本概念 1.1 存储单元 存储单元一般应具有存储数据和读写数据的功能,以8位(bit)二进制作为一个存储单元...
    Robinone阅读 45,646评论 5 16
  • C语言大总结 一、基础: 1、进制、位权、1字节等于8位(位是计算机的最小储存单位,字节是计算机最小存储单元)、十...
    雾中探雪阅读 2,826评论 1 36