【C 相关】

C 语言相关网站

1> http://c-faq-chn.sourceforge.net/ccfaq/

2> http://www.vcgood.com/

3> http://www.chinaunix.net/jh/23/

4> http://www.shuax.com 

5> http://www.cguage.com/ 

======================================================================== 

C 语言经典书籍

Expert C Programming(C专家编程)

作   者:(美)LinDen,P.V.D(林登) 著,徐波 译

出版社: 人民邮电出版社

编辑推荐:

      书本撷取了几十个实例,细致、深入地讲解了C的历史、语言特性、声明、数组、指针、链接、运行时、内存以及分析了如何进一步学习C++等问题。 本书是一本ANSI C编程语言的高级读本。它适用于已经编写过C程序的人,以及那些想迅速获取一些专家观点和技巧的人。
  专家级的C编程指南展示优秀C程序员的编程技巧。
  即使你读过Andy Koneig的《C陷阱与缺陷》,你还是应该看看Peter Van Der Linden的书。我想,他们两人的书称都应该千方百计的搞到,如获至宝地捧读。如果我是你的上司,这是必须的要求。
                  ——Francis Glassborow,ACCU主席

内容介绍:

      《C专家编程》展示了最优秀的C程序员所使用的编码技巧,并专门开辟了一章对C++的基础知识进行了介绍。
  书中C的历史、语言特性、声明、数组、指针、链接、运行时、内存以及如何进一步学习C++等问题进行了细致的讲解和深入的分析。全书撷取几十个实例进行讲解,对C程序员具有非常高的实用价值。
  本书可以帮助有一定经验的C程序员成为C编程方面的专家,对于具备相当的C语言基础的程序员,本书可以帮助他们站在C的高度了解和学习C++

我要说话:

      我喜欢作者的写作风格,很有趣,像我,好吧,我承认,我脸皮好厚。

 C语言详解(第五版)

作 者:(美)Hanly,J.R.(汉利),(美)Koffman,E.B.(科夫曼) 著,

             万波,潘蓉,郑海红 译

出版社: 人民邮电出版社

内容介绍:

      本书是C语言的经典教材。与同类图书相比,本书的最大特色在于,不仅深入浅出地讲述了实际开发最需要的C语言基础知识以及动态数据结构和多进程等高级内容,而且在此过程中同时阐述程序设计思想,注重提高学生的问题解决能力和实际编程能力,使学生能够了解和初步掌握当前软件行业公认的程序设计风格和编程实践。书中对指针的处理 非常合理,有效地降低了这一难点的学习门槛。此外,本书有丰富的教学辅助内容,配有各种层次的习题和示例,而且每一章都有一个或多个来自实际生活的实例研究。
  本书可以作为计算机科学专业及其他专业本科生C语言程序设计课程的教材;对已有C语言编程经验的技术人员,本书也是不可多得的参考书。

我要说话:

      人家是教材,我还能说什么,总之,照人家说的做总没错。

 

C语言程序设计现代方法

作   者:(美)King,K.N.(金) 著,吕秀锋 译

出版社: 人民邮电出版社

 

内容介绍:

      时至今日,c语言仍然是计算机领域的通用语言之一,但今天的c语言已经和最初的时候大不相同。本书最主要的一个目的就是通过一种“现代方法”来介绍c语言,实现客观评价c语言、强调标准化c语亨、强调软件工程、不再强调“手工优化”、强调与c++语言的兼容性的目标。本书分为c语言的基础特性、c语言的高级特性、c语言标准库和参考资料4个部分。每章都有“问与答”小节,给出一系列与本章内容相关的问题及其答案,此外还包含适量的习题。
      本书是为大学本科阶段的c语言课程编写的教材,同时也非常适合作为其他一些课程的辅助用书。

作者简介:

      K.N.King,世界知名的计算机程序设计教育家,佐治亚州立大学数学与计算机科学系副教授。耶鲁大学计算机科学硕士,加州大学伯克利分校计算机科学博士,曾任教于佐治亚理工学院。除本书外,他还撰写了广受欢迎的教材Java Programming:From the Beginning,.并在Dr.Dobb's Journal等权威杂志上发表了许多论文。业余时间,King教授还在多部电影中扮演过角色。

我要说话:

      这本书能让你写的C与众不同,哪不同呢,这儿,这儿,还有这儿。

 

C语言核心技术

出版社: 机械工业出版社

  

编辑推荐:

     C程序员在编写程序时手头一定要有这本书。
  C 语言专家 Peter Prinz和Tony Crawford为你提供大量的编程参考信息,本书会成为C语言程序员必备的工作利器!
  “这本书覆盖你希望知道的关于 C语言的一切知识。本书作者完成了一项相当杰出的任务,把语言、标准链接库以及一些重要工具的用法都包含其中。”
                                               —Kyle Loudon,Yahoo!资深工程师
     “本书精选的范例非常有利于读者学习,这比苍白的语言叙述更加有效。这本书不会被放在书架上落灰尘,因为你需要常常翻阅它。”
      —Matthias Kalle Dalheimer,Klaralvdalens Datakonsult AB,董事长兼首席执行官
     “25年来我始终使用C语言编写程序,这本书是我见过的这方面最清楚,最完整的书。所有知识点都使用准确的语言和有序的方式进行描述,这是程序员希望看到的。”
                  —Matt Crawford,Wide Area Systems,Fermilab,集团领导人
     “结合阅读和参考双重功效,你不能错过这本书。这本书可以让你与时俱进,让你的 C 语言编程能力紧跟最新的C99 标准。”
                        —Dave Kitabjian,NetCarrier, Inc., 软件开发经理

内容介绍:

     C程序员在编写程序时手头一定要有这本书。在这本书中,C 语言专家 Peter Prinz和Tony Crawford为你提供大量的编程参考信息。全书叙述清晰,语句简洁,分析深刻。本书主题包括: C 语言的语法、GNU编译器选项、标准链接库函数、GDB和make、预处理指令、C99特色和扩充。
本书内容丰富,总共包含21章,能够让你深刻了解C 语言关键概念,比如类型转换、动态内存管理、指针处理等。想知道 GNU make 或 GNU 调试器的细节吗?本书开辟专门的章节来讲解。一书在手,程序开发会更加顺利!Peter 和 Tony 所编写的这本书会成为C语言程序员必备的工作利器!

我要说话:

      说实话,我还没看呢,好像挺难,那你看明白了吗,额,这个问题,忽略。

 

C程序设计语言

作   者:(美)Brian W.Kernighan(克尼汉),里奇 著,徐宝文,李志 译

出版社: 机械工业出版社

编辑推荐:

      在计算机发展的历史上,没有哪一种程序设计语言像C语言这样应用如此广泛。本书原著即为C语言的设计者之一Dennis M.Ritchie和著名的计算机科学家Brian W.Kernighan合著的一本介绍C语言的权威经典著作。我们现在见到的大量论述C语言程序设计的教材和专著均以此书为蓝本。原著第1版中介绍的C语言成为后来广泛使用的C语言版本——标准C的基础。人们熟知的“hello,world”程序就是由本书首次引入的,现在,这一程序已经成为所有程序设计语言入门的第一课。

内容介绍:

      本书是由C语言的设计者Brian W. Kernighan和Dennis M. Ritchie编写的一部介绍标准C语言及其程序设计方法的权威性经典著作。全面、系统地讲述了C语言的各个特性及程序设计的基本方法,包括基本概念、类型和表达式、控制流、函数与程序结构、指针与数组、结构、输入与输出、UNIX系统接口、标准库等内容。
  本书的讲述深入浅出,配合典型例证,通俗易懂,实用性强,适合作为大专院校计算机专业或非计算机专业的C语言教材,也可以作为从事计算机相关软硬件开发的技术人员的参考书。 在计算机发展的历史上,没有哪一种程序设计语言像C语言这样应用如此广泛。

作者介绍:

      Brian W.Kernighan贝尔实验室计算科学研究中心高级研究人员,著名的计算机科学家。他参加了UNIX系统、C语言、AWK语言和许多其他系统的开发,同时出版了许多在计算机领域具有影响的著作,如《The C Proguamming Language》、《The Elementsof Programming Style》等。
      徐宝文,东南大学计算机科学与工程系教授,博士生导师,江苏省政协常委,江苏省计算机学会副理事长,江苏省软件行业协会副会长,中国计算机学会理事,中国软件行业协会理事。主要从事程序设计语言、软件工程等方面的教学与研究工作,负责承担十多项国家级、部省级科研项目;在国内外发表论文130多篇,出版著译作10多部;担任《实用软件详解丛书》与《新世纪计算机系列教材》的主编,第五次国际青年计算机学术地议大会主席;发起并主办过两次“全国程序设计语言发展与教学学术会议”;先后获航空航天部优秀青年教师、江苏省优秀教育工作者、江苏省优秀青年骨干教师、江苏省感世纪学术带头人等称号。

