深入 char * ,char ** ,char a[ ] ,char *a[] 内核

来源:互联网 发布:怎么运营好淘宝店铺 编辑:程序博客网 时间:2024/04/28 03:55
【公告】博客系统优化升级     【收藏】Scala 资源一应俱全     博乐招募开始啦     程序员七夕表白礼品指南    
<iframe id="cpro_u2392861_iframe" src="http://pos.baidu.com/zcrm?sz=300x250&amp;rdid=2392861&amp;dc=2&amp;exps=113008&amp;di=u2392861&amp;dri=0&amp;dis=0&amp;dai=1&amp;ps=553x1211&amp;coa=at%3D3%26rsi0%3D300%26rsi1%3D250%26pat%3D17%26tn%3DbaiduCustNativeAD_xuanfu%26rss1%3D%2523FFFFFF%26conBW%3D1%26adp%3D1%26ptt%3D0%26titFF%3D%2525E5%2525BE%2525AE%2525E8%2525BD%2525AF%2525E9%25259B%252585%2525E9%2525BB%252591%26titFS%3D14%26rss2%3D%2523000000%26titSU%3D0&amp;dcb=BAIDU_SSP_define&amp;dtm=HTML_POST&amp;dvi=0.0&amp;dci=-1&amp;dpt=none&amp;tsr=0&amp;tpr=1470821504632&amp;ti=%E6%B7%B1%E5%85%A5%20char%20*%20%2Cchar%20**%20%2Cchar%20a%5B%20%5D%20%2Cchar%20*a%5B%5D%20%E5%86%85%E6%A0%B8%20-%20daiyutage%E7%9A%84%E4%B8%93%E6%A0%8F%20-&amp;ari=2&amp;dbv=2&amp;drs=3&amp;pcs=1222x564&amp;pss=1222x14506&amp;cfv=0&amp;cpl=37&amp;chi=1&amp;cce=true&amp;cec=UTF-8&amp;tlm=1470821504&amp;rw=564&amp;ltu=http%3A%2F%2Fblog.csdn.net%2Fdaiyutage%2Farticle%2Fdetails%2F8604720&amp;ltr=http%3A%2F%2Fmy.csdn.net%2F&amp;ecd=1&amp;psr=1280x720&amp;par=1280x660&amp;pis=-1x-1&amp;ccd=24&amp;cja=true&amp;cmi=61&amp;col=zh-CN&amp;cdo=-1&amp;tcn=1470821505&amp;qn=2b99e35078fc0934&amp;tt=1470821504604.32.180.183" width="300" height="250" align="center,center" vspace="0" hspace="0" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" allowtransparency="true" style="border-width: 0px; border-style: initial; vertical-align: bottom; margin: 0px;"></iframe>
关闭
 

深入 char * ,char ** ,char a[ ] ,char *a[] 内核

标签: Charchar aC语言
 30822人阅读 评论(15) 收藏 举报
 分类:

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

          比如

        

