内存条是一个非常精密的部件,包含了上亿个电子元器件,它们很小,达到了纳米级别。这些元器件,有两种状态,通电和断电,通电是高电平(5V),用1来表示,断电是低电平(0V),用0来表示。所以,一个元器件有2种状态。 如果我们通过电路来控制这些元器件的通断电,那么就可以得到很多0、1状态的组合。例如,8个元器件有 28=256 种不同的组合,16个元器件有 216=65536 种不同的状态。虽然一个元器件只能表示2种状态,但是多个组合起来就可以表示很多状态了,所以,我们通常将8个、16个、24或32个元器件组合起来表示一个字符,8个元器件可以表示256个字符,16个可以表示65536个字符。 前面我们已经讲到,计算机只认识0和1两个数字,多个0和1的组合可以表示很多字符,字符在存储到内存之前也都被转换成了二进制(0和1序列) 可以将不同的编程语言比喻成各国语言(如汉语、英语、法语等),为了表达同一个意思,可能使用不同的语句。例如,表达“世界你好”的意思: 编译器 决定种类和语法 实际最底层调用都是一致的 可以说,所有的编程语言都是一个原理,使用人类能读懂的语言来编写源代码(source code),再利用编译器将源代码编译成机器能读懂的语言(0和1序列),称为目标代码(object language)。 编程语言是这样一套规范,它规定了编译器按照什么样的语法将源代码编译成目标代码。 开发一种编程语言,重点是它的语法和编译器,有了他们,就有了编程语言。
#include<stdio.h> // 包含stdio.h头文件 int main(){ int i, total=0; // 声明两个整型变量 /* for语句用来循环 i的起始值为 1,每循环一次加 1,i>100 时退出循环 */ for(i=1; i<=100; i++){ total+=i; // 加法运算,也可以写作 total=total+i } printf("1+2+3+...+99+100=%d\n", total); // 将total以整数形式输出 return 0; }
#include<stdio.h> // 包含stdio.h头文件 int max(int, int); // 函数声明 int main(){ int a, b; // 声明两个整型变量 printf("Input two integers: "); // 以空格为分隔 scanf("%d %d", &a, &b); // 将输入的两个整数分别赋值给a, b // 以整数形式输出最大值, a, b为实际参数(实参) printf("The max integer is %d.\n", max(a, b)); return 0; } // 函数定义 int max(int num1, int num2){ // num1, num2为形式参数(形参) if(num1>num2){ // 如果num1大于num2 return num1; // 返回num1并结束max函数 }else{ // 如果num2大于num1 return num2; // 返回num2并结束max函数
GCC
Linux下使用最广泛的C/C++编译器是GCC,大多数的Linux发行版本都默认安装,不管是开发人员还是初学者,一般都将GCC作为Linux下首选的编译工具。本教程毫不犹豫地使用GCC来编译C程序。
保存文件后退出,打开终端并 cd 到当前目录,输入下面的命令:
gcc test.c -o test
可以直接将C代码编译链接为可执行文件。
可以看到在当前目录下多出一个文件 test,这就是可执行文件。不像Windows,Linux不以文件后缀来区分可执行文件,Linux下的可执行文件后缀理论上是可以任意更改的。
当然,也可以分步编译:
1) 预处理
gcc -E test.c -o test.i
在当前目录下会多出一个预处理结果文件 test.i,打开 test.i 可以看到,在 test.c 的基础上把stdio.h和stdlib.h的内容插进去了。
2) 编译为汇编代码
gcc -S test.i -o test.s
其中-S参数是在编译完成后退出,-o为指定文件名。
3) 汇编为目标文件
gcc -c test.s -o test.o
.o就是目标文件。目标文件与可执行文件类似,都是机器能够识别的可执行代码,但是由于还没有链接,结构会稍有不同。
3) 链接并生成可执行文件
gcc test.o -o test
如果有多个源文件,可以这样来编译:
gcc -c test1.c -o test1.o
gcc -c test2.c -o test2.o
gcc test1.o test2.o -o test
注意:如果不指定文件名,GCC会生成名为a.out的文件,.out文件只是为了区分编译后的文件,Linux下并没有标准的可执行文件后缀名,一般可执行文件都没有后缀名。
编译后生成的test文件就是程序了,运行它:
./test
如果没有运行权限,可以使用sudo命令来增加权限(注意要在Linux的分区下):
sudo cdmod test 777
对于程序的检错,我们可以用-pedantic、-Wall、-Werror选项:
- -pedantic选项能够帮助程序员发现一些不符合 ANSI/ISO C标准的代码(并不是全部);
- -Wall可以让gcc显示警告信息;
- -Werror可以让gcc在编译中遇到错误时停止继续。
auto | 声明自动变量 |
short | 声明短整型变量或函数 |
int | 声明整型变量或函数 |
long | 声明长整型变量或函数 |
float | 声明浮点型变量或函数 |
double | 声明双精度变量或函数 |
char | 声明字符型变量或函数 |
struct | 声明结构体变量或函数 |
union | 声明共用数据类型 |
enum | 声明枚举类型 |
typedef | 用以给数据类型取别名 |
const | 声明只读变量 |
unsigned | 声明无符号类型变量或函数 |
signed | 声明有符号类型变量或函数 |
extern | 声明变量是在其他文件正声明 |
register | 声明寄存器变量 |
static | 声明静态变量 |
volatile | 说明变量在程序执行中可被隐含地改变 |
void | 声明函数无返回值或无参数,声明无类型指针 |
if | 条件语句 |
else | 条件语句否定分支(与 if 连用) |
switch | 用于开关语句 |
case | 开关语句分支 |
for | 一种循环语句 |
do | 循环语句的循环体 |
while | 循环语句的循环条件 |
goto | 无条件跳转语句 |
continue | 结束当前循环,开始下一轮循环 |
break | 跳出当前循环 |
default | 开关语句中的“其他”分支 |
sizeof | 计算数据类型长度 |
return | 子程序返回语句(可以带参数,也可不带参数)循环条件 |
6. 常量
常量是值不能被改变的量。C语言中使用的常量可分为数字常量、字符常量、字符串常量、符号常量、转义字符等多种。在后面章节中将专门给予介绍。7.变量
变量由用户自己命名,用来保存特定类型的数据,数据可以被改变。数据类型有整数、浮点数、字符等,将在后面章节进行介绍。8. 注释
标准C语言的注释是以“/*”开头并以“*/”结尾的串。在“/*”和“*/”之间的即为注释。程序编译时,不对注释作任何处理。注释可出现在程序中的任何位置。注释用来向用户提示或解释程序的意义。在调试程序中对暂不使用的语句也可用注释符括起来,使翻译跳过不作处理,待调试结束后再去掉注释符。另外,各种编译器都支持以“//”开头的注释,虽然它不是标准C的规定,但是早已成为事实标准,在程序中广泛使用。“//”仅支持单行注释,也就是说,注释中不能出现换行符;而“/* */”支持多行注释,注释中可以出现换行符。
printf("Input two integers: "); // 以空格为分隔 // 以'|'为分隔符,将输入的两个整数分别赋值给a, b scanf("%d|%d", &a, &b);运行结果:
Input two integers: 12|34↙
通过前面的几个例子,我们总结出了C语言程序的几个特点:
- 一个C语言源程序可以由一个或多个源文件组成。
- 每个源文件可由一个或多个函数组成。
- 一个源程序不论由多少个文件组成,都有一个且只能有一个main函数,即主函数。
- 源程序中可以有预处理命令(include 命令仅为其中的一种),预处理命令通常应放在源文件或源程序的最前面。
- 每一个说明,每一个语句都必须以分号结尾。但预处理命令,函数头和花括号“}”之后不能加分号。
- 标识符,关键字之间必须至少加一个空格以示间隔。若已有明显的间隔符,也可不再加空格来间隔。
从书写清晰,便于阅读,理解,维护的角度出发,在书写程序时应遵循以下规则:所谓数据类型是按被定义变量的性质,表示形式,占据存储空间的多少
- 一个说明或一个语句占一行。
- 用{} 括起来的部分,通常表示了程序的某一层次结构。{}一般与该结构语句的第一个字母对齐,并单独占一行。
- 低一层次的语句或说明可比高一层次的语句或说明缩进若干格后书写。以便看起来更加清晰,增加程序的可读性
数据类型说明:
数据类型 | 说明 |
---|---|
基本数据类型 | 基本数据类型最主要的特点是,其值不可以再分解为其它类型。也就是说,基本数据类型是自我说明的。 |
构造数据类型 | 构造数据类型是根据已定义的一个或多个数据类型用构造的方法来定义的。也就是说,一个构造类型的值可以分解成若干个“成员”或“元素”。每个“成员”都是一个基本数据类型或又是一个构造类型。在C语言中,构造类型有以下几种:数组类型、结构体类型、共用体(联合)类型。 |
指针类型 | 指针是一种特殊的,同时又是具有重要作用的数据类型。其值用来表示某个变量在内存储器中的地址。虽然指针变量的取值类似于整型量,但这是两个类型完全不同的量,因此不能混为一谈。 |
空类型 | 在调用函数值时,通常应向调用者返回一个函数值。这个返回的函数值是具有一定的数据类型的,应在函数定义及函数说明中给以说明,例如在例题中给出的max函数定义中,函数头为:
int max(int a,int b);
其中“int ”类型说明符即表示该函数的返回值为整型量。又如在例题中,使用了库函数sin,由于系统规定其函数返回值为双精度浮点型,因此在赋值语句s=sin (x);中,s也必须是双精度浮点型,以便与sin函数的返回值一致。所以在说明部分,把s说明为双精度浮点型。但是,也有一类函数,调用后并不需要向调用者返回函数值,这种函数可以定义为“空类型”。其类型说明符为void。在后面函数中还要详细介绍。 |
#define 标识符 常量
其中#define也是一条预处理命令(预处理命令都以"#"开头),称为宏定义命令(在后面预处理程序中将进一步介绍),其功能是把该标识符定义为其后的常量值。一经定义,以后在程序中所有出现该标识符的地方均代之以该常量值。
习惯上符号常量的标识符用大写字母,变量标识符用小写字母,以示区别。
http://c.biancheng.net/cpp/html/20.html
http://c.biancheng.net/cpp/html/21.html
http://c.biancheng.net/cpp/html/22.html
http://c.biancheng.net/cpp/html/26.html
http://c.biancheng.net/cpp/html/28.html
在C语言中逗号“,”也是一种运算符,称为逗号运算符。 其功能是把两个表达式连接起来组成一个表达式, 称为逗号表达式。其一般形式为:
表达式1, 表达式2
int a=2,b=4,c=6,x,y; y=(x=a+b),(b+c);条件运算符为?和:,它是一个三目运算符,即有三个参与运算的量。由条件运算符组成条件表达式的一般形式为:
表达式1 ? 表达式2 : 表达式3
其求值规则为:如果表达式1的值为真,则以表达式2 的值作为条件表达式的值,否则以表达式2的值作为整个条件表达式的值。条件表达式通常用于赋值语句之中。
字符串和字符串结束标志
在C语言中没有专门的字符串变量,通常用一个字符数组来存放一个字符串。前面介绍字符串常量时,已说明字符串总是以'\0'作为串的结束符。因此当把一个字符串存入一个数组时,也把结束符'\0'存入数组,并以此作为该字符串是否结束的标志。有了'\0'标志后,就不必再用字符数组的长度来判断字符串的长度了。
C语言允许用字符串的方式对数组作初始化赋值。例如:
char c[]={'c', ' ','p','r','o','g','r','a','m'};
可写为:
char c[]={"C program"};
或去掉{}写为:
char c[]="C program";
用字符串方式赋值比用字符逐个赋值要多占一个字节, 用于存放字符串结束标志'\0'。上面的数组c在内存中的实际存放情况为:
#include <stdio.h> int main(void){ char c[]="BASIC\ndBASE"; printf("%s\n",c); return 0; }例7-10】使用 scanf() 从控制台输入一个字符串,然后使用 printf() 将其输出
#include <stdio.h> int main(void){ char st[15]; printf("input string: "); scanf("%s",st); printf("your string is: %s\n",st); return 0; }C语言提供了丰富的字符串处理函数,大致可分为字符串的输入、输出、合并、修改、比较、转换、复制、搜索几类。 使用这些函数可大大减轻编程的负担。用于输入输出的字符串函数,在使用前应包含头文件"stdio.h",使用其它字符串函数则应包含头文件"string.h"。
下面介绍几个最常用的字符串函数。字符串输出函数 puts
格式: puts(字符数组名)
功能:把字符数组中的字符串输出到显示器。 即在屏幕上显示该字符串。
【例7-12】从程序中可以看出puts函数中可以使用转义字符,因此输出结果成为两行。puts函数完全可以由printf函数取代。当需要按一定格式输出时,通常使用printf函数。
- #include"stdio.h"
- main(){
- char c[]="BASIC\ndBASE";
- puts(c);
- }
字符串输入函数 gets
格式: gets (字符数组名)
功能:从标准输入设备键盘上输入一个字符串。
本函数得到一个函数值,即为该字符数组的首地址。
【例7-13】可以看出当输入的字符串中含有空格时,输出仍为全部字符串。说明gets函数并不以空格作为字符串输入结束的标志,而只以回车作为输入结束。这是与scanf函数不同的。
- #include"stdio.h"
- main(){
- char st[15];
- printf("input string:\n");
- gets(st);
- puts(st);
- }
字符串连接函数 strcat
格式: strcat(字符数组名1,字符数组名2)
功能:把字符数组2中的字符串连接到字符数组1 中字符串的后面,并删去字符串1后的串标志“\0”。本函数返回值是字符数组1的首地址。
【例7-14】本程序把初始化赋值的字符数组与动态赋值的字符串连接起来。要注意的是,字符数组1应定义足够的长度,否则不能全部装入被连接的字符串。
- #include"string.h"
- main(){
- static char st1[30]="My name is ";
- int st2[10];
- printf("input your name:\n");
- gets(st2);
- strcat(st1,st2);
- puts(st1);
- }
字符串拷贝函数strcpy
格式: strcpy(字符数组名1,字符数组名2)
功能:把字符数组2中的字符串拷贝到字符数组1中。串结束标志“\0”也一同拷贝。字符数名2,也可以是一个字符串常量。这时相当于把一个字符串赋予一个字符数组。
【例7-15】本函数要求字符数组1应有足够的长度,否则不能全部装入所拷贝的字符串。
- #include"string.h"
- main(){
- char st1[15],st2[]="C Language";
- strcpy(st1,st2);
- puts(st1);printf("\n");
- }
字符串比较函数strcmp
格式: strcmp(字符数组名1,字符数组名2)
功能:按照ASCII码顺序比较两个数组中的字符串,并由函数返回值返回比较结果。
字符串1=字符串2,返回值=0;
字符串2〉字符串2,返回值〉0;
字符串1〈字符串2,返回值〈0。
本函数也可用于比较两个字符串常量,或比较数组和字符串常量。
【例7-16】本程序中把输入的字符串和数组st2中的串比较,比较结果返回到k中,根据k值再输出结果提示串。当输入为dbase时,由ASCII 码可知“dBASE”大于“C Language”故k〉0,输出结果“st1>st2”。
- #include"string.h"
- main(){
- int k;
- static char st1[15],st2[]="C Language";
- printf("input a string:\n");
- gets(st1);
- k=strcmp(st1,st2);
- if(k==0) printf("st1=st2\n");
- if(k>0) printf("st1>st2\n");
- if(k<0) printf("st1<st2\n");
- }
测字符串长度函数strlen
格式: strlen(字符数组名)
功能:测字符串的实际长度(不含字符串结束标志‘\0’)并作为函数返回值。
【例7-17】
- #include"string.h"
- main(){
- int k;
- static char st[]="C language";
- k=strlen(st);
- printf("The lenth of the string is %d\n",k);
- }