我要说话:

      呵呵,重量级的人物写得书,我都不知道说什么了,敬礼。

C Traps and Pitfalls(C陷阱与缺陷)

作   者:(美)Andrew Koenig(凯尼格) 著,高巍 译

出版社: 人民邮电出版社

编辑推荐:

      Andrew Koenig自己在Bell实验室时发表的论文为基础,结合自己的工作经验扩展成这本对C程序员具有珍贵价值的经典著作。写作本书的出发点不是要批判C语言,而是要帮助C程序员绕过编程过程中的陷阱和障碍。
本书所揭示的知识,至少能够帮助你减少C代码和初级C++代码中90%的Bug。
                                           ——Francis Glassborow,ACCU主席

内容介绍:

      作者以自己1985年在Bell实验室时发表的一篇论文为基础,结合自己的工作经验扩展成为这本对C程序员具有珍贵价值的经典著作。写作本书的出发点不是要批判C语言,而是要帮助C程序员绕过编程过程中的陷阱和障碍。
  全书分为 8章,分别从词法分析、语法语义、连接、库函数、预处理器、可移植性缺陷等几个方面分析了C编程中可能遇到的问题。最后,作者用一章的篇幅给出了若干具有实用价值的建议。
  本书适合有一定经验的C程序员阅读学习,即便你是C编程高手,本书也应该成为你的案头必备书籍。

作者简介:

      Andrew Koenig AT&T大规模程序研发部(莉贝尔实验室)成员。他从1 986年开始从事C语言的研究,1977年加入贝尔实验室。在1989年ISO/ANSI C++委员会成立时,他就加入了该委员会,并一直担任项目编辑;他还是《Rumirlatio rls on C++》(C++沉思录)、《CTraps and Pitfalls》的作者。

我要说话:

      我都掉了好多次坑后,才看的这本书,汗死,早知道,早点看了。

     

Pointers on C(C和指针)

作 者:(美)里科 著,徐波 译             

出版社: 人民邮电出版社

编辑推荐:

      本书通过对指针的基础知识和高级特性的探讨,帮助程序员把指针的强大功能融入到自己的程序中去。全书共18章,覆盖了数据、语句、操作符和表达式、指针、函数、数组、字符串、结构和联合等几乎所有重要的C编程话题。 本书适合C语言初学者和初级c程序员阅读,也可作为计算机专业学生学习C语言的参考。   
  我竭尽全力地推荐这本我所见过的最好的C编程入门图书。作者深知读者所需,并为他们打下良好基础。如果你已经开始学习C语言但始终不得要领,不妨试一试这本书。
             ——Francis Glassborow,ACCU主席

内容介绍:

      本书提供与C语言编程相关的全面资源和深入讨论。本书通过对指针的基础知识和高级特性的探讨,帮助程序员把指针的强大功能融入到自己的程序中去。
全书共18章,覆盖了数据、语句、操作符和表达式、指针、函数、数组、字符串、结构和联合等几乎所有重要的C编程话题。书中给出了很多编程技巧和提示,每章后面有针对性很强的练习,附录部分则给出了部分练习的解答。
本书适合C语言初学者和初级c程序员阅读,也可作为计算机专业学生学习c语言的参考。

我要说话:

      上帝说,C的指针好强大,之后,赐予我这本书。

 

你必须知道的495个C语言问题(CFAQ)

作   者: (美)Steve Summit(萨米特) 著,孙云,朱群英 译

出版社: 人民邮电出版社

编辑推荐:

     全球C语言程序员集体智慧的结晶
  Amazon全五星图书
  权威解答495个最常遇到的C语言问题
  C是一门简洁精妙的语言,掌握基本语法容易,真正能够自如运用,就不那么简单了。你难免会遇到各种各样的问题,有些可能让你百思不得其解,甚至翻遍图书馆,也找不到问题的答案。
  《你必须知道的495个C语言问题》的出版填补了这一空白。书中内容是世界各地的C语言用户多年来在新闻组comp.1ang.c中讨论的成果。作者在网络版CFAQ列表的基础上进行了大幅度的扩充和丰富,结合代码示例,权威而且详细深入地解答了实际学习和工作中最常遇到的495个C语言问题,涵盖了初始化、数组、指针、字符串、内存分配、库函数、C预处理器等各个方面的主题。许多知识点的阐述都是其他资料中所没有的,弥足珍贵。
  涵盖C99标准
  “本书是Summit以及C FAQ在线列表的许多参与者多年心血的结晶,是C语言界最为珍贵的财富之一。我向所有C语言程序员推荐本书。”.
      ——Francis Glassborow,著名C/C++专家,ACCU(C/C++用户协会)前主席
  “本书清晰地阐明了Kernighan与Ritchie的The C Programming Language一书中许多简略的地方,而且精彩地总结了C语言编程实践,强烈推荐!”
      ——Yechiel M.Kimchi,以色列理工学院

内容介绍:

      本书以问答的形式组织内容,讨论了学习或使用C语言的过程中经常遇到的一些问题。书中列出了C用户经常问的400多个经典问题,涵盖了初始化、数组、指针、字符串、内存分配、库函数、C预处理器等各个方面的主题,并分别给出了解答,而且结合代码示例阐明要点。
  本书结构清晰,讲解透彻,是各高校相关专业C语言课程很好的教学参考书,也是各层次C程序员的优秀实践指南。

作者简介:

      Steve Summit,著名的C语言专家。Usenet C FAQ的创始人和维护者,有近30年的C编程经验。毕业于麻省理工学院。他曾在华盛顿大学教授C语言课程多年。除本书外,他还与人合著了C Unleashed一书。

我要说话:

      不用全读完,有些问题真的很简单,是我水平太高吗?

 

C标准库

作 者:(美)普劳格 著,卢红星,徐明亮,霍建同 译

出版社:人民邮电出版社

编辑推荐:

作  者:(美)普劳格 著,卢红星,徐明亮,霍建同 译

出 版 社:人民邮电出版社

编辑推荐:

     C程序员在编写程序时手头一定要有这本书。在这本书中,C 语言专家 Peter Prinz和Tony Crawford为你提供大量的编程参考信息。全书叙述清晰,语句简洁,分析深刻。本书主题包括: C 语言的语法、GNU编译器选项、标准链接库函数、GDB和make、预处理指令、C99特色和扩充。
本书内容丰富,总共包含21章,能够让你深刻了解C 语言关键概念,比如类型转换、动态内存管理、指针处理等。想知道 GNU make 或 GNU 调试器的细节吗?本书开辟专门的章节来讲解。一书在手,程序开发会更加顺利!Peter 和 Tony 所编写的这本书会成为C语言程序员必备的工作利器!

内容介绍:

      本书集中讨论了C标准库,全面介绍了ANSI/ISO C语言标准的所有库函数。书中通过引用ISO C标准的相关部分,详细讲解了每一个库函数的使用方法,并通过示例描述了其实现细节,且给出了实现和测试这些函数的完整代码。此外,每章结尾附有不同难度的习题,帮助读者巩固和提高。通过此书,读者将会更好地使用C标准库,并学会如何设计和实现库。
  本书结构清晰,内容权威,阐述精辟,对于各层次C程序员和相关专业高校师生都是一本优秀的参考书。

      我最喜欢了,C标准库+Google好强啊。

P.S.:这些书在当当上都买的到,有兴趣看一下。

 

C语言深度解剖

Q: 网络版4.1.5小节好像讲错了,不是编译器的错误,是因为那个指针其实是指向自己的,不知到这个错误改正了没? 

 A: 网友指正的问题都已修正。谢谢指教。

 

陈正冲 湖南沅江人,毕业于长春光学精密机械学院数学系。具有丰富的嵌入式软件开发与管理经验,曾多次举办各种技术和管理方面的讲座和培训。讲课深入、透彻、幽默,深受学员好评。目前从事与CMMI相关的流程管理方面的工作。

?dissection_c at 163.com

Kernighan & Ritchie   ||    The C Programming Language

Linden                          ||     Expert C Programming 

Andrew & Koening     ||     C Traps and Pitfalls

Steve Maguire            ||     Write Clean Code

Steve McConnell       ||      Code Complete. Second Edition

林锐                             ||      高质量C++/C 编程指南

=======================================================================
C 源程序的关键字

