Union 结构
有时需要一种数据结构,不同的场合表示不同的数据类型。比如,如果只用一种数据结构表示水果的“量”,这种结构就需要有时是整数(6个苹果),有时是浮点数(1.5公斤草莓)。
C 语言提供了 Union
结构,用来自定义可以灵活变更的数据结构。它内部可以包含各种属性,但同一时间只能有一个属性,因为所有属性都保存在同一个内存地址,后面写入的属性会覆盖前面的属性。这样做的最大好处是节省空间。
12345union quantity { short count; float weight; float volume;};
上面示例中,union命令定义了一个包含三个属性的数据类型quantity。虽然包含三个属性,但是同一时间只能取到一个属性。最后赋值的属性,就是可以取到值的那个属性。
使用时,声明一个该类型的变量。
123456789// 写法一union quantity q;q.count = 4;// 写法二union quantity q = {.count=4};// 写法三union quantity q = { ...
typedef 命令
简介
typedef命令用来为某个类型起别名。
1typedef type name;
上面代码中,type代表类型名,name代表别名。
123typedef unsigned char BYTE;BYTE c = 'z';
上面示例中,typedef命令为类型unsign char起别名BYTE,然后就可以使用BYTE声明变量。
typedef 可以一次指定多个别名。
1typedef int antelope, bagel, mushroom;
上面示例中,一次性为int类型起了三个别名。
typedef 可以为指针起别名。
1234typedef int* intptr;int a = 10;intptr x = &a;
上面示例中,intptr是int*的别名。不过,使用的时候要小心,这样不容易看出来,变量x是一个指针类型。
typedef 也可以用来为数组类型起别名。
123typedef int five_ints[5];five_ints x = {11, 22, 33, 44, 55};
...
struct 结构
简介
C
语言内置的数据类型,除了最基本的几种原始类型,只有数组属于复合类型,可以同时包含多个值,但是只能包含相同类型的数据,实际使用中并不够用。
实际使用中,主要有下面两种情况,需要更灵活强大的复合类型。
复杂的物体需要使用多个变量描述,这些变量都是相关的,最好有某种机制将它们联系起来。
某些函数需要传入多个参数,如果一个个按照顺序传入,非常麻烦,最好能组合成一个复合结构传入。
为了解决这些问题,C
语言提供了struct关键字,允许自定义复合数据类型,将不同类型的值组合在一起。这样不仅为编程提供方便,也有利于增强代码的可读性。C
语言没有其他语言的对象(object)和类(class)的概念,struct
结构很大程度上提供了对象和类的功能。
下面是struct自定义数据类型的一个例子。
1234struct fraction { int numerator; int denominator;};
上面示例定义了一个分数的数据类型struct fraction,包含两个属性numerator和denominator。
注意,作为一个 ...
C 语言的内存管理
简介
C
语言的内存管理,分成两部分。一部分是系统管理的,另一部分是用户手动管理的。
系统管理的内存,主要是函数内部的变量(局部变量)。这部分变量在函数运行时进入内存,函数运行结束后自动从内存卸载。这些变量存放的区域称为”栈“(stack),”栈“所在的内存是系统自动管理的。
用户手动管理的内存,主要是程序运行的整个过程中都存在的变量(全局变量),这些变量需要用户手动从内存释放。如果使用后忘记释放,它就一直占用内存,直到程序退出,这种情况称为”内存泄漏“(memory
leak)。这些变量所在的内存称为”堆“(heap),”堆“所在的内存是用户手动管理的。
void 指针
前面章节已经说过了,每一块内存都有地址,通过指针变量可以获取指定地址的内存块。指针变量必须有类型,否则编译器无法知道,如何解读内存块保存的二进制数据。但是,向系统请求内存的时候,有时不确定会有什么样的数据写入内存,需要先获得内存块,稍后再确定写入的数据类型。
为了满足这种需求,C 语言提供了一种不定类型的指针,叫做 void
指针。它只有内存块的地址信息,没有类型信息,等到使用该块内存的时候,再向 ...
字符串
简介
C
语言没有单独的字符串类型,字符串被当作字符数组,即char类型的数组。比如,字符串“Hello”是当作数组{'H', 'e', 'l', 'l', 'o'}处理的。
编译器会给数组分配一段连续内存,所有字符储存在相邻的内存单元之中。在字符串结尾,C
语言会自动添加一个全是二进制0的字节,写作\0字符,表示字符串结束。字符\0不同于字符0,前者的
ASCII 码是0(二进制形式00000000),后者的 ASCII
码是48(二进制形式00110000)。所以,字符串“Hello”实际储存的数组是{'H', 'e', 'l', 'l', 'o', '\0'}。
所有字符串的最后一个字符,都是\0。这样做的好处是,C
语言不需要知道字符串的长度,就可以读取内存里面的字符串,只要发现有一个字符是\0,那么就知道字符串结束了。
1char localString[10];
上面示例声明了一个10个成员的字符数组,可以当作字符串。由于必须留一个位置给\0,所以最多只能容纳9个字符的字符串。
字符串写成数组的形式,是非常麻烦的。C
语言提 ...
数组
简介
数组是一组相同类型的值,按照顺序储存在一起。数组通过变量名后加方括号表示,方括号里面是数组的成员数量。
1int scores[100];
上面示例声明了一个数组scores,里面包含100个成员,每个成员都是int类型。
注意,声明数组时,必须给出数组的大小。
数组的成员从0开始编号,所以数组scores[100]就是从第0号成员一直到第99号成员,最后一个成员的编号会比数组长度小1。
数组名后面使用方括号指定编号,就可以引用该成员。也可以通过该方式,对该位置进行赋值。
12scores[0] = 13;scores[99] = 42;
上面示例对数组scores的第一个位置和最后一个位置,进行了赋值。
注意,如果引用不存在的数组成员(即越界访问数组),并不会报错,所以必须非常小心。
123int scores[100];scores[100] = 51;
上面示例中,数组scores只有100个成员,因此scores[100]这个位置是不存在的。但是,引用这个位置并不会报错,会正常运行,使得紧跟在scores后面的那块内存区域被赋值,而那实际上是其他变量的区域,因 ...
函数
简介
函数是一段可以重复执行的代码。它可以接受不同的参数,完成对应的操作。下面的例子就是一个函数。
123int plus_one(int n) { return n + 1;}
上面的代码声明了一个函数plus_one()。
函数声明的语法有以下几点,需要注意。
(1)返回值类型。函数声明时,首先需要给出返回值的类型,上例是int,表示函数plus_one()返回一个整数。
(2)参数。函数名后面的圆括号里面,需要声明参数的类型和参数名,plus_one(int n)表示这个函数有一个整数参数n。
(3)函数体。函数体要写在大括号里面,后面(即大括号外面)不需要加分号。大括号的起始位置,可以跟函数名在同一行,也可以另起一行,本书采用同一行的写法。
(4)return语句。return语句给出函数的返回值,程序运行到这一行,就会跳出函数体,结束函数的调用。如果函数没有返回值,可以省略return语句,或者写成return;。
调用函数时,只要在函数名后面加上圆括号就可以了,实际的参数放在圆括号里面,就像下面这样。
12int a = plus_one(13 ...
指针
指针是 C 语言最重要的概念之一,也是最难理解的概念之一。
简介
指针是什么?首先,它是一个值,这个值代表一个内存地址,因此指针相当于指向某个内存地址的路标。
字符*表示指针,通常跟在类型关键字的后面,表示指针指向的是什么类型的值。比如,char*表示一个指向字符的指针,float*表示一个指向float类型的值的指针。
1int* intPtr;
上面示例声明了一个变量intPtr,它是一个指针,指向的内存地址存放的是一个整数。
星号*可以放在变量名与类型关键字之间的任何地方,下面的写法都是有效的。
123int *intPtr;int * intPtr;int* intPtr;
本书使用星号紧跟在类型关键字后面的写法(即int* intPtr;),因为这样可以体现,指针变量就是一个普通变量,只不过它的值是内存地址而已。
这种写法有一个地方需要注意,如果同一行声明两个指针变量,那么需要写成下面这样。
12345// 正确int * foo, * bar;// 错误int* foo, bar;
上面示例中,第二行的执行结果是,foo是整数指针变量,而bar是整数变量, ...
数据类型
C
语言的每一种数据,都是有类型(type)的,编译器必须知道数据的类型,才能操作数据。所谓“类型”,就是相似的数据所拥有的共同特征,那么一旦知道某个值的数据类型,就能知道该值的特征和操作方式。
基本数据类型有三种:字符(char)、整数(int)和浮点数(float)。复杂的类型都是基于它们构建的。
字符类型
字符类型指的是单个字符,类型声明使用char关键字。
1char c = 'B';
上面示例声明了变量c是字符类型,并将其赋值为字母B。
C 语言规定,字符常量必须放在单引号里面。
在计算机内部,字符类型使用一个字节(8位)存储。C
语言将其当作整数处理,所以字符类型就是宽度为一个字节的整数。每个字符对应一个整数(由
ASCII 码确定),比如B对应整数66。
字符类型在不同计算机的默认范围是不一样的。一些系统默认为-128到127,另一些系统默认为0到255。这两种范围正好都能覆盖0到127的
ASCII 字符范围。
只要在字符类型的范围之内,整数与字符是可以互换的,都可以赋值给字符类型的变量。
123char c = 66;// 等同于cha ...
流程控制
C
语言的程序是顺序执行,即先执行前面的语句,再执行后面的语句。开发者如果想要控制程序执行的流程,就必须使用流程控制的语法结构,主要是条件执行和循环执行。
if 语句
if语句用于条件判断,满足条件时,就执行指定的语句。
1if (expression) statement
上面式子中,表达式expression为真(值不为0)时,就执行statement语句。
if后面的判断条件expression外面必须有圆括号,否则会报错。语句体部分statement可以是一个语句,也可以是放在大括号里面的复合语句。下面是一个例子。
1if (x == 10) printf("x is 10");
上面示例中,当变量x为10时,就会输出一行文字。对于只有一个语句的语句体,语句部分通常另起一行。
12if (x == 10) printf("x is 10\n");
如果有多条语句,就需要把它们放在大括号里面,组成一个复合语句。
1234if (line_num == MAX_LINES) { line_num = 0; pa ...
