Linux C 编程一站式学习记录(三)- C语言值得注意的地方

阅读 宋劲杉 老师的 Linux C 编程一站式学习 总结 C 语言的一些特性。

声明和定义

声明:变量声明、函数声明、类型声明。
分配存储空间的声明同时也是定义,不分配存储空间的声明不是定义。
凡是被多次声明的变量或函数,必须有且只有一个声明是定义的,如果有多个定义,或者一个定义都没有,链接器就无法完成链接。
定义一个变量,就是分配一块存储空间并给它命名; 给一个变量赋值,就是把一个值保存到这块存储空间。
初始化是一种特殊的声明,而不是一种赋值语句。

表达式

任何表达式都用值和类型两个基本属性。

左值右值

有些表达式可以表示存储位置和值,有些只能表示值。表达式所表示的存储位置称为左值 (lvalue),允许放在等号左边,表达式的值称为右值 (rvalue) ,只能放在等号右边。如下 error :

(a = b) = c
minute + 1 = hour;
返回值

函数返回一个值相当预定义一个和返回值类型相同的临时变量并用 return 后面的表达式来初始化。
函数返回值不是左值,或者说函数调用表达式不能做左值。

is_even(20) = 1; /* error */

转义序列 \n 是在编译时处理,而转换说明在运行时调用 printf 函数处理的。

printf("character: %c\n", '}'); 
整数类型

整型:char、int 型

副作用 (Side Effect)

改变计算机存储单元⾥的数据或者做输⼊输出操作都算 Side Effect,包括如下几个:

  • 函数有副作用,与数学函数在概念上的根本区别。比如 printf 通常不关心它的返回值,只是利用它所产生的打印的副作用。或者在函数中修改某全局变量也是一种 Side Effect。

  • 赋值运算符,把表达式 a = b 看做函数,返回值既是 a 又是 b 的值,副作用是 a 的值被改变。

  • 前缀或后缀运算符 (++ --),把表达式 ++i 看作函数调用,传入一个参数返回值等于参数加 1,副作用是 i 的值加 1。

  • 复合赋值运算符 (*= /= %= += -= <<= >>= &= ^= |=), a += 1 相当于 a = a + 1,但有⼀点细微的差别,前者对表达式 a 只求值⼀次,⽽后者求值两次。

     a[foo()] += 1; a[foo()] = a[foo()] + 1// 如果 foo() 函数调⽤有Side Effect,⽐如会打印⼀条消息,那么前者只打印⼀次,⽽后者打印两次。
    
Sequence Point

C标准规定代码中的某些点是 Sequence Point,当执⾏到⼀个 Sequence Point 时,在此之前的 Side Effect 必须全部作⽤完毕,在此之后的 Side Effect 必须⼀个都没发⽣。⾄于两个 Sequence Point 之间的多个 Side Effect 哪个先发⽣哪个后发⽣则没有规定,编译器可以任意选择各 Side Effect 的作⽤顺序。如下例子结果 undefined,跟编译器实现相关。

int a = 0;
a = (++a)+(++a)+(++a)+(++a);
printf("%d\n", i++ * i++);

包含如下几种 Sequence Point:

  • 调⽤⼀个函数时,在所有准备⼯作做完之后、函数调⽤开始之前是Sequence Point。

    foo(f(), g());  // f() g()执行顺序未知
    
  • 条件运算符 ?:、逗号运算符、逻辑与 &&、逻辑或 || 的第⼀个操作数求值之后是 Sequence Point。

  • 在⼀个完整的声明末尾是Sequence Point,所谓完整的声明是指这个声明不是另外⼀个声明的⼀部分。

    int a[10], b[20]; // 在 a[10] 末尾是Sequence Point,在 b[20] 末尾也是
    
  • 在⼀个完整的表达式末尾是Sequence Point,所谓完整的表达式是指这个表达式不是另外⼀个表达式的⼀部分。

     f(); g(); // f()在g()之前先执行
    
  • 在库函数即将返回时是Sequence Point。
    写表达式应遵循的原则:

  1. 在两个 Sequence Point 之间,同⼀个变量的值只允许被改变⼀次 。
  2. 如果在两个 Sequence Point 之间既要读⼀个变量的值⼜要改它的值,只有在读写顺序确定的情况下才可以这么写。