所谓关键字就是已被C语言本身使用, 不能作其它用途使用的字。例如关键字不能用作变量名、函数名等 
  由ANSI标准定义的C语言关键字共32个 : 
  auto double int struct break else long switch
  case enum register typedef char extern return union
  const float short unsigned continue for signed void
  default goto sizeof volatile do if while static 
  根据关键字的作用,可以将关键字分为数据类型关键字和流程控制关键字两大类。
  1 数据类型关键字
  A基本数据类型(5个)
  void:声明函数无返回值或无参数,声明无类型指针,显式丢弃运算结果
  char:字符型类型数据,属于整型数据的一种
  int:整型数据,通常为编译器指定的机器字长
  float:单精度浮点型数据,属于浮点数据的一种
  double:双精度浮点型数据,属于浮点数据的一种
  B 类型修饰关键字(4个)
  short:修饰int,短整型数据,可省略被修饰的int。
  long:修饰int,长整形数据,可省略被修饰的int。
  signed:修饰整型数据,有符号数据类型
  unsigned:修饰整型数据,无符号数据类型
  C 复杂类型关键字(5个)
  struct:结构体声明
  union:共用体声明
  enum:枚举声明
  typedef:声明类型别名
  sizeof:得到特定类型或特定类型变量的大小
  D 存储级别关键字(6个)
  auto:指定为自动变量,由编译器自动分配及释放。通常在栈上分配
  static:指定为静态变量,分配在静态变量区,修饰函数时,指定函数作用域为文件内部
  register:指定为寄存器变量,建议编译器将变量存储到寄存器中使用,也可以修饰函数形参,建议编译器通过寄存器而不是堆栈传递参数
  extern:指定对应变量为外部变量,即在另外的目标文件中定义,可以认为是约定由另外文件声明的韵蟮囊桓觥耙 谩?
  const:与volatile合称“cv特性”,指定变量不可被当前线程/进程改变(但有可能被系统或其他线程/进程改变)
  volatile:与const合称“cv特性”,指定变量的值有可能会被系统或其他进程/线程改变,强制编译器每次从内存中取得该变量的值
  2 流程控制关键字
  A 跳转结构(4个)
  return:用在函数体中,返回特定值(或者是void值,即不返回值)
  continue:结束当前循环,开始下一轮循环
  break:跳出当前循环或switch结构
  goto:无条件跳转语句
  B 分支结构(5个)
  if:条件语句
  else:条件语句否定分支(与if连用)
  switch:开关语句(多重分支语句)
  case:开关语句中的分支标记
  default:开关语句中的“其他”分治,可选。
  C 循环结构(3个)
  for:for循环结构,for(1;2;3)4;的执行顺序为1->2->4->3->2...循环,其中2为循环条件
  do:do循环结构,do 1 while(2); 的执行顺序是 1->2->1...循环,2为循环条件
  while:while循环结构,while(1) 2; 的执行顺序是1->2->1...循环,1为循环条件
  以上循环语句,当循环条件表达式为真则继续循环,为假则跳出循环。 新标准  在ANSI标准化后,C语言的标准在一段相当的时间内都保持不变,尽管C++继续在改进。(实际上,Normative Amendment1在1995年已经开发了一个新的C语言版本。但是这个版本很少为人所知。)标准在90年代才经历了改进,这就是ISO9899:1999(1999年出版)。这个版本就是通常提及的C99。它被ANSI于2000年三月采用。 
  在C99中包括的特性有: 
  对编译器限制增加了,比如源程序每行要求至少支持到 4095 字节,变量名函数名的要求支持到 63 字节 (extern 要求支持到 31) 
  预处理增强了。例如: 
  宏支持取参数 #define Macro(...) __VA_ARGS__ 
  使用宏的时候,参数如果不写,宏里用 #,## 这样的东西会扩展成空串。(以前会出错的) 
  支持 // 行注释(这个特性实际上在C89的很多编译器上已经被支持了) 
  增加了新关键字 restrict, inline, _Complex, _Imaginary, _Bool 
  支持 long long, long double _Complex, float _Complex 这样的类型 
  支持 <: :> <% %> %: %:%: ,等等奇怪的符号替代
  支持了不定长的数组。数组的长度就可以用变量了。声明类型的时候呢,就用 int a[*] 这样的写法。不过考虑到效率和实现,这玩意并不是一个新类型。所以就不能用在全局里,或者 struct union 里面,如果你用了这样的东西,goto 语句就受限制了。 
  变量声明不必放在语句块的开头,for 语句提倡这么写 for(int i=0;i<100;++i) 就是说,int i 的声明放在里面,i 只在 for 里面有效。(VC没有遵守这条标准,i 在 for 外也有效) 
  当一个类似结构的东西需要临时构造的时候,可以用 (type_name){xx,xx,xx} 这有点像 C++ 的构造函数 
  初始化结构的时候现在可以这样写: 
  struct {int a[3], b;} hehe[] = { [0].a = , [1].a = 2 }; 
  struct {int a, b, c, d;} hehe = { .a = 1, .c = 3, 4, .b = 5} // 3,4 是对 .c,.d 赋值的 
  字符串里面,\u 支持 unicode 的字符 
  支持 16 进制的浮点数的描述 
  所以 printf scanf 的格式化串多支持了 ll / LL (VC6 里用的 I64) 对应新的 long long 类型。 
  浮点数的内部数据描述支持了新标准,这个可以用 #pragma 编译器指定 
  除了已经有的 __line__ __file__ 以外,又支持了一个 __func__ 可以得到当前的函数名 
  对于非常数的表达式,也允许编译器做化简 
  修改了对于 / % 处理负数上的定义,比如老的标准里 -22 / 7 = -3, -22 % 7 = -1 而现在 -22 / 7 = -4, -22 % 7 = 6 
  取消了不写函数返回类型默认就是 int 的规定 
  允许 struct 定义的最后一个数组写做 [] 不指定其长度描述 
  const const int i; 将被当作 const int i; 处理 
  增加和修改了一些标准头文件, 比如定义 bool 的 <stdbool.h> 定义一些标准长度的 int 的 <inttypes.h> 定义复数的 <complex.h> 定义宽字符的 <wctype.h> 有点泛型味道的数学函数 <tgmath.h> 跟浮点数有关的 <fenv.h>。<stdarg.h> 里多了一个 va_copy 可以复制 ... 的参数。<time.h> 里多了个 struct tmx 对 struct tm 做了扩展 
  输入输出对宽字符还有长整数等做了相应的支持 
  相对于c89的变化还有 
  1、增加restrict指针 
  C99中增加了公适用于指针的restrict类型修饰符,它是初始访问指针所指对象的惟一途径,因此只有借助restrict指针表达式才能访问对象。restrict指针指针主要用做函数变元,或者指向由malloc()函数所分配的内存变量。restrict数据类型不改变程序的语义。 
  如果某个函数定义了两个restrict指针变元,编译程序就假定它们指向两个不同的对象,memcpy()函数就是restrict指针的一个典型应用示例。C89中memcpy()函数原型如下: 
  代码: void *memcpy (void *s1, const void *s2, size_t size); 
  如果s1和s2所指向的对象重叠,其操作就是未定义的。memcpy()函数只能用于不重叠的对象。C99中memcpy()函数原型如下:代码: void *memcpy(void *restrict s1, const void *restrict s2,size_t size); 
  通过使用restrict修饰s1和s2 变元,可确保它们在该原型中指向不同的对象。 
  2、inline(内联)关键字 
  内联函数除了保持结构化和函数式的定义方式外,还能使程序员写出高效率的代码.函数的每次调用与返回都会消耗相当大的系统资源,尤其是当函数调用发生在重复次数很多的循环语句中时.一般情况下,当发生一次函数调用时,变元需要进栈,各种寄存器内存需要保存.当函数返回时,寄存器的内容需要恢复。如果该函数在代码内进行联机扩展,当代码执行时,这些保存和恢复操作旅游活动会再发生,而且函数调用的执行速度也会大大加快。函数的联机扩展会产生较长的代码,所以只应该内联对应用程序性能有显著影响的函数以及长度较短的函数 
  3、新增数据类型 
  _Bool 
  值是0或1。C99中增加了用来定义bool、true以及false宏的头文件夹<stdbool.h>,以便程序员能够编写同时兼容于C与C++的应用程序。在编写新的应用程序时,应该使用 
  <stdbool.h>头文件中的bool宏。 
  _Complex and _Imaginary 
  C99标准中定义的复数类型如下:float_Complex; float_Imaginary; double_Complex; double_Imaginary; long double_Complex; long double_Imaginary. 
  <complex.h>头文件中定义了complex和imaginary宏,并将它们扩展为_Complex和_Imaginary,因此在编写新的应用程序时,应该使用<stdbool.h>头文件中的complex和imaginary宏。 
  long long int 
  C99标准中引进了long long int(-(2e63 - 1)至2e63 - 1)和unsigned long long int(0 - 2e64 - 1)。long long int能够支持的整数长度为64位。 
  4、对数组的增强 
  可变长数组 
  C99中,程序员声明数组时,数组的维数可以由任一有效的整型表达式确定,包括只在运行时才能确定其值的表达式,这类数组就叫做可变长数组,但是只有局部数组才可以是变长的. 
  可变长数组的维数在数组生存期内是不变的,也就是说,可变长数组不是动态的.可以变化的只是数组的大小.可以使用*来定义不确定长的可变长数组。 
  数组声明中的类型修饰符 
  在C99中,如果需要使用数组作为函数变元,可以在数组声明的方括号内使用static关键字,这相当于告诉编译程序,变元所指向的数组将至少包含指定的元素个数。也可以在数组声明的方括号内使用restrict,volatile,const关键字,但只用于函数变元。如果使用restrict,指针是初始访问该对象的惟一途径。如果使用const,指针始终指向同一个数组。使用volatile没有任何意义。 
  5、单行注释 
  引入了单行注释标记 "//" , 可以象C++一样使用这种注释了。 
  6、分散代码与声明 
  7、预处理程序的修改 
  a、变元列表 
  宏可以带变元,在宏定义中用省略号(...)表示。内部预处理标识符__VA_ARGS__决定变元将在何处得到替换。例:#define MySum(...) sum(__VA_ARGS__) 语句MySum(k,m,n); 
  将被转换成:sum(k, m, n); 变元还可以包含变元。例: #define compare(compf, ...) compf(__VA_ARGS__) 其中的compare(strcmp,"small", "large"); 将替换成:strcmp("small","large"); 
  b、_Pragma运算符 
  C99引入了在程序中定义编译指令的另外一种方法:_Pragma运算符。格式如下: 
  _Pragma("directive") 
  其中directive是要满打满算的编译指令。_Pragma运算符允许编译指令参与宏替换。 
  c、内部编译指令 
  STDCFP_CONTRACT ON/OFF/DEFAULT 若为ON,浮点表达式被当做基于硬件方式处理的独立单元。默认值是定义的工具。 
  STDCFEVN_ACCESS ON/OFF/DEFAULT 告诉编译程序可以访问浮点环境。默认值是定义的工具。 
  STDC CX_LIMITED_RANGE ON/OFF/DEFAULT 若值为ON,相当于告诉编译程序某程序某些含有复数的公式是可靠的。默认是OFF。 
  d、新增的内部宏 
  __STDC_HOSTED__ 若操作系统存在,则为1 
  __STDC_VERSION__ 199991L或更高。代表C的版本 
  __STDC_IEC_599__ 若支持IEC 60559浮点运算,则为1 
  __STDC_IEC_599_COMPLEX__ 若支持IEC 60599复数运算,则为1 
  __STDC_ISO_10646__ 由编译程序支持,用于说明ISO/IEC 10646标准的年和月格式:yyymmmL 
  9、复合赋值 
  C99中,复合赋值中,可以指定对象类型的数组、结构或联合表达式。当使用复合赋值时,应在括弧内指定类型,后跟由花括号围起来的初始化列表;若类型为数组,则不能指定数组的大小。建成的对象是未命名的。 
  例: double *fp = (double[]) {1.1, 2.2, 3.3}; 
  该语句用于建立一个指向double的指针fp,且该指针指向这个3元素数组的第一个元素。 在文件域内建立的复合赋值只在程序的整个生存期内有效。在模块内建立的复合赋值是局部对象,在退出模块后不再存在。 
  10、柔性数组结构成员 
  C99中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员,但结构中的柔性数组成员前面必须至少一个其他成员。柔性数组成员允许结构中包含一个大小可变的数组。sizeof返回的这种结构大小不包括柔性数组的内存。包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。 
  11、指定的初始化符 
  C99中,该特性对经常使用稀疏数组的程序员十分有用。指定的初始化符通常有两种用法:用于数组,以及用于结构和联合。用于数组的格式:[index] = vol; 其中,index表示数组的下标,vol表示本数组元素的初始化值。 
  例如: int x[10] = {[0] = 10, [5] = 30}; 其中只有x[0]和x[5]得到了初始化.用于结构或联合的格式如下: 
  member-name(成员名称) 
  对结构进行指定的初始化时,允许采用简单的方法对结构中的指定成员进行初始化。 
  例如: struct example{ int k, m, n; } object = {m = 10,n = 200}; 
  其中,没有初始化k。对结构成员进行初始化的顺序没有限制。 
  12、printf()和scanf()函数系列的增强 
  C99中printf()和scanf()函数系列引进了处理long long int和unsigned long long int数据类型的特性。long long int 类型的格式修饰符是ll。在printf()和scanf()函数中,ll适用于d, i, o, u 和x格式说明符。另外,C99还引进了hh修饰符。当使用d, i, o, u和x格式说明符时,hh用于指定char型变元。ll和hh修饰符均可以用于n说明符。 
  格式修饰符a和A用在printf()函数中时,结果将会输出十六进制的浮点数。格式如下:[-]0xh, hhhhp + d 使用A格式修饰符时,x和p必须是大写。A和a格式修饰符也可以用在scanf()函数中,用于读取浮点数。调用printf()函数时,允许在%f说明符前加上l修饰符,即%lf,但不起作用。 
  13、C99新增的库 
  C89中标准的头文件 
  <assert.h> 定义宏assert() 
  <ctype.h> 字符处理 
  <errno.h> 错误报告 
  <float.h> 定义与实现相关的浮点值勤 
  <limits.h> 定义与实现相关的各种极限值 
  <locale.h> 支持函数setlocale() 
  <math.h> 数学函数库使用的各种定义 
  <setjmp.h> 支持非局部跳转 
  <signal.h> 定义信号值 
  <stdarg.h> 支持可变长度的变元列表 
  <stddef.h> 定义常用常数 
  <stdio.h> 支持文件输入和输出 
  <stdlib.h> 其他各种声明 
  <string.h> 支持串函数 
  <time.h> 支持系统时间函数 
  C99新增的头文件和库 
  <complex.h> 支持复数算法 
  <fenv.h> 给出对浮点状态标记和浮点环境的其他方面的访问 
  <inttypes.h> 定义标准的、可移植的整型类型集合。也支持处理最大宽度整数的函数 
  <iso646.h> 首先在此1995年第一次修订时引进,用于定义对应各种运算符的宏 
  <stdbool.h> 支持布尔数据类型类型。定义宏bool,以便兼容于C++ 
  <stdint.h> 定义标准的、可移植的整型类型集合。该文件包含在<inttypes.h>中 
  <tgmath.h> 定义一般类型的浮点宏 
  <wchar.h> 首先在1995年第一次修订时引进,用于支持多字节和宽字节函数 
  <wctype.h> 首先在1995年第一次修订时引进,用于支持多字节和宽字节分类函数 
  14、__func__预定义标识符 
  用于指出__func__所存放的函数名,类似于字符串赋值。 
  15、其它特性的改动 
  放宽的转换限制 
  限制 C89标准 C99标准 
  数据块的嵌套层数 15 127 
  条件语句的嵌套层数 8 63 
  内部标识符中的有效字符个数 31 63 
  外部标识符中的有效字符个数 6 31 
  结构或联合中的成员个数 127 1023 
  函数调用中的参数个数 31 127 
  不再支持隐含式的int规则 
  删除了隐含式函数声明 
  对返回值的约束 
  C99中,非空类型函数必须使用带返回值的return语句. 
  扩展的整数类型 
  扩展类型 含义 
  int16_t 整数长度为精确16位 
  int_least16_t 整数长度为至少16位 
  int_fast32_t 最稳固的整数类型,其长度为至少32位 
  intmax_t 最大整数类型 
  uintmax_t 最大无符号整数类型 
  对整数类型提升规则的改进 
  C89中,表达式中类型为char,short int或int的值可以提升为int或unsigned int类型. 
  C99中,每种整数类型都有一个级别.例如:long long int 的级别高于int, int的级别高于char等.在表达式中,其级别低于int或unsigned int的任何整数类型均可被替换成int或unsigned int类型. 
  有必要说明的是,c99的main()函数要求必须返回一个int值给程序的激活者(通常是操作系统)0表示正常推出,非0表示异常。
  但是各个公司对C99的支持所表现出来的兴趣不同。当GCC和其它一些商业编译器支持C99的大部分特性的时候,微软和Borland却似乎对此不感兴趣。 C程序实例  1.功能:求三个整数的平均值。程序如下:
  #include <stdio.h> /*载入头文件stdio.h*/
  int main() /*主函数main*/
  {
  int a,b,c,d; /*定义a,b,c,d为整型变量*/
  a=105;
  b=75;
  c=85;
  d=(a+b+c)/3;
  printf("d=%d\n",d); /*显示"d=某数"并换行,某数是d的值*/
  getchar(); /*等待用户,按任意键退出*/
  return 0;
  }
  2.功能:由键盘输入多边形的边数,求其内角和。程序如下:
  #include <stdio.h>
  void main()
  {
  /*辨别多边形是否成立,不成立重新执行do-while语句间的命令*/
  do
  {
  int x;
  printf("n=");
  scanf("%d",&x); /*由键盘输入x的值*/
  printf("%d\n",(x-2)*180); /*显示内角和,公式为(x-2)*180*/
  /*辨别多边形是否成立,不成立显示“Error”,成立显示“Right”*/
  if (x<=2)
  printf("Error\n");
  else
  printf("Right\n");
  }
  while (x<=2); /*do-while语句*/
  getchar();
  return 0;
  }
  3.功能:由键盘输入两个数,显示器上显示较大数。程序如下:
  #include <stdio.h>
  int big(int,int); /*创建整型变量函数big*/
  void main()
  {
  int x,y;
  scanf("%d,%d",&x,&y);
  printf("big is %d\n",big(x,y)); 
  /*输出big函数的值,因为main函数与用户自定义函数big无关系,所以printf后的输出项表是big(x,y),而不是big(n1,n2)*/
  getchar();
  return 0;
  }
  int big(int n1,int n2) /*用户自定义函数big*/
  {
  if (n1>n2)
  return n1; /*返回n1值*/
  else
  return n2; /*返回n2值*/
  }
  4.功能:由键盘输入一个等腰三角形的层数,画一个这样的三角形。程序如下:
  #include <stdio.h>
  void main()
  {
  int i,j,n;
  printf("n:");
  scanf("%d",&n);
  for (i=1;i<=n;i++) /*循环n次,每次输出一行*/
  {
  for (j=1;j<=n-i;j++) /*输出该行前面的空格*/
  printf(" ");
  for (j=1;j<=2*i-1;j++) /*输出该行中的星号*/
  printf("*");
  printf("\n");
  }
  getchar();
  return 0;
  }
  注释:不能输入太多的行数,如10000。
  5.功能:由键盘输入一个最大值,求2到这个最大值中的所有质数(素数)。程序如下:
  #include <stdio.h>
  #include <math.h> /*数学文件*/
  void main()
  {
  int n,m,flag,i,j,num=1;
  printf("n:");
  scanf("%d",&n);
  printf("2~%d:\n",n);
  for (i=2;i<=n;i++) /*循环查找质数*/
  {
  flag=1;
  m=(int)sqrt(i);
  for (j=2;j<=m;j++)
  if (i%j==0) /*条件为真时表示不是质数,退出for循环*/
  {
  flag=0;
  break;
  }
  if (flag==1) /*条件为真时表示i是质数*/
  {
  printf("%4d",i);
  if (num++%10==0) /*每行最多输出10个数*/
  printf("\n");
  }
  }
  printf("\n");
  getchar();
  return 0;
  }
  6.功能:显示保护屏幕。程序如下:
  #include <stdlib.h> /*综合库*/
  #include <graphics.h> /*绘图库*/
  int main()
  {
  int gdriver=DETECT; /*定义图象驱动器为DETECT*/
  int gmode=DETECT; /*定义图象模式为DETECT*/
  long n; /*定义长型变量n*/
  initgraph(&gdriver,&gmode,"E:\TC\bgi");
  /*进入图象状态(警告:""之间的是图象驱动器的路径,建议网友们设置为空,即只有"",或设为编程系统的路径,例路径是C:\TC,那么就是输入C:\TC\bgi)*/
  sleep(1); /*暂停一秒,以保证无误差进入图象状态*/
  for (n=1;n<=40000000;n++) /*画40000000个圆*/
  {
  setcolor(rand()); /*作图颜色随机*/
  circle(random(639),random(639),25); /*画圆,位置随机,半径为25*/
  }
  getchar();
  return 0;
  }

