目录
1 C语言基础讲解
1.1 C语言历史
C语言
是在70年代初问世的。1978年由美国电话电报公司(AT&T)贝尔实验室正式发表了C语言。早期的C语言主要是用于Unix
系统。由于C语言
的强大功能和各方面的优点逐渐为人们认识,到了八十年代,C开始进入其它操作系统,并很快在各类大、中、小和微型计算机上得到了广泛的使用,成为当代最优秀的程序设计语言之一。由于C语言与Unix的密切关系,C语言也成为Linux下的最流行的高级语言,实际上Linux系统的绝大部分代码是用C语言写的
1.2 C语言特点
C语言的强大生命力归功于C语言的优秀特点,归纳起来,C语言主要特点如下:
- C语言非常简洁,而且重视实用性,关键字少,一共只有32个。因此c语言书写的程序长度短,减少了输入的工作量。
- C语言表达能力很强,其他高级语言难于表达的运算表达式使用C语言可以很容易地实现。这是内于c语言的运算符非常丰富,共有34种运算符,包含的范围很广泛,可以用来构建类型多样化的表达式。在C语言中括号、赋值符号、强制类型转换等被视为运算符,灵活使用各种运算符和表达式可以实现非常复杂的运算功能。
- C语言易于描述复杂的数据结构。C语言的数据类型有;整型、实型、字符型、数组类型、指针类型、结构体类型、共用体类型等,可以方便的描述各种常用的数据结构,如链表、树、图等,这使得C语言在开发大型的系统和应用程序方面有很强的优势。C语言的一大特色就是可以进行灵活的指针操作,这就使C语言程序可以完成其他高级语言难于实现的功能,并保证了极高的运行效率。
- C语言非常接近于硬件,允许直接访问物理地址,并能进行位操作,能够以更容易理解的方式实现汇编语言的大部分功能。C语言具有高级语言和低级语言的双重特性,既可以作为系统的描述和开发语言,又是通用的程序设计语言。现在流行的操作系统一般由C语言编写绝大多数的代码,而非常接近硬件并对性能要求严格的部分才由汇编来完成。
- C语言编写的代码编译生成的目标代码质量非常高,程序运行效率一般只比汇编程序生成的目标代码低10%—20%。C语言有非常优秀的编译系统可以选择,程序员可以把大部分的优化工作交给编译程序完成。
- C语言具有由函数集合所构成的模块化结构。函数是C语言代码的基本构成部分。开发者可以将一个大型程序分割成若干部分或函数,并分别由不同的人员同时编写,因此,C语言也是一种具有高度结构化和模块化特性的语言。
- C语言编写的程序非常容易移植,到目前为止,几乎所有的操作系统平台上都有C语言的编译系统,而且C语言具有统一的标准,大大简化了软件在不同平台的移植工作。
但是,C语言也存在缺点,比如:
- C语言的语法检查不严格,虽然这样可以给程序员更大的灵活性,但是这样会使程序员养成不良的编程习惯,并导致程序存在隐含的错误。这个特点使得C语言对程序员的程序设计思想和技能要求较高。
- C语言的指针操作非常灵活,不正确的使用会造成程序运行的严重错误,甚至会造成系统不稳定。 随着Linux的不断发展,C语言作为Linux系统的主要编程语言应用越来越广泛
1.3 C语言程序结构
1.3.1 简单示例
在屏幕上输出"Hello,world!"
#include <stdio.h>
int main() [Tags]/*主函数*/
{
/*注释*/
printf("Hello world!\n");
return 0;
}
接下来我们讲解一下上面这段程序:
- 程序的第一行
#include <stdio.h>
是预处理器指令,告诉 C 编译器在实际编译之前要包含stdio.h
文件。它指的是一个程序把另一个指定文件的内容包含进来,扩展名为.h
的文件称为头文件
或首部文件
。书写时,可以使用引号
也可以用尖括号
,例如:#include "stdio.h"
引号和尖括号区别:- 都是在程序中把文件
stido.h
的内容(引号或尖括号是一定要的)包含进来。文件名是用双引号还是尖括号,其含义并不一样。 - 使用
尖括号
时,C编译系统首先在系统指定的目录中寻找包含文件,如果没有找到,就到当前工作目录中去寻找,这是引用系统提供的包含文件所采用的方法。 - 使用
双引号
时,C编译系统会先在前工作目录中搜索,如果未找到则去系统默认目录查找。 在程序设计中,文件包含语句是非常有用的。一般C系统中带有大量的.h
文件,用户可根据不同的需要将相应的.h
文件包含进来
- 都是在程序中把文件
- 下一行
int main()
是主函数,程序从这里开始执行
所有的C程序都是由一个或多个函数构成,其中必须只能有一个主函数main
- 下一行
/*...*/
将会被编译器忽略,这里放置程序的注释内容。它们被称为程序的注释。 - 下一行
printf(...)
是 C 中另一个可用的函数,会在屏幕上显示消息 "Hello, World!"。 - 下一行
return 0;
终止main()
函数,并返回值 0 - C语言中一个语句一般占一行,语句的最后跟着一个分号
;
,C语言规定在每条语句最后必须加一个分号;
表示该语句的结束。分号称为终结符
,单独一个分号也可构成一个语句,这就是空语句
。如果不加分号,编译程序会提示错误
1.3.2 编译&执行 C 程序
接下来让我们看看如何把源代码保存在一个文件中,以及如何编译并运行它。下面是简单的步骤:
- 打开一个文本编辑器,添加上述代码。
- 保存文件为
hello.c
- 打开命令提示符,进入到保存文件所在的目录。
- 键入
gcc hello.c
,输入回车,编译代码。 - 如果代码中没有错误,命令提示符会跳到下一行,并生成 a.out 可执行文件。
$ gcc hello.c
$ ./a.out
Hello, World!
如果是多个 c 代码的源码文件,编译方法如下:
gcc test1.c test2.c -o main.out
$ ./main.out
2 数据类型
2.1 概述
计算机的基本功能是进行数据处理,一种语言支持的数据类型越丰富,它的应用范围就越广。C语言可以提供丰富的数据类型,不仅能表达并处理基本的数据(如整形、实数、字符等),还可以组织成复杂的数据结构(如链表、树等)。
在C语言中,数据类型可分为:基本数据类型
,构造数据类型
,指针类型
三大类。
- 基本数据类型可分为
整形
、浮点型
、双精度浮点型
、字符型
- 构造类型可分为
数组类型
、结构体类型
和共用体类型
以及枚举类型
。在C语言中,每一种数据类型都有一个标识符与之相对应,称之为类型名。
C语言的运算符非常丰富,主要有以下几类:算术运算符、关系运算符、逻辑运算符、位运算符和其他一些用于完成特殊任务的运算符。C语言中运算符和表达式数量之多, 在高级语言中是少见的。正是丰富的运算符和表达式使C语言功能十分完善。这也是C语言的主要特点之一。
2.2 标识符
所谓标识符,实际上就是一个字符序列。在C语言中,标识符用来标记常量、变量、数据类型、函数及程序的名字
。在C语言中构成标识符必须将合下列语法规则:
- 以字母或下划线
_
中任一字符打头。 - 在第一个字符之后,可以是任意的字母、下划线或数字组成的字符序列,这个序列可以是空串。
C语言中的标识符可以分为下述3类:
- 关键字
关键字是用来说明C语言中某一固定含义的字。float是关键字,它用以说明变量是浮点类型。C语言中一共有如下32个关键字:
int char float double short long unsigned struct union auto extern register static typedef goto return sizeof break continue if else do while switch case default enum for void const volatile enum
这些关键字为C语言专用字,不得赋予其它含义。C语言中的习惯是用小写字母,所有这些关键字也都是由小写字母构成的。
- 特定字
特定字是具有特定含义的标识符,主要有如下7个:
define include undef ifdef ifndef endif line
它们主要用在C语言的预处理
程序中,这些标识符虽然不是关键字,但由于给这些字赋予了特定含义,所以人们习惯把它们也看作是关键字。因此在程序中不能把这些特定字当作一般标识符使用。
- 一般标识符
通常是用户根据前面所提到标识符构成规则定义的标识符,根据标识符的构成规则,下列用户定义字都是合法的标识符:
Hello, valid, VALID_identifier, Also_valid, _hello, a1, b_2a, _100
用户定义的标识符没有固定含义只有给出说明后才具有特定的含义。在数学中,我们用罗马字母表示常量或变量,在C语言中,凡是要命名的对象(如变量、常量、函数、数据类型等),都用标识符来标识其名字。
C语言中大小写字母是具有不同的含义的
,例如name和NAME就代表不同的标识符
2.3 数据类型
C 中的类型可分为以下几种:
- 基本类型:
它们是算术类型,包括两种类型:整数类型和浮点类型。 - 枚举类型:
它们也是算术类型,被用来定义在程序中只能赋予其一定的离散整数值的变量 - void 类型:
类型说明符 void 表明没有可用的值 - 派生类型:
它们包括:指针类型、数组类型、结构类型、共用体类型和函数类型
2.3.1 整数类型
下表列出了关于标准整数类型的存储大小和值范围的细节:
类型 | 存储大小 | 值范围 |
---|---|---|
char | 1 字节 | -128 到 127 或 0 到 255 |
unsigned char | 1 字节 | 0 到 255 |
signed char | 1 字节 | -128 到 127 |
int | 2 或 4 字节 | -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647 |
unsigned int | 2 或 4 字节 | 0 到 65,535 或 0 到 4,294,967,295 |
short | 2 字节 | -32,768 到 32,767 |
unsigned short | 2 字节 | 0 到 65,535 |
long | 4 字节 | -2,147,483,648 到 2,147,483,647 |
unsigned long | 4 字节 | 0 到 4,294,967,295 |
注意:
各种类型的存储大小与系统位数有关,但目前通用的以64位系统为主
unsigned
是指没有符号,只有正数和0,没有负数
2.3.2 两种浮点数据
float
:单精度浮点值。单精度是这样的格式,1位符号,8位指数,23位小数
符号位
:0是正数,1是负数指数位
:8位,可表示0~255
或-126~128
小数位
:由于取值范围是指数位决定的,所以剩余尾数23位,最多可表示2*2^32
个有效数字,以10为底的对数为log10(2*2^32)=6.92
,因此有效位为6~7
位
double
: 双精度浮点值。双精度是1位符号,11位指数,52位小数
在计算机中,常采用两种方法来表示数据,一种是定点表示法
,另一种是浮点表示法
。
对于这两种表示方法,都用相同的字长表示二进制数。浮点数表示的方法中小数点的位置是浮动
的。在这种表示方法中,一部分表示尾数(小数),一部分表示指数。尾数通常要进行归一化处理,即尾数必须大于0.1,小于1
。
例如:123.78
,表示为浮点型应是:0.12378*103
;而0.0294
,表示为浮点型就是:0.2940*10-1
2.3.3 void 类型
void 类型指定没有可用的值。它通常用于以下三种情况下:
- 函数返回为空
C 中有各种函数都不返回值,或者您可以说它们返回空。不返回值的函数的返回类型为空。例如void exit (int status);
- 函数参数为空
C 中有各种函数不接受任何参数。不带参数的函数可以接受一个 void。例如int rand(void);
- 指针指向 void
类型为void *
的指针代表对象的地址,而不是类型。例如,内存分配函数void *malloc( size_t size );
返回指向void
的指针,可以转换为任何数据类型。
2.3.4 常量
C语言把字符型常量分为两种类型:字符常量和字符串。
常量是固定值,在程序执行期间不会改变。这些固定的值,又叫做字面量。
常量可以是任何的基本数据类型,比如整数常量、浮点常量、字符常量,或字符串字面值,也有枚举常量。
2.3.4.1 整数常量
整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x
或 0X
表示十六进制,0
表示八进制,不带前缀则默认表示十进制
整数常量也可以带一个后缀,后缀是 U
和 L
的组合,U
表示无符号整数(unsigned),L
表示长整数(long
)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。
2.3.4.2 浮点常量
浮点常量由整数部分、小数点、小数部分和指数部分组成。可以使用小数形式或者指数形式来表示浮点常量。
当使用小数形式表示时,必须包含整数部分、小数部分,或同时包含两者。当使用指数形式表示时, 必须包含小数点、指数,或同时包含两者。带符号的指数是用 e
或 E
引入的。
2.3.4.3 字符常量
字符常量是括在单引号
中,例如,'x'
可以存储在 char
类型的简单变量中。
字符常量可以是一个普通的字符(例如 'x')、一个转义序列(例如 '\t'),或一个通用的字符(例如 '\u02C0')。
在 C 中,有一些特定的字符,当它们前面有反斜杠时,它们就具有特殊的含义,被用来表示如换行符(\n)或制表符(\t)等。下表列出了一些这样的转义序列码:
转义序列 | 含义 |
---|---|
\\ |
\ 字符 |
\' |
' 字符 |
\" |
" 字符 |
\? |
? 字符 |
\a |
警报铃声 |
\b |
退格键 |
\f |
换页符 |
\n |
换行符 |
\r |
回车 |
\t |
水平制表符 |
\v |
垂直制表符 |
\ooo |
一到三位的八进制数 |
\xhh . . . |
一个或多个数字的十六进制数 |
2.3.4.4 字符串常量
字符串字面值或常量是括在双引号 ""
中的。一个字符串包含类似于字符常量的字符:普通的字符、转义序列和通用的字符。
可以使用空格做分隔符,把一个很长的字符串常量进行分行
例如:"Hello world!","C program","120.45",等都是字符串。
不要将字符常量与字符串混为一谈。’a’
是字符常量,而“a”
是字符串,二者有本质的区别。C系统自动在每一个字符串的末尾加一个字符串结束标志
,系统据此判断字符串是否结束。
字符串结束标志
是一个ASCII
码值为0
的字符,即\0
。从ASCII
码表中可以看到ASCII
码为0
的字符是空操作字符.它不引起任何控制动作,也不是一个可显示的字符。如果有一个字符串CHINA
,它在内存中占6
个字节,最后一个字符为\0
。但在输出时不输出\0
,所以字符串CHINA
有效的字符个数是5
2.3.4.5 自定义定义常量
在 C 中,有两种简单的定义常量的方式:
- 使用
#define
预处理器
#define identifier value
- 使用
const
关键字
const
声明常量要在一个语句内完成
const type variable = value;
#define
是宏定义,宏定义可以实现在字面意义上和其它定义常量相同的功能,本质的区别就在于 #define
不为宏名分配内存,而 const
也不为常量分配内存,其实 const
并不是去定义一个常量,而是去改变一个变量的存储类,把该变量所占的内存变为只读
2.3.5 数组
C 语言
支持数组数据结构,它可以存储一个固定大小
的相同类型元素
的顺序集合
。数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。
数组的声明并不是声明一个个单独的变量
所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。
C 中的数组可以通过索引访问,第一个索引值为 0。
2.3.5.1 声明数组
在 C 中要声明一个数组,需要指定元素的类型和元素的数量,如下所示:
type arrayName [ arraySize ];
这叫做一维数组。arraySize
必须是一个大于零的整数常量,type
可以是任意有效的 C 数据类型。例如,要声明一个类型为 double 的包含 10 个元素的数组 balance,声明语句如下:double balance[10];
现在 balance 是一个可用的数组,可以容纳 10 个类型为 double 的数字。
2.3.5.2 初始化数组
在 C 中,您可以逐个初始化数组,也可以使用一个初始化语句,如下所示:
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
大括号 { }
之间的值的数目不能大于我们在数组声明时在方括号 [ ]
中指定的元素数目。
如果省略掉了数组的大小,数组的大小则为初始化时元素的个数。因此,如果:double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};
,将创建一个数组,它与前一个实例中所创建的数组是完全相同的。下面是一个为数组中某个元素赋值的实例:
balance[4] = 50.0;
上述的语句把数组中第五个元素的值赋为 50.0。所有的数组都是以 0 作为它们第一个元素的索引,也被称为基索引,数组的最后一个索引是数组的总大小减去 1
2.3.5.3 访问数组元素
数组元素可以通过数组名称加索引进行访问。元素的索引是放在方括号内,跟在数组名称的后边。例如:
double salary = balance[9];
上面的语句将把数组中第 10 个元素的值赋给 salary 变量。下面的实例使用了上述的三个概念,即,声明数组、数组赋值、访问数组:
2.4 数据类型转换
- 自动类型转换
C语言规定,不同类型的数据在参加运算前会自动转换成相同的类型,再进行运算。转换的规则是:如果运算的数据有float
型或double
型,自动转换成double
型再运算,结果为double
型。如果运算的数据中无float
型或double
型,但是有long
型,数据自动转换成long
型再运算,结果为long
型,其余情况为int
型。 - 强制类型转换
在C语言中也可以使用强制类型转换符,强制表达式的值转换为某一特定类型。强制类型转换形式为:(类型) 表达式
强制类型转换最主要的用途之一是满足一些运算对类型的特殊要求,例如求余运算符“%”,要求运算符两侧的数据为整型,如(int)2.5%3;其次是为了防止整数除法中的小数部分丢失,例如:
int a=3,b=2;
float c;
c=(float)a/b;
此时,c的值为1.500000。如果不用强制类型转换,即c=a/b,结果为1.000000。
需要注意的是,在强制类型转换时,原来变量的类型并未发生变化,如上例中(float) a
的值为float
型的0.0
,但是a
的类型不变,仍然为整型数据3
2.5 数据输入输出
2.5.1 getchar() & putchar() 函数
int getchar(void)
:函数从屏幕读取下一个可用的字符,并把它返回为一个整数。这个函数在同一个时间内只会读取一个单一的字符。可以在循环内使用这个方法,以便从屏幕上读取多个字符。int putchar(int c)
: 函数把字符输出到屏幕上,并返回相同的字符。这个函数在同一个时间内只会输出一个单一的字符。可以在循环内使用这个方法,以便在屏幕上输出多个字符。
#include <stdio.h>
int main( )
{
int c;
printf( "Enter a value :");
c = getchar( );
printf( "\nYou entered: ");
putchar( c );
printf( "\n");
return 0;
}
结果:
$./a.out
Enter a value :runoob
You entered: r
2.5.2 gets() & puts() 函数
char *gets(char *s)
:函数从stdin
读取一行到 s 所指向的缓冲区,直到一个终止符或EOF
。int puts(const char *s)
:函数把字符串 s 和一个尾随的换行符写入到 stdout
#include <stdio.h>
int main( )
{
char str[100];
printf( "Enter a value :");
gets( str );
printf( "\nYou entered: ");
puts( str );
return 0;
}
结果:
$./a.out
Enter a value :runoob
You entered: runoob
2.5.3 scanf() 和 printf() 函数
int scanf(const char *format, ...)
:函数从标准输入流 stdin 读取输入,并根据提供的format
来浏览输入int printf(const char *format, ...)
: 函数把输出写入到标准输出流stdout
,并根据提供的格式产生输出
format
可以是一个简单的常量字符串,但是可以分别指定 %s、%d、%c、%f
等来输出或读取字符串、整数、字符或浮点数。还有许多其他可用的格式选项,可以根据需要使用。
#include <stdio.h>
int main( ) {
char str[100];
int i;
printf( "Enter a value :");
scanf("%s %d", str, &i);
printf( "\nYou entered: %s %d ", str, i);
printf("\n");
return 0;
}
结果:
$./a.out
Enter a value :runoob 123
You entered: runoob 123
在读取字符串时,只要遇到一个空格,scanf() 就会停止读取,所以 "this is test"对 scanf() 来说是三个字符串
格式字符 | 作用 |
---|---|
d | 以十进制形式输出带符号的整数,输出长整型数据时,使用ld。 |
o | 以八进制无符号形式输出整数,输出长整型数据时,使用lo。 |
x | 以十六进制无符号形式输出整数,输出长整型数据时,使用lx。 |
u | 以十进制无符号形式输出整数,输出长整型数据时,使用lu。 |
c | 以字符形式输出单个字符。 |
f | 以十进制形式输出单、双精度浮点数(注意输出双精度double型数据时,也用格式f,而不是d)。 |
e | 以指数(科学记数法)形式输出单、双精度浮点数。 |
s | 输出指定的字符串。 |