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

 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所保存的字符串的地址。。。

          比如

        

[cpp] view plaincopy print?
  1. #include <stdio.h>
  2. int main()
  3. {
  4. char *s;
  5. s = "hello";
  6. printf("%p\n",s);
  7. return 0;
  8. }

          

      可以看到 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语言中数组名可以复制给指针表示地址, 但是却不能赋给给数组名,它是一个常量类型,所以不能修改。。

       当然也可以这样:
       

[cpp] view plaincopy print?
  1. char  a [ ] = "hello";
  2. char *s =a;
  3. for(int i= 0; i < strlen(a) ; i++)
  4. printf("%c", s[i]);
  5. 或  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个字节了

            看一下实例:

        

[cpp] view plaincopy print?
  1. #include <stdio.h>
  2. int main()
  3. {
  4.  char *a [ ] = {"China","French","America","German"};
  5.  printf("%p %p %p %p\n",a[0],a[1],a[2],a[3]);
  6.  return 0;
  7. }

    

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

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

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

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

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

char **s;

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

举例:

[cpp] view plaincopy print?
  1. char *a [ ] = {"China","French","America","German"};
  2. char **s =   a;

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

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

即 *s = 00422FB8 = "China";

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

[cpp] view plaincopy print?
  1. printf("%s",*s);
  2. printf("%s",a[0]);
  3. printf("%s",*a);

都是一样的。。。

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

再看一个易错的点:

[cpp] view plaincopy print?
  1. char **s = "hello world";

这样是错误的,

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

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

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

  如果是这样呢?
 

[cpp] view plaincopy print?
  1. char  **s;
  2. *s = "hello world";

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

why??

咱来慢慢推敲一下。。

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

举例:

[cpp] view plaincopy print?
  1. s = 0x1000;

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

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

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

如果直接 char  **s;

[cpp] view plaincopy print?
  1. *s = "hello world";

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

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

[cpp] view plaincopy print?
  1. char  **s ;
  2. s = (char **) malloc(sizeof(char**));
  3. *s =  "hello world";

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

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

再如:

[cpp] view plaincopy print?
  1. #include  <stdio.h>
  2. void  buf( char **s)
  3. {
  4. *s = "message";
  5. }
  6. int main()
  7. {
  8. char *s ;
  9. buf(&s);
  10. printf("%s\n",s);
  11. }

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

转自:http://blog.csdn.net/daiyutage/article/details/8604720

char *与char []相关推荐

  1. char [] 和 char * 区别

    只要记住一点就能很好区分char *和char []:char *定义的是一个指向字符串的指针(注意:C语言中没有对应字符串的内置类型或者类类型),而char []就是C语言中的用来定义字符数组(注意 ...

  2. c语言中 char怎样用,C语言中char*和char[]用法区别分析

    C语言中char*和char[]用法区别分析 本文实例分析了C语言中char* 和 char []的区别.分享给大家供大家参考之用.具体分析如下: 一般来说,很多人会觉得这两个定义效果一样,其实差别很 ...

  3. int main( int argc , char *argv[] , char *envp[] )中参数解说

    原文链接:https://blog.csdn.net/mengyandelove/article/details/82285504 argc是命令行总的参数个数 argv[]是argc个参数,其中第0 ...

  4. 如何将std :: string转换为const char *或char *?

    如何将<code>std::string转换为char*或const char* ? #1楼 看看这个: string str1("stackoverflow"); c ...

  5. c++中关于char数组/char*指针/string类型

    c++中关于char数组/char*指针/string 小渣渣一直被有关string和char[]以及char*的相关问题弄得痛不欲生,传参.返回值.函数调用等到底应该用哪种形式这真的是让小渣癫狂了, ...

  6. char[],char *,string之间转换

    char []与char *之间转换 char []转char *:直接进行赋值即可 // char[] 转char * char str[] = "lala"; char *st ...

  7. const char*, char const*, char*const 的区别

    把一个声明从右向左读. char * const cp; ( * 读成 pointer to ) cp is a const pointer to char char const * p; p is ...

  8. 【转】关于char * 与 char[]

    问题引入: 在实习过程中发现了一个以前一直默认的错误,同样char *c = "abc"和char c[]="abc",前者改变其内 容程序是会崩溃的,而后者完 ...

  9. const char * 和 char const * 和 char * const 区别

    我们都用过const 修饰符修饰一个变量,而且我们知道使用const修饰之后,这个变量相当于常量了,他的值在代码其他部分不能再修改,这对于函数调用时候,防止错误的修改原本不应修改的变量起到很大作用,举 ...

  10. C++中string、char *、char[]的转换

    C++中string.char *.char[]的转换 头段时间有人问过我这个问题,可是我一点头绪都没有,直接说不会.现在从网上找了点资料,看了看,知道点东西了. 一.string转char*. 主要 ...

最新文章

  1. App性能分析数据监控
  2. Compass(更新中。。。)
  3. 软件行业里学历和资历哪个更重要?
  4. Vue自定义指令实现下拉加载:v-loadmore
  5. python三国演义人物出场统计_python爬取三国演义文本
  6. 横空出世,席卷互联网--评微软等公司数据结构+算法面试100题
  7. jenkins配置git出现ERROR: Timeout after 10 minutes 同时命令行出现:Enter passphrase for key 的提示
  8. 2020年12月份学习总结,PMP与信息系统项目管理师的回顾
  9. 【前端基础进阶】JS-Object 功能详解
  10. C++内存管理——指针数组
  11. 特斯拉Model 3本周平均日产约900辆 7000辆周产量有望
  12. Python的算数运算符
  13. 功能表单之智能选择字段类型的使用——JEPLUS软件快速开发平台
  14. 【基础数据库】经济行业分类
  15. iphone申请AppleID后无法登陆App Store
  16. Java与Android配合开发ICQ、2018-8-4
  17. HTML+CSS学习笔记(篇幅较大)
  18. 基于Bootstrap的下拉框多选 Bootstrap Multiselect 插件使用
  19. 工业制造厂房vr虚拟实景展示,真实立体呈现到客户面前
  20. 桌面应用程序脚本录制

热门文章

  1. Android之BaseAdapter—convertView回收机制与动态控件响应
  2. (六)python3 只需3小时带你轻松入门——循环
  3. 橡皮筋还能发动载人飞行器?
  4. 沙漠上不小心挖了个洞,让这个地狱之门般的巨坑,燃烧了50年
  5. 这就是你们有钱人炫富的新方式吗?
  6. 面试可以,但别打扰我睡觉! | 今日最佳
  7. 你试过不用if撸代码吗?
  8. 四大金刚 数据结构_GIS技术在气象领域应用综述
  9. 苹果手机透明桌面_原来苹果手机辨别真假很容易!查看桌面1个图标,就能轻松分辨...
  10. sql 计算 某字段 不同值出现 的次数_教你如何合理选用和计算电线规格,电线回路分配和注意事项...