==================================================================

C 语言指针详解 

摘自网易广州社区的C语言版开发精华区 

                                                                     girlrong   ------ C语言版版主

第一章。指针的概念

指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区。让我们分别说明。  

先声明几个指针放着做例子:  

例一:  

(1)int *ptr;  

(2)char *ptr;  

(3)int **ptr;  

(4)int (*ptr)[3];  

(5)int *(*ptr)[4];  

如果看不懂后几个例子的话,请参阅我前段时间贴出的文章<<如何理解c和c++的复杂类型声明>>。  
 

1。 指针的类型。  

从语法的角度看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。让我们看看例一中各个指针的类型:  

(1)int *ptr; //指针的类型是int *  

(2)char *ptr; //指针的类型是char *  

(3)int **ptr; //指针的类型是 int **  

(4)int (*ptr)[3]; //指针的类型是 int(*)[3]  

(5)int *(*ptr)[4]; //指针的类型是 int *(*)[4]  

怎么样?找出指针的类型的方法是不是很简单?  

2。指针所指向的类型。  

当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。

从语法上看,你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。例如:  

(1)int *ptr; //指针所指向的类型是int  

(2)char *ptr; //指针所指向的的类型是char  

(3)int **ptr; //指针所指向的的类型是 int *  

(4)int (*ptr)[3]; //指针所指向的的类型是 int()[3]  

