LinuxC编程一站式学习全书学习笔记。Linux C编程一站式学习 宋劲杉 北京亚嵌教育研究中心,电子版本。作为一站式学习,坚持做笔记。从中摘录一些自己模糊的概念,同时学习作者的写技术类书籍的风格。吸收技术,逐渐成长。为LINUX学习打下基础,越是基础的东西越要静心学习。谢谢网络上提供此教程的无名人士,在此也对作者表示感谢。
定义一个变量,就是分配一块存储空间并给它命名;给一个变量赋值,就是把一个值保存到这块存储空间中。
不同类型的变量所占的存储空间大小是不同的,存储表示方式也不同,最小存储单位是字节(Byte),在C语言中char型变量占一个字节,其它类型的变量占多少字节在不同平台上有不同的规定。
字符也可以用ASCII码转义序列表示,这种转义序列由/加上1~3个八进制数字组成,或者由/x或大写/X加上1~2个十六进制数字组成,可以用在字符常量或字符串字面值中。例如'/0'表示NUL字符,'/11'或'/x9'表示Tab字符,"/11"或"/x9"表示由Tab字符组成的字符串。
printf也是一个函数,上例中的printf("sin(pi/2)=%f/nln1=%f/n", sin(pi/2), log(1.0))是带三个参数的函数调用,而函数调用也是一种表达式,因此printf语句也是表达式语句的一种。但是printf感觉不像一个数学函数,为什么呢?因为像log这种函数,我们传进去一个参数会得到一个返回值,我们调用log函数就是为了得到它的返回值,至于printf,我们并不关心它的返回值(事实上它也有返回值,表示实际打印的字符数),我们调用printf不是为了得到它的返回值,而是为了利用它所产生的副作用(Side Effect)--打印。
使用math.h中声明的库函数还有一点特殊之处,gcc命令行必须加-lm选项,因为数学函数位于libm.so库文件中(这些库文件通常位于/lib目录下),-lm选项告诉编译器,我们程序中用到的数学函数要到这个库文件里找。本书用到的大部分库函数(例如printf)位于libc.so库文件中,使用libc.so中的库函数在编译时不需要加-lc选项,当然加了也不算错,因为这个选项是gcc的默认选项。
形参相当于函数中定义的变量,调用函数传递参数的过程相当于定义形参变量并且用实参的值来初始化。
、一个函数中定义的变量不能被另一个函数使用。例如print_time中的hour和minute在main函数中没有定义,不能使用,同样main函数中的局部变量也不能被print_time函数使用。如果这样定义:
void print_time(int hour, int minute)
{
printf("%d:%d/n", hour, minute);
}
int main(void)
{
int hour = 23, minute = 59;
print_time(hour, minute);
return 0;
}
main函数中定义了局部变量hour,print_time函数中也有参数hour,虽然它们名称相同,但仍然是两个不同的变量,代表不同的存储单元。main函数的局部变量minute和print_time函数的参数minute也是如此。
每次调用函数时局部变量都表示不同的存储空间。局部变量在每次函数调用时分配存储空间,在每次函数返回时释放存储空间,例如调用print_time(23, 59)时分配hour和minute两个变量的存储空间,在里面分别存上23和59,函数返回时释放它们的存储空间,下次再调用print_time(12, 20)时又分配hour和minute的存储空间,在里面分别存上12和20。
我们知道函数体可以由很多条语句组成,现在学过的有变量定义语句和表达式语句。在函数体中通常把所有的变量定义语句放在最前面,然后才能写其它语句,本书的示例代码都遵循这种风格。C99的新特性允许变量定义语句穿插在其它语句之中,只要对于每个变量都遵循先声明后使用的原则就行。
与局部变量的概念相对的是全局变量(Global Variable),全局变量定义在所有的函数体之外,它们在程序开始运行时分配存储空间,在程序结束时释放存储空间,在任何函数中都可以访问全局变量。
则第一次调用print_time打印的是全局变量的值,第二次直接调用printf打印的则是main函数局部变量的值。在C语言中每个标识符都有特定的作用域,全局变量是定义在所有函数体之外的标识符,它的作用域从定义的位置开始直到源文件结束,而main函数局部变量的作用域仅限于main函数之中。如上图所示,设想整个源文件是一张大纸,也就是全局变量的作用域,而main函数是盖在这张大纸上的一张小纸,也就是main函数局部变量的作用域。在小纸上用到标识符hour和minute时应该参考小纸上的定义,因为大纸(全局变量的作用域)被盖住了,如果在小纸上用到某个标识符却没有找到它的定义,那么再去翻看下面的大纸上有没有定义,例如上图中的变量x。
到目前为止我们在初始化一个变量时都是用常量做Initializer,其实也可以用表达式做Initializer,但要注意一点:局部变量可以用类型相符的任意表达式来初始化,而全局变量只能用常量表达式初始化。例如,全局变量pi这样初始化是合法的:
double pi = 3.14 + 0.0016;
但这样初始化是不合法的:
double pi = acos(-1.0);
然而局部变量这样初始化却是可以的。程序开始运行时要用适当的值来初始化全局变量,所以初始值必须保存在编译生成的可执行文件中,因此初始值在编译时就要计算出来,然而上面第二种Initializer的值必须在程序运行时调用acos函数才能得到,所以不能用来初始化全局变量。请注意区分编译时和运行时这两个概念。为了简化编译器的实现,C语言从语法上规定全局变量只能用常量表达式来初始化,因此下面这种全局变量初始化是不合法的:
int minute = 360;
int hour = minute / 60;
虽然在编译时计算出hour的初始值是可能的,但是minute / 60不是常量表达式,不符合语法规定,所以编译器不必想办法去算这个初始值。
如果全局变量在定义时不初始化则初始值是0,如果局部变量在定义时不初始化则初始值是不确定的。所以,局部变量在使用之前一定要先赋值,如果基于一个不确定的值做后续计算肯定会引入Bug。
如果传入的参数是2,则从case 2分支开始执行,先是打印相应的信息,然后遇到break语句,它的作用是跳出整个switch语句块。C语言规定各case分支的常量表达式必须互不相同,如果控制表达式不等于任何一个常量表达式,则从default分支开始执行,通常把default分支写在最后,但不是必须的。使用switch语句要注意几点:
1. case后面跟表达式的必须是常量表达式,这个值和全局变量的初始值一样必须在编译时计算出来。
2. 第 2 节 "if/else语句"讲过浮点型不适合做精确比较,所以C语言规定case后面跟的常量表达式必须是整型的。
3. 进入case后如果没有遇到break语句就会一直往下执行,后面其它case或default分支的语句也会被执行到,直到遇到break,或者执行到整个switch语句块的末尾。通常每个case后面都要加上break语句,但有时会故意不加break来利用这个特性。P76