a = (++a)+(++a)+(++a)+(++a); // error 
a[i++] = i; // error
 i = i + 1; // 读写顺序确定,ok
void 设计原因

从语法上规定没有返回值的函数调用表达式是 void 类型,有一个 void 类型的值,这样任何表达式都有值,不必考虑特殊情况,编译器的语法解析比较容易; 然后从语义上规定 void 类型的表达式不能参与运算,从而兼顾类语法上的一致和语义上的不矛盾。

运算符优先级

后缀运算符包括后缀++、后缀–、结构体取成员.、 数组取下标[],单目(或前缀)运算符包括前缀++、前缀–、正号+ 、负号-、逻辑非!。后缀运算符优先级最高,单目运算符仅次于后缀,比其它运算符都高。

++count[2] 是对 count[2] 做前缀 ++ 运算。

数组

数组类型做右值使用时,自动转换成指向数组首元素的指针。下面的写法是错误的。

int a[5] = { 4, 3, 2, 1 };
int b[5] = a; /* error; incompatible types in assignment */

在函数原型中,如果参数写成数组的形式,则该参数实际上是指针类型。

define 和枚举

define 不仅可以定义常量,也可以定义更复杂的语法结构,宏定义,define 定义在预处理阶段处理,枚举在编译阶段处理。
结构体的成员名和变量名不在同一命名空间,枚举的成员名和变量名在同一命名空间。

#include <stdio.h>
enum coordinate_type { RECTANGULAR = 1, POLAR };

int main(void)
{
    int RECTANGULAR;
    printf("%d %d\n", RECTANGULAR, POLAR);  /* 2 0 */
    return 0;
}
Old Style C 风格

并非所有函数声明包含完整的函数原型,如

void threelines();  /* 没有指出参数类型和个数 */
void main() {}
sizeof

sizeof 是⼀个特殊的运算符,有两种形式:sizeof 表达式sizeof(类型名)

sizeof 表达式 中的⼦表达式并不求值,⽽只是根据类型转换规则求得⼦表达式的类型,然后把这种类型所占的字节数作为整个表达式的值。 sizeof(表达式) 形式⾥的括号和 return(1); 的括号⼀样,不起任何作⽤。

sizeof(类型名) 的括号是必须写的,整个表达式的值也是这种类型所占的字节数。