(5)int *(*ptr)[4]; //指针所指向的的类型是 int *()[4]  

在指针的算术运算中,指针所指向的类型有很大的作用。  

指针的类型(即指针本身的类型)和指针所指向的类型是两个概念。当你对C越来越熟悉时,你会发现,把与指针搅和在一起的“类型”这个概念分成“指针的类型”和“指针所指向的类型”两个概念,是精通指针的关键点之一。我看了不少书,发现有些写得差的书中,就把指针的这两个概念搅在一起了,所以看起书来前后矛盾,越看越糊涂。

3。 指针的值,或者叫指针所指向的内存区或地址。  

指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长。  

指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。  

指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在例一中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。  

以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指向的类型是什么?该指针指向了哪里?  

4。 指针本身所占据的内存区。  

指针本身占了多大的内存?你只要用函数sizeof(指针的类型)测一下就知道了。在32位平台里,指针本身占据了4个字节的长度。

指针本身占据的内存这个概念在判断一个指针表达式是否是左值时很有用。  

第二章。指针的算术运算
 
指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减运算的意义是不一样的。例如:  

例二:  

1。 char a[20];  

2。 int *ptr=a;  

...  

...  

3。 ptr++;  

在上例中,指针ptr的类型是int*,它指向的类型是int,它被初始化为指向整形变量a。接下来的第3句中,指针ptr被加了1,编译器是这样处理的:它把指针ptr的值加上了sizeof(int),在32位程序中,是被加上了4。由于地址是用字节做单位的,故ptr所指向的地址由原来的变量a的地址向高地址方向增加了4个字节。

由于char类型的长度是一个字节,所以,原来ptr是指向数组a的第0号单元开始的四个字节,此时指向了数组a中从第4号单元开始的四个字节。

我们可以用一个指针和一个循环来遍历一个数组,看例子:  

例三:  

int array[20];  

int *ptr=array;  

...  

//此处略去为整型数组赋值的代码。  

...  

for(i=0;i<20;i++)  

{  

(*ptr)++;  

ptr++;  

}  

这个例子将整型数组中各个单元的值加1。由于每次循环都将指针ptr加1,所以每次循环都能访问数组的下一个单元。再看例子:  

例四:  

1。 char a[20];  

2。 int *ptr=a;  

...  

...  

3。 ptr+=5;  

在这个例子中,ptr被加上了5,编译器是这样处理的:将指针ptr的值加上5乘sizeof(int),在32位程序中就是加上了5乘4=20。由于地址的单位是字节,故现在的ptr所指向的地址比起加5后的ptr所指向的地址来说,向高地址方向移动了20个字节。在这个例子中,没加5前的ptr指向数组a的第0号单元开始的四个字节,加5后,ptr已经指向了数组a的合法范围之外了。虽然这种情况在应用上会出问题,但在语法上却是可以的。这也体现出了指针的灵活性。  

如果上例中,ptr是被减去5,那么处理过程大同小异,只不过ptr的值是被减去5乘sizeof(int),新的ptr指向的地址将比原来的ptr所指向的地址向低地址方向移动了20个字节。  

总结一下,一个指针ptrold加上一个整数n后,结果是一个新的指针ptrnew,ptrnew的类型和ptrold的类型相同,ptrnew所指向的类型和ptrold所指向的类型也相同。ptrnew的值将比ptrold的值增加了n乘sizeof(ptrold所指向的类型)个字节。就是说,ptrnew所指向的内存区将比ptrold所指向的内存区向高地址方向移动了n乘sizeof(ptrold所指向的类型)个字节。一个指针ptrold减去一个整数n后,结果是一个新的指针ptrnew,ptrnew的类型和ptrold的类型相同,ptrnew所指向的类型和ptrold所指向的类型也相同。ptrnew的值将比ptrold的值减少了n乘sizeof(ptrold所指向的类型)个字节,就是说,ptrnew所指向的内存区将比ptrold所指向的内存区向低地址方向移动了n乘sizeof(ptrold所指向的类型)个字节。
 