[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  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 plain copy
 print?在CODE上查看代码片派生到我的代码片
  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 plain copy
 print?在CODE上查看代码片派生到我的代码片
  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 plain copy
 print?在CODE上查看代码片派生到我的代码片
  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 plain copy
 print?在CODE上查看代码片派生到我的代码片
  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 plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. printf("%s",*s);  
  2. printf("%s",a[0]);  
  3. printf("%s",*a);  

      都是一样的。。。

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

      再看一个易错的点:

      

[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. char **s = "hello world";  

      这样是错误的,

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

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

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

  如果是这样呢?
    

[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. char  **s;  
  2. *s = "hello world";  

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

       why??

      咱来慢慢推敲一下。。

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

      举例:

      

[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. s = 0x1000;  

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

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

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

      如果直接 char  **s;

      

[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. *s = "hello world";  

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

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

      

[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. char  **s ;  
  2. s = (char **) malloc(sizeof(char**));  
  3. *s =  "hello world";  

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

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

    再如:

   

[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  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.  }  

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

     

     

   

 

      

     

      

      

    

    

     

       

 

     

     

      

   

   

 

 

          

            

       

      

      

 

 

 

 

 

            

 

 

 

 

 

         

         

  

         

        

        

 

 

 

 

 

 

                      

18
0
 
 

我的同类文章

  • _beginthread和CreateThread 创建线程2013-12-08
  • cl.exe 介绍2013-08-19
  • C语言15大头文件介绍2013-02-28
  • C语言32大关键字2013-02-28
  • C 语言四大存储类型。。。2013-02-22
  • crt0.c代码内容解释和编译器构造2013-12-03
  • C语言实现关机2013-02-28
  • 类型的定义与申明2013-02-28
  • 字符串常量到底存放在哪个存储区2013-02-23
  • C关键字之 volatile2013-02-21
更多文章
猜你在找
C语言指针与汇编内存地址
4.5.数组&字符串&结构体&共用体&枚举-C语言高级专题第5部分
C语言指针与汇编内存地址(二)
内存这个大话题-4.1.C语言高级专题第一部分
Visual Studio 2015开发C++程序的基本使用
深入理解c语言char的范围为什么是-128到127而不是-127到127
摘要关于VC中的数据类型转换BSTRchar和CString的深入研究
深入理解C语言中两级指针char pptr的参数的用法
Char类型数值超出范围导致程序陷入死循环深入分析
关于VC中的数据类型转换BSTRchar和CString的深入研究
<iframe id="iframeu1607657_0" src="http://pos.baidu.com/zcrm?sz=728x90&amp;rdid=1607657&amp;dc=2&amp;exps=113008&amp;di=u1607657&amp;dri=0&amp;dis=0&amp;dai=2&amp;ps=12374x342&amp;coa=at%3D3%26rsi0%3D728%26rsi1%3D90%26pat%3D6%26tn%3DbaiduCustNativeAD%26rss1%3D%2523FFFFFF%26conBW%3D1%26adp%3D1%26ptt%3D0%26titFF%3D%2525E5%2525BE%2525AE%2525E8%2525BD%2525AF%2525E9%25259B%252585%2525E9%2525BB%252591%26titFS%3D%26rss2%3D%2523000000%26titSU%3D0%26ptbg%3D90%26piw%3D0%26pih%3D0%26ptp%3D0&amp;dcb=BAIDU_SSP_define&amp;dtm=HTML_POST&amp;dvi=0.0&amp;dci=-1&amp;dpt=none&amp;tsr=0&amp;tpr=1470821504632&amp;ti=%E6%B7%B1%E5%85%A5%20char%20*%20%2Cchar%20**%20%2Cchar%20a%5B%20%5D%20%2Cchar%20*a%5B%5D%20%E5%86%85%E6%A0%B8%20-%20daiyutage%E7%9A%84%E4%B8%93%E6%A0%8F%20-&amp;ari=2&amp;dbv=2&amp;drs=3&amp;pcs=1222x564&amp;pss=1222x14506&amp;cfv=0&amp;cpl=37&amp;chi=1&amp;cce=true&amp;cec=UTF-8&amp;tlm=1470821504&amp;rw=564&amp;ltu=http%3A%2F%2Fblog.csdn.net%2Fdaiyutage%2Farticle%2Fdetails%2F8604720&amp;ltr=http%3A%2F%2Fmy.csdn.net%2F&amp;ecd=1&amp;psr=1280x720&amp;par=1280x660&amp;pis=-1x-1&amp;ccd=24&amp;cja=true&amp;cmi=61&amp;col=zh-CN&amp;cdo=-1&amp;tcn=1470821505&amp;qn=dcbd4f3042ecbabd&amp;tt=1470821504604.34.193.194" width="728" height="90" align="center,center" vspace="0" hspace="0" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" allowtransparency="true" style="border-width: 0px; border-style: initial; vertical-align: bottom; margin: 0px;"></iframe>
查看评论
13楼 piankejingmo 前天 17:49发表 [回复]
大大,请问可以转载吗?
Re: daiyutage 前天 10:27发表 [回复]
回复piankejingmo:欢迎转载
12楼 qq_32818599 2016-05-22 21:46发表 [回复]
获益良多
但在最后的举例中,
char **s ;

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

*s = "hello world";

malloc的类型是不是设置为 char* ?即:

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

因为malloc的节点是指针s指向的一个指针结点,他本身指向char类型 那么他的类型应该是 char * 
不知道理解的对不对 还希望博主指正。
11楼 stigma_whu 2016-04-26 12:11发表 [回复]
LZ在VS2013上, char **s; *s = "hello world";貌似编译通不过,说使用了未初始化的局部变量。
10楼 干的漂亮 2015-12-29 16:12发表 [回复]
char str[10] = {"hello world"};都11个了,10个太少
9楼 oak00001 2015-09-27 06:22发表 [回复]
清晰透彻,经典文章。
8楼 darling0825 2015-09-14 23:48发表 [回复]
好文 谢谢
7楼 vcttyys 2015-05-10 14:26发表 [回复]
不错
6楼 fivsvn 2014-11-03 16:19发表 [回复]
谢谢
5楼 make_APP 2014-08-31 10:15发表 [回复]
终于搞懂了,太赞
4楼 xukai871105 2014-07-09 08:55发表 [回复]
好文章,char *a[] 和 char a[][]的确很容易搞混淆
3楼 手低眼高 2014-06-08 13:55发表 [回复]
收藏了,谢谢
2楼 JKhere 2014-03-31 10:10发表 [回复]
不错
1楼 wenluderen 2013-12-28 13:44发表 [回复]
好文 谢谢
发表评论
  • 用 户 名:
  • piankejingmo
  • 评论内容:
  • 插入代码
      
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
核心技术类目
全部主题 Hadoop AWS 移动游戏 Java Android iOS Swift 智能硬件 Docker OpenStackVPN Spark ERP IE10 Eclipse CRM JavaScript 数据库 Ubuntu NFC WAP jQueryBI HTML5 Spring Apache .NET API HTML SDK IIS Fedora XML LBS UnitySplashtop UML components Windows Mobile Rails QEMU KDE Cassandra CloudStackFTC coremail OPhone CouchBase 云计算 iOS6 Rackspace Web App SpringSide MaemoCompuware 大数据 aptech Perl Tornado Ruby Hibernate ThinkPHP HBase Pure SolrAngular Cloud Foundry Redis Scala Django Bootstrap
    个人资料
     
    daiyutage
     
    • 访问:260123次
    • 积分:2785
    • 等级: 
    • 排名:第9040名
    • 原创:75篇
    • 转载:71篇
    • 译文:0篇
    • 评论:46条
    文章分类
  • c语言(30)
  • c++(6)
  • windows编程(21)
  • 算法(3)
  • Linux(6)
  • Web开发(4)
  • 操作系统(12)
  • 汇编(5)
  • 哲理人生(1)
  • 心路历程(1)
  • android 开发(6)
  • PHP(27)
  • MySQL(2)
  • Java(3)
  • Hadoop(6)
  • Spark(3)
  • Storm(2)
  • hive(1)
  • 数据库(1)
  • redis(1)
  • 分布式架构(0)
  • HBase(1)
  • 消息中间件(4)
  • Kafka(3)
  • Erlang(1)
  • Python(1)
  • Flume(3)
  • Log4j(2)
  • Zookeeper(1)
  • Scala(0)
  • ETL(1)
    文章存档
  • 2016年08月(23)
  • 2016年07月(15)
  • 2016年05月(1)
  • 2016年02月(1)
  • 2015年10月(20)
    展开
    阅读排行
  • C语言文件操作之fgets()(75519)
  • 深入 char * ,char ** ,char a[ ] ,char *a[] 内核(30789)
  • 字符串常量到底存放在哪个存储区(15860)
  • 浅析为什么char类型的范围是 —128~+127(14030)
  • 关于WM_CHAR 消息的解读(11474)
  • C 语言 switch 语句(8246)
  • C语言输入输出函数之 fputs(...)(6508)
  • C语言实现关机(5653)
  • TASM 5.0 安装及使用教程(4627)
  • C 语言main 函数终极探秘(3721)
    评论排行
  • 深入 char * ,char ** ,char a[ ] ,char *a[] 内核(15)
  • 浅析为什么char类型的范围是 —128~+127(15)
  • C语言文件操作之fgets()(6)
  • SVN 服务器的搭建(2)
  • 字符串常量到底存放在哪个存储区(2)
  • 关于WM_CHAR 消息的解读(2)
  • MFC 多线程参数传递(1)
  • C 语言main 函数终极探秘(1)
  • 汇编中各寄存器的功能(1)
  • MFC 读取网页源码(1)
    推荐文章
    • * 致JavaScript也将征服的物联网世界
    • * 从苏宁电器到卡巴斯基:难忘的三年硕士时光
    • * 作为一名基层管理者如何利用情商管理自己和团队(一)
    • * Android CircleImageView圆形ImageView
    • * 高质量代码的命名法则
    最新评论
  • 浅析为什么char类型的范围是 —128~+127

    a5161027: 不局限于8位讨论-128,都是耍流氓。

  • 深入 char * ,char ** ,char a[ ] ,char *a[] 内核

    daiyutage: @piankejingmo:欢迎转载

  • 深入 char * ,char ** ,char a[ ] ,char *a[] 内核

    piankejingmo: 大大,请问可以转载吗?

  • C语言文件操作之fgets()

    lidonghat: @u011131874:行尾有\n,你看不到

  • 深入 char * ,char ** ,char a[ ] ,char *a[] 内核

    qq_32818599: 获益良多但在最后的举例中, char **s ; s = (char **)...

  • C语言文件操作之fgets()

    AnXT: 假设一行为 : 123456789 char s; int ...

  • 深入 char * ,char ** ,char a[ ] ,char *a[] 内核

    stigma_whu: LZ在VS2013上, char **s; *s = "hello world";貌似编译通...

  • 浅析为什么char类型的范围是 —128~+127

    frank_jb: 总结的好

  • C 语言main 函数终极探秘

    JQ_AK47: DANIU

  • 深入 char * ,char ** ,char a[ ] ,char *a[] 内核

    干的漂亮: char str = {"hello world"};都11个了,10个太少


0 0