int a[12];
printf("%d\n", sizeof a/sizeof a[0]);
网上很多地方可以找到本书的下载,我唯一能够额外贡献的是,提供多格式的打包下载,包括:PDF格式,离线网页格式,在线网页格式。 如果您觉得下载比较麻烦,那么建议您直接使用在线教程,网址是:http://learn.akae.cn/media/index.html 如果您只需要一个可以打印的PDF格式文件,那么建议你下载Defonds的资源,因为我的PDF格式文件就是从他那下的,资源链接是:http://download.csdn.net/detail/defonds/2855361 如果您和我一样,希望在多种环境下都可以方便地使用本书,那么欢迎您下载我提供的这个打包资源! 对本书的描述 版权 © 2008, 2009 宋劲杉, 北京亚嵌教育研究中心 这本书最初是为北京亚嵌教育研究中心的嵌入式Linux系统工程师就业班课程量身定做的教材之一,作者宋劲松。 这是一本从零基础开始学习编程的书,不要求读者有任何编程经验,但读者应勤于思考。本书尽最大努力理清概念之间的依赖关系,力求一站式学习,读者不需要为了找一个概念的定义去翻其它书,也不需要为了搞清楚一个概念在本书中前后一通乱翻,只需从前到后按顺序学习即可。但一站式学习并不等于傻瓜式学习,有些章节有一定的难度,需要积极思考才能领会。本书可以替你节省时间,但不能替你思考,不要指望像看小说一样走马观花看一遍就能学会。 本书不是孤立地讲C语言,而是和编译原理、操作系统、计算机体系结构结合起来讲。或者说,本书的内容只是以C语言为载体,真正讲的是计算机的原理和程序的原理。 强调基本概念和基本原理,在编排顺序上非常重视概念之间的依赖关系,每次引入一个新的概念,只依赖于前面章节已经讲过的概念,而绝不会依赖后面章节要讲的概念。有些地方为了叙述得完整,也会引用后面要讲的内容,比如说“有关XX我们到XX章再仔细讲解”,凡是这种引用都不是必要的依赖,可以当它不存在,只管继续往下看就行了。 这本书定位在入门级,虽然内容很多,但不是一本百科全书,除了C语言基本要讲透之外其它内容都不深入,书中列出了很多参考资料,是读者进一步学习的起点。K&R的第一章是一个Whirlwind Tour,把全书的内容简单过了一遍,然后再逐个深入进去讲解。本书也可以看作是计算机专业课程体系的一个Whirlwind Tour,学习完本书之后有了一个全局观,再去学习那些参考资料就应该很容易上手了。 本书的主要内容包括大部分: C语言入门。介绍基本的C语法,帮助没有任何编程经验的读者理解什么是程序,怎么写程序,培养程序员的思维习惯,找到编程的感觉。前半部分改编自《How To Think Like A Computer Scientist: Learning with C++》(Allen B. Downey.) C语言本质。结合计算机和操作系统的原理讲解C程序是怎么编译、链接、运行的,同时全面介绍C的语法。位运算的章节改编自亚嵌教育林小竹老师的讲义,链表和二叉树的章节改编自亚嵌教育朱老师的讲义。汇编语言的章节改编自《Programming from the Ground Up: An Introduction to Programming using Linux Assembly Language》(Jonathan Bartlett.)在该书的最后一章提到,学习编程有两种Approach,一种是Bottom Up,一种是Top Down,各有优缺点,需要两者结合起来。所以作者编这本书的思路是,第一部分Top Down,第二部分Bottom Up,第部分可以算填了中间的空隙,部分全都围绕C语言展开。 Linux系统编程。介绍各种Linux系统函数和内核的工作原理。Socket编程的章节改编自亚嵌教育卫剑钒老师的讲义。 为什么要在Linux平台上学C语言?用Windows学C语言不好吗? 用Windows还真的是学不好C语言C语言是一种面向底层的编程语言,要写好C程序,必须对操作系统的工作原理非常清楚,因为操作系统也是用C写的,我们用C写应用程序直接使用操作系统提供的接口。既然你选择了看这本书,你一定了解:Linux是一种开源的操作系统,你有任何疑问都可以从源代码和文档中找到答案,即使你看不懂源代码,也找不到文档,也很容易找个高手教你,各种邮件列表、新闻组和论坛上从来都不缺乐于助人的高手;而Windows是一种封闭的操作系统,除了微软的员工别人都看不到它的源代码,只能通过文档去猜测它的工作原理,更糟糕的是,微软向来喜欢藏着揶着,好用的功能留着自己用,而不会写到文档里公开。本书的第一部分在Linux或Windows平台上学习都可以,但第二部分和第部分介绍了很多Linux操作系统的原理以帮助读者更深入地理解C语言,只能在Linux平台上学习。 Windows平台上的开发工具往往和各种集成开发环境(IDE,Integrated Development Environment)绑在一起,例如Visual Studio、Eclipse等。使用IDE确实很便捷,但IDE对于初学者绝对不是好东西。微软喜欢宣扬傻瓜式编程的理念,告诉你用鼠标拖几个控件,然后点一个按钮就可以编译出程序来,但是真正有用的程序有哪个是这么拖出来的?很多从Windows平台入门学编程的人,编了好几年程序,还是只知道编完程序点一个按钮就可以跑了,把几个源文件拖到一个项目里就可以编译到一起了,如果有更复杂的需求他们就傻眼了,因为他们脑子里只有按钮、菜单的概念,根本没有编译器、链接器、Makefile的概念,甚至连命令行都没用过,然而这些都是初学编程就应该建立起来的基本概念。另一方面,编译器、链接器和C语言的语法有密切的关系,不了解编译器、链接器的工作原理,也不可能真正掌握C的语法。所以,IDE并没有帮助你学习,而是阻碍了你学习,本来要学好C编程只要把语法和编译命令学会就行了,现在有了IDE,除了学会语法和编译命令,你还得弄清楚编译命令和IDE是怎么集成的,这才算学明白了,本来就很复杂的学习任务被IDE搞得更加复杂了。Linux用户的使用习惯从来都是以敲命令为主,以鼠标操作为辅,从学编程的第一天起就要敲命令编译程序,等到你把这些基本概念都搞清楚了,你觉得哪个IDE好用你再去用,不过到那时候你可能会更喜欢vi或emacs而不是IDE了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值