第三章。运算符&和*
这里&是取地址运算符,*是...书上叫做“间接运算符”。&a的运算结果是一个指针,指针的类型是a的类型加个*,指针所指向的类型是a的类型,指针所指向的地址嘛,那就是a的地址。*p的运算结果就五花八门了。总之*p的结果是p所指向的东西,这个东西有这些特点:它的类型是p指向的类型,它所占用的地址是p所指向的地址。

例五:  

int a=12;  

int b;  

int *p;  

int **ptr;  

p=&a;//&a的结果是一个指针,类型是int*,指向的类型是int,指向的地址是a的地址。  

*p=24;//*p的结果,在这里它的类型是int,它所占用的地址是p所指向的地址,显然,*p就是变量a。

ptr=&p;//&p的结果是个指针,该指针的类型是p的类型加个*,在这里是int**。该指针所指向的类型是p的类型,这里是int*。该指针所指向的地址就是指针p自己的地址。  

*ptr=&b;//*ptr是个指针,&b的结果也是个指针,且这两个指针的类型和所指向的类型是一样的,所以?amp;b来给*ptr赋值就是毫无问题的了。

**ptr=34;//*ptr的结果是ptr所指向的东西,在这里是一个指针,对这个指针再做一次*运算,结果就是一个int类型的变量。

第四章。指针表达式。 
一个表达式的最后结果如果是一个指针,那么这个表达式就叫指针表达式。下面是一些指针表达式的例子:  

例六:  

int a,b;  

int array[10];  

int *pa;  

pa=&a;//&a是一个指针表达式。  

int **ptr=&pa;//&pa也是一个指针表达式。  

*ptr=&b;//*ptr和&b都是指针表达式。  

pa=array;  

pa++;//这也是指针表达式。  

例七:  

char *arr[20];  

char **parr=arr;//如果把arr看作指针的话,arr也是指针表达式  

char *str;  

str=*parr;//*parr是指针表达式  

str=*(parr+1);//*(parr+1)是指针表达式  

str=*(parr+2);//*(parr+2)是指针表达式  

由于指针表达式的结果是一个指针,所以指针表达式也具有指针所具有的四个要素:指针的类型,指针所指向的类型,指针指向的内存区,指针自身占据的内存。

好了,当一个指针表达式的结果指针已经明确地具有了指针自身占据的内存的话,这个指针表达式就是一个左值,否则就不是一个左值。 在例七中,&a不是一个左值,因为它还没有占据明确的内存。*ptr是一个左值,因为*ptr这个指针已经占据了内存,其实*ptr就是指针pa,既然pa已经在内存中有了自己的位置,那么*ptr当然也有了自己的位置。

第五章。数组和指针的关系
 
如果对声明数组的语句不太明白的话,请参阅我前段时间贴出的文章<<如何理解c和c++的复杂类型声明>>。 数组的数组名其实可以看作一个指针。看下例:  

例八:  

int array[10]={0,1,2,3,4,5,6,7,8,9},value;  

...  

...  

value=array[0];//也可写成:value=*array;  

value=array[3];//也可写成:value=*(array+3);  

value=array[4];//也可写成:value=*(array+4);  

上例中,一般而言数组名array代表数组本身,类型是int [10],但如果把array看做指针的话,它指向数组的第0个单元,类型是int *,所指向的类型是数组单元的类型即int。因此*array等于0就一点也不奇怪了。同理,array+3是一个指向数组第3个单元的指针,所以*(array+3)等于3。其它依此类推。  

例九:  

char *str[3]={  

"Hello,this is a sample!",  

"Hi,good morning.",  

"Hello world"  

};  

char s[80];  

strcpy(s,str[0]);//也可写成strcpy(s,*str);  

strcpy(s,str[1]);//也可写成strcpy(s,*(str+1));  

strcpy(s,str[2]);//也可写成strcpy(s,*(str+2));  

上例中,str是一个三单元的数组,该数组的每个单元都是一个指针,这些指针各指向一个字符串。把指针数组名str当作一个指针的话,它指向数组的第0号单元,它的类型是char**,它指向的类型是char *。

*str也是一个指针,它的类型是char*,它所指向的类型是char,它指向的地址是字符串"Hello,this is a sample!"的第一个字符的地址,即'H'的地址。 str+1也是一个指针,它指向数组的第1号单元,它的类型是char**,它指向的类型是char *。  

*(str+1)也是一个指针,它的类型是char*,它所指向的类型是char,它指向"Hi,good morning."的第一个字符'H',等等。  

下面总结一下数组的数组名的问题。声明了一个数组TYPE array[n],则数组名称array就有了两重含义:第一,它代表整个数组,它的类型是TYPE [n];第二,它是一个指针,该指针的类型是TYPE*,该指针指向的类型是TYPE,也就是数组单元的类型,该指针指向的内存区就是数组第0号单元,该指针自己占有单独的内存区,注意它和数组第0号单元占据的内存区是不同的。该指针的值是不能修改的,即类似array++的表达式是错误的。  

在不同的表达式中数组名array可以扮演不同的角色。  

在表达式sizeof(array)中,数组名array代表数组本身,故这时sizeof函数测出的是整个数组的大小。  

在表达式*array中,array扮演的是指针,因此这个表达式的结果就是数组第0号单元的值。sizeof(*array)测出的是数组单元的大小。  

表达式array+n(其中n=0,1,2,....。)中,array扮演的是指针,故array+n的结果是一个指针,它的类型是TYPE*,它指向的类型是TYPE,它指向数组第n号单元。故sizeof(array+n)测出的是指针类型的大小。  

例十:  

int array[10];  

int (*ptr)[10];  

ptr=&array;  

上例中ptr是一个指针,它的类型是int (*)[10],他指向的类型是int [10],我们用整个数组的首地址来初始化它。在语句ptr=&array中,array代表数组本身。  

本节中提到了函数sizeof(),那么我来问一问,sizeof(指针名称)测出的究竟是指针自身类型的大小呢还是指针所指向的类型的大小?答案是前者。例如:

int (*ptr)[10];  

则在32位程序中,有:  

sizeof(int(*)[10])==4  

sizeof(int [10])==40  

sizeof(ptr)==4  

实际上,sizeof(对象)测出的都是对象自身的类型的大小,而不是别的什么类型的大小。  

第六章。指针和结构类型的关系

可以声明一个指向结构类型对象的指针。  

例十一:  

struct MyStruct  

{  

int a;  

int b;  

int c;  

}  

MyStruct ss={20,30,40};//声明了结构对象ss,并把ss的三个成员初始化为20,30和40。

MyStruct *ptr=&ss;//声明了一个指向结构对象ss的指针。它的类型是

MyStruct*,它指向的类型是MyStruct。

int *pstr=(int*)&ss;//声明了一个指向结构对象ss的指针。但是它的类型和它指向的类型和ptr是不同的。

请问怎样通过指针ptr来访问ss的三个成员变量?  

答案:  

ptr->a;  

ptr->b;  

ptr->c;  

又请问怎样通过指针pstr来访问ss的三个成员变量?  

答案:  

*pstr;//访问了ss的成员a。  

*(pstr+1);//访问了ss的成员b。  

*(pstr+2)//访问了ss的成员c。  

呵呵,虽然我在我的MSVC++6.0上调式过上述代码,但是要知道,这样使用pstr来访问结构成员是不正规的,为了说明为什么不正规,让我们看看怎样通过指针来访问数组的各个单元:  

例十二:  

int array[3]={35,56,37};  

int *pa=array;  

通过指针pa访问数组array的三个单元的方法是:  

*pa;//访问了第0号单元  

*(pa+1);//访问了第1号单元  

*(pa+2);//访问了第2号单元  

从格式上看倒是与通过指针访问结构成员的不正规方法的格式一样。

所有的C/C++编译器在排列数组的单元时,总是把各个数组单元存放在连续的存储区里,单元和单元之间没有空隙。但在存放结构对象的各个成员时,在某种编译环境下,可能会需要字对齐或双字对齐或者是别的什么对齐,需要在相邻两个成员之间加若干个“填充字节”,这就导致各个成员之间可能会有若干个字节的空隙。

所以,在例十二中,即使*pstr访问到了结构对象ss的第一个成员变量a,也不能保证*(pstr+1)就一定能访问到结构成员b。因为成员a和成员b之间可能会有若干填充字节,说不定*(pstr+1)就正好访问到了这些填充字节呢。这也证明了指针的灵活性。要是你的目的就是想看看各个结构成员之间到底有没有填充字节,嘿,这倒是个不错的方法。  

通过指针访问结构成员的正确方法应该是象例十二中使用指针ptr的方法。

 

第七章。指针和函数的关系

 

 

可以把一个指针声明成为一个指向函数的指针。  

int fun1(char*,int);  

int (*pfun1)(char*,int);  

pfun1=fun1;  

....  

....  

int a=(*pfun1)("abcdefg",7);//通过函数指针调用函数。  

可以把指针作为函数的形参。在函数调用语句中,可以用指针表达式来作为实参。  

例十三:  

 

int fun(char*);  

 

int a;  

 

char str[]="abcdefghijklmn";  

 

a=fun(str);  

 

...  

 

...  

 

int fun(char*s)  

 

{  

 

int num=0;  

 

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

 

{  

 

num+=*s;s++;  

 

}  

 

return num;  

 

}  

 

这个例子中的函数fun统计一个字符串中各个字符的ASCII码值之和。前面说了,数组的名字也是一个指针。在函数调用中,当把str作为实参传递给形参s后,实际是把str的值传递给了s,s所指向的地址就和str所指向的地址一致,但是str和s各自占用各自的存储空间。在函数体内对s进行自加1运算,并不意味着同时对str进行了自加1运算。  

 

 

 

第八章。指针类型转换

 

 

 

当我们初始化一个指针或给一个指针赋值时,赋值号的左边是一个指针,赋值号的右边是一个指针表达式。在我们前面所举的例子中,绝大多数情况下,指针的类型和指针表达式的类型是一样的,指针所指向的类型和指针表达式所指向的类型是一样的。  

 

例十四:  

 

1。 float f=12.3;  

 

2。 float *fptr=&f;  

 

3。 int *p;  

 

在上面的例子中,假如我们想让指针p指向实数f,应该怎么搞?是用下面的语句吗?  

 

p=&f;  

 

不对。因为指针p的类型是int*,它指向的类型是int。表达式&f的结果是一个指针,指针的类型是float*,它指向的类型是float。两者不一致,直接赋值的方法是不行的。至少在我的MSVC++6.0上,对指针的赋值语句要求赋值号两边的类型一致,所指向的类型也一致,其它的编译器上我没试过,大家可以试试。为了实现我们的目的,需要进行“强制类型转换”:  

 

p=(int*)&f;  

 

如果有一个指针p,我们需要把它的类型和所指向的类型改为TYEP*和TYPE,那么语法格式是:  

 

(TYPE*)p;  

 

这样强制类型转换的结果是一个新指针,该新指针的类型是TYPE*,它指向的类型是TYPE,它指向的地址就是原指针指向的地址。而原来的指针p的一切属性都没有被修改。  

 

一个函数如果使用了指针作为形参,那么在函数调用语句的实参和形参的结合过程中,也会发生指针类型的转换。

 

例十五:  

 

void fun(char*);  

 

int a=125,b;  

 

fun((char*)&a);  

 

...  

 

...  

 

void fun(char*s)  

 

{  

 

char c;  

 

c=*(s+3);*(s+3)=*(s+0);*(s+0)=c;  

 

c=*(s+2);*(s+2)=*(s+1);*(s+1)=c;  

 

}  

 

注意这是一个32位程序,故int类型占了四个字节,char类型占一个字节。函数fun的作用是把一个整数的四个字节的顺序来个颠倒。注意到了吗?在函数调用语句中,实?amp;a的结果是一个指针,它的类型是int *,它指向的类型是int。形参这个指针的类型是char*,它指向的类型是char。这样,在实参和形参的结合过程中,我们必须进行一次从int*类型到char*类型的转换。结合这个例子,我们可以这样来想象编译器进行转换的过程:编译器先构造一个临时指针 char*temp,然后执行temp=(char*)&a,最后再把temp的值传递给s。所以最后的结果是:s的类型是char*,它指向的类型是char,它指向的地址就是a的首地址。  

 

我们已经知道,指针的值就是指针指向的地址,在32位程序中,指针的值其实是一个32位整数。那可不可以把一个整数当作指针的值直接赋给指针呢?就象下面的语句:  

 

unsigned int a;  

 

TYPE *ptr;//TYPE是int,char或结构类型等等类型。  

 

...  

 

...  

 

a=20345686;  

 

ptr=20345686;//我们的目的是要使指针ptr指向地址20345686(十进制)  

 

ptr=a;//我们的目的是要使指针ptr指向地址20345686(十进制)  

 

编译一下吧。结果发现后面两条语句全是错的。那么我们的目的就不能达到了吗?不,还有办法:  

 

unsigned int a;  

 

TYPE *ptr;//TYPE是int,char或结构类型等等类型。  

 

...  

 

...  

 

a=某个数,这个数必须代表一个合法的地址;  

 

ptr=(TYPE*)a;//呵呵,这就可以了。  

 

严格说来这里的(TYPE*)和指针类型转换中的(TYPE*)还不一样。这里的(TYPE*)的意思是把无符号整数a的值当作一个地址来看待。  

 

上面强调了a的值必须代表一个合法的地址,否则的话,在你使用ptr的时候,就会出现非法操作错误。  

 

想想能不能反过来,把指针指向的地址即指针的值当作一个整数取出来。完全可以。下面的例子演示了把一个指针的值当作一个整数取出来,然后再把这个整数当作一个地址赋给一个指针:  

 

例十六:  

 

int a=123,b;  

 

int *ptr=&a;  

 

char *str;  

 

b=(int)ptr;//把指针ptr的值当作一个整数取出来。  

 

str=(char*)b;//把这个整数的值当作一个地址赋给指针str。  

 

好了,现在我们已经知道了,可以把指针的值当作一个整数取出来,也可以把一个整数值当作地址赋给一个指针。  

 

 

 

第九章。指针的安全问题

 

看下面的例子:  

 

例十七:  

 

char s='a';  

 

int *ptr;  

 

ptr=(int*)&s;  

 

*ptr=1298;  

 

指针ptr是一个int*类型的指针,它指向的类型是int。它指向的地址就是s的首地址。在32位程序中,s占一个字节,int类型占四个字节。最后一条语句不但改变了s所占的一个字节,还把和s相临的高地址方向的三个字节也改变了。这三个字节是干什么的?只有编译程序知道,而写程序的人是不太可能知道的。也许这三个字节里存储了非常重要的数据,也许这三个字节里正好是程序的一条代码,而由于你对指针的马虎应用,这三个字节的值被改变了!这会造成崩溃性的错误。让我们再来看一例:  

 

例十八:  

 

1。 char a;  

 

2。 int *ptr=&a;  

 

...  

 

...  

 

3。 ptr++;  

 

4。 *ptr=115;  

 

该例子完全可以通过编译,并能执行。但是看到没有?第3句对指针ptr进行自加1运算后,ptr指向了和整形变量a相邻的高地址方向的一块存储区。这块存储区里是什么?我们不知道。有可能它是一个非常重要的数据,甚至可能是一条代码。而第4句竟然往这片存储区里写入一个数据!这是严重的错误。所以在使用指针时,程序员心里必须非常清楚:我的指针究竟指向了哪里。  

 

在用指针访问数组的时候,也要注意不要超出数组的低端和高端界限,否则也会造成类似的错误。  

 

在指针的强制类型转换:ptr1=(TYPE*)ptr2中,如果sizeof(ptr2的类型)大于sizeof(ptr1的类型),那么在使用指针ptr1来访问ptr2所指向的存储区时是安全的。如果sizeof(ptr2的类型)小于sizeof(ptr1的类型),那么在使用指针ptr1来访问ptr2所指向的存储区时是不安全的。至于为什么,读者结合例十七来想一想,应该会明白的。

 

 

请写出以下程序的运行结果:

 

#include<stdio.h>

int *p;

pp(int a,int *b);

main()

{

int a=1,b=2,c=3;

p=&b;

pp(a+c,&b);

printf("(1)%d%d%dn",a,b,*p);

}

pp(int a,int *b)

{int c=4;

*p=*b+c;

a=*p-c;

printf("(2)%d%d%dn",a,*b,*p);

}

 
==========================================

高人指点 如何学习C语言

高人指点 如何学习C语言 

     很多人对学习C语言感到无从下手,经常问我同一个问题:究竟怎样学习C语言?我是一个教师,已经开发了很多年的程序,和很多刚刚起步的人一样,学习的第一个计算机语言就是C语言。经过这些年的开发,我深深的体会到C语言对于一个程序设计人员多么的重要,如果不懂C语言,你想写底层程序这几乎听起来很可笑,不懂C语言,你想写出优秀高效的程庌輌这简直就是天方夜谭。为什么C语言如此重要呢? 

 

     第一:C语言语法结构很简洁精妙,写出的程序也很高效,很便于描述算法,大多数的程序员愿意使用C语言去描述算法本身,所以,如果你想在程序设计方面有所建树,就必须去学它。 

 

     第二:C语言能够让你深入系统底层,你知道的操作系统,哪一个不是C语言写的?所有的windows,Unix,Linux,Mac,os/2,没有一个里外的,如果你不懂C语言,怎么可能深入到这些操作系统当中去呢?更不要说你去写它们的内核程序了。 

 

     第三:很多新型的语言都是衍生自C语言,C++,Java,C#,J#,perl...哪个不是呢?掌握了C语言,可以说你就掌握了很多门语言,经过简单的学习,你就可以用这些新型的语言去开发了,这个再一次验证了C语言是程序设计的重要基础。还有啊,多说一点:即使现在招聘程序员,考试都是考C语言,你想加入it行业,那么就一定要掌握好C语言。 

 

     那么究竟怎样学习C语言呢? 

 

     1:工欲善其事,必先利其器 

     这里介绍几个学习C语言必备的东东: 

     一个开发环境,例如turbo C 2.0,这个曾经占据了DOS时代开发程序的大半个江山。但是现在windows时代,用turbo C有感觉不方面,编辑程序起来很吃力,并且拖放,更没有函数变量自动感应功能,查询参考资料也不方便。建议使用Visual C++,这个东西虽然比较大块头,但是一旦安装好了,用起来很方便。 

 

     一本学习教程,现在C语言教材多如牛毛,但推荐大家使用《C语言程序设计》谭浩强主编 第二版 清华大学出版社,此书编写的很适合初学者,并且内容也很精到。 

 

     除此以外,现在有很多辅助学习的软件,毕竟现在是Window时代了,学习软件多如牛毛,不象我们当初学习,只有读书做题这么老套。我向大家推荐一个“集成学习环境(C语言)”,里边的知识点总结和例程讲解都非常好,还有题库测试环境,据说有好几千题,甚至还有一个windows下的trubo C,初学者甚至不用装其它的编译器,就可以练习编程了,非常适合初学者。还有一个“C语言学习系统”软件,不过感觉只是一个题库系统,如果你觉得题做的不够,不妨也可以试试。 

 

     2:葵花宝典 

     学习计算机语言最好的方法是什么?答曰:读程序。 

 

     没错,读程序是学习C语言入门最快,也是最好的方法。如同我,现在学习新的J#,C#等其他语言,不再是抱着书本逐行啃,而是学习它们的例程。当然,对于没有学过任何计算机语言的初学者,最好还是先阅读教程,学习完每一章,都要认真体会这一章的所有概念,然后不放过这一章中提到的所有例程,然后仔细研读程序,直到每一行都理解了,然后找几个编程题目,最好是和例程类似的或一样的,自己试图写出这段已经读懂的程序,不要以为例程你已经读懂了,你就可以写出和它一样的程序,绝对不一定,不相信你就试一试吧,如果写不出来,也不要着急,回过头来再继续研究例程,想想自己为什么写不出来,然后再去写这段程序,反反复复,直到你手到擒来为止,祝贺你,你快入门了。 

 

     3:登峰造极 

     写程序的最高境界其实就是掌握各种解决问题的手段(数据结构)和解决问题的方法(算法)。 

 

     是不是写出底层程序就是程序设计高手呢?非也,写底层程序,无非是掌握了硬件的结构,况且硬件和硬件还不一样,要给一个芯片写驱动程序,无非就是掌握这块芯片的各种寄存器及其组合,然后写值读值,仅此而已。这不过是熟悉一些io函数罢了。那么怎样才算精通程序设计呢?怎样才能精通程序设计呢?举个例子:你面前有10个人,找出一个叫“张三”的人,你该怎么办?第一种方法:直接对这10个人问:“谁叫张三”。第2种方法:你挨个去问“你是不是张三?”,直到问到的这个人就是张三。第三种方法:你去挨个问一个人“你认不认识张三,指给我看”。不要小看这个问题,你说当然会选第一种方法,没错恭喜你答对了,因为这个方法最快,效率最高,但是在程序设计中找到解决问题的最优方法和你用的手段却是考验一个程序员程序设计水平的重要标志,而且是不容易达到的。刚才这个问题类似于数据结构和算法中的:Map数据结构,穷举查找和折半查找。所以掌握好数据结构和一些常用算法,是登峰造极的必然之路。最后给大家推荐严尉敏的《数据结构》清华大学出版社,希望每一个想成为程序设计高手的人研读此书。 

二,语言学习方法 

   

谈谈偶的学习方法,抛砖引玉! 

 

     学习不论讲不讲方法,最终都能学会。但是别人1个月学会了,而你却100年才学会,这不就晚了么?:)所以说,学习还是要讲究方法的。学习方法正确,事半功倍;学习方法不正确,事倍而功半。 

 

什么才是学习C语言的捷径?我的答案是看书。书中所写,是前人数十年经验所写,看十本书,就相当于汲取了前人数十年的功力,那么你的内功也会迅速上升1甲子。:)书当然要看好书,只有好书才营养丰富。假如你花了1天时间看了一本破书,而该书介绍的知识虽然对于你来说是全新的知识,但是由于书中组织不当、或者深度不够,使你获取的营养很少,还不如自己研究一天来的快,这种书就不值当看了。学习C语言要看那些好书?我认为首先要把基础书给看扎实了,比如《C语言之四书五经》中介绍的4本。(虽然这些书很多已经绝版了,但我相信电子版也是很有益处。况且,如果你真的想看,我相信你一定有办法搞的到。)这些书你会在很短的时间内看完(比如一两个月),这取决于你的基础和悟性。之后要看那些书呢?我不妨再列几本。Bjarne Stroustrup的《C++程序设计语言》(The C++ Programming Language)一定要看,这本书里面对于C的一些基础概念的定义,比我见过的其他任何C语言书都要全,都要仔细;Bjarne Stroustrup的《C++语言的设计与演化》(The Design and Evolution of C++)和David R.Hanson 的《C语言接口与实现 创建可重用软件的技术》(C Interfaces and Implaementations Techniques for Creating Reusable Software)一定要看,这两本书讲述了如何用C来实现异常处理、实现类型的封装和扩展等一些大的项目中经常用到的高级技术。其他的书,操作系统的要看,编译原理的要看,算法的要看,模式的也要看。读书破万卷,coding如有神。总而言之,就如《传》中云:“生而知之者,上也;学而知之者,次也;困而学之又其次也。”我们不能总是因困而学之,而要做到兵马未动,粮草先行。 

     看书是学习的导向,书中能一一介绍清楚概念,但书却不能把应用的细节一一介绍给你,因为应用总是招数繁复,变化多端。因此我们要想熟悉招数,懂得书中所讲怎么使用,还要多读源码。Linus给别人解答问题的时候,常说Read the ****ing source code;候捷也在其文中提到“源码之前,了无秘密。”这就是大师的箴言呀。源码就像是动画、就像是幻灯片,把书中的招式一一演练给你看。可以说高手的经验大都是源自代码。源码和书一样,也是要看好的,不要看差的。在此,我推荐看Linux kernel source code 和 Linux tcp/ip source code。这两套代码都是开源的,垂手可得。此外,还可以配合着Andrew S.Tanenbaum的《操作系统的设计与实现》(Operating Systems:Design and Implementation)、毛德操 胡希明的《Linux内核 源代码情景分析》、Jonathan Corbet,Alessandro Rubini,Greg Kroah-Hartman合著的《Linux设备驱动程序》(Linux Device Driver,3e)、W.Richard Stevens《TCP/IP详解(3部)》 (TCP/IP Illustracted )、W.Richard Stevens《UNIX环境高级编程》(Advanced Programming in the UNIX Environment,新版增加了Linux kernel的内容)等书来看,方便的很当然程序不是看出来的,是写出来的。我高中的时候每天坚持写6个小时程序,《数据结构》和排列组合、图论方面的习题反复做了N遍。到现在虽然已经时隔五六年,很多内容早已淡忘掉,但却培养了我很强的编码能力和调试能力,直到现在还对我的工作有很大的帮助。 

     学习忌贪多、忌浮躁、忌急功近利、忌目中无人。把学习计划放的长一些,培养一个好的学习方法,一步一步慢慢走,终能成为高手。

===============================================================================

 

首先一个数组是存放到一个连续的地址空间的,数组名就是数组的首地址

第(i+1)个数组元素的地址可以表示为&data[ i ] 或者 (data+i)

有如下三种方法可访问数组

int a[5]={1,2,3,4,5}, i, *p;

for(i=0; i<5; i++)    printf("%4d", a[ i ]);

for(i=0; i<5; i++)    printf("%4d", *(a+i) );

for(i=0; i<5; i++)    printf("%4d", *p++);

上述三种方法虽然能输出相同结果,但效率不同

第一二两种方法都要先计算出(a+i),即找到相应的存储单元才能完成读写;而用指针变量指向数组元素时不必每次计算数组元素的地址,所以第三种方法效率高

下面还有一种计算量更少的方法

for(p=a; p<p+5; p++)      printf("%4d", *p);            //减少了i的计算

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值