c语言基础语法
一,概要
二,数据类型、运算符与表达式
1. C的数据类型
C的数据类型包括:整型、字符型、实型或浮点型(单精度和双精度)、枚举类型、数组类型、结构体类型、共用体类型、指针类型和空类型。
2. 常量与变量
常量其值不可改变,符号常量名通常用大写。变量其值可以改变,变量名只能由字母、数字和下划线组成,且第一个字符必须为字母或下划线。否则为不合法的变量名。变量在编译时为其分配相应存储单元。
3. 整型数据
整型常量的表示方法:十进制不用说了,八进制以0开头,如0123,十六进制以0x开头,如0x1e。
整型变量分为:基本型(int)、短整型(short int)、长整型(long int)和无符号型。不同机器上各类数据所占
内存字节数不同,一般int型为2个字节,long型为4个字节。
4. 实型数据
实型常量表示形式:十进制形式由数字和小数点组成(必须有小数点),如:0.12、.123、123
0.0等。指数形式如123e3代表123×10的三次方。
实型变量分为单精度(float)和双精度(double)两类。在一般系统中float型占4字节,7位有效数字,double型占
8字节,15~16位有效数字。
5. 字符型数据
第 1 页新建 文本文档
字符变量用单引号括起来,如'a','b'等。还有一些是特殊的字符常量,如'\n','\t'等。分别代表换行和横向跳格。
字符变量以char 来定义,一个变量只能存放一个字符常量。
字符串常量是由双引号括起来的字符序列。这里一定要注意'a'和”a”的不同,前者为字符常量,后者为字符串常量,
c规定:每个字符串的结尾加一个结束标志,实际上”a”包含两个字符:'a'和。
6. 数值型数据间的混合运算
整型、字符型、实型数据间可以混合运算,运算时不同类型数据要转换成同一类型再运算,转换规则:
char,short → int → unsigned → long → double ← float
7. 运算符和表达式
c运算符包括:
算数运算符( + - * / % )
关系运算符( > < == >= ⇐ != )
逻辑运算符( ! && || )
位运算符( « » ~ | ^ & )
赋值运算符( = )
条件运算符( ? : )
逗号运算符( , )
指针运算符( * & )
求字节数( sizeof )
强制类型转换(类型)
分量运算符( . → )
下标运算符( [ ] )
其它运算符( 如函数调用运算符( ) )
自增自减运算符( ++ – )注意:++i和i++的不同之处,++i使用i之前先使i加1,i++使用i之后,使i加1。
逗号表达式的求解过程:先求解表达式1,再求解表达式2,整个表达式的值是表达式2的值。
Top of Page
第三章 最简单的c程序设计
1.c的9种控制语句:
if() ~ else~
for()~
while()~
do~while()
continue
break
switch
goto
第 2 页新建 文本文档
return
程序的三种基本结构:顺序结构,选择结构,循环结构
2.数据输出
c语言不提供输入输出语句,输入输出操作是由c的库函数完成。但要包含头文件stdio.h。
putchar( ) 向终端输出一个字符
printf( )的格式字符:
① d格式符 用来输出十进制整数
%d 按整型数据的实际长度输出
%md 使输出长度为m,如果数据长度小于m,则左补空格,如果大于m,则输出实际长度
%ld 输出长整型数据
② o格式符 以八进制形式输出整数
③ x格式符 以十六进制形式输出整数
④ u格式符 用来输出unsigned型数据,以十进制形式输出
⑤ c格式符 用来输出一个字符
⑥ s格式符 输出一个字符串
%s 输出实际长度字符串
%ms 输出的串占m列,如果串长度小于m,左补空格,如果大于m,实际输出
%-ms输出的串占m列,如果串长度小于m,右补空格,
%m.ns 输出占m列,但只取字符串中左端n个字符并靠右对齐
%-m.ns m、n含义同上,靠左对齐,如果n>m,则m自动取n值
⑦ f格式符 以小数形式输出实数
%f 整数部分全部输出,小数部分输出6位
%m.nf 输出数据共占m列,其中有n位小数。如果数值长度小于m,左补空格
%-m.nf 同上,右补空格
⑧ e格式符 以指数形式输出实数
%e 系统指定6位小数,5位指数(e+002 )
⑨ g格式符 输出实数,根据数值大小,自动选f格式或e格式
3.数据输入
getchar( ) 从终端输入一个字符
scanf( 格式控制,地址列表) 标准C scanf中不使用%u,对于unsigned型数据,以%d或%o或%x输入。%后的*,用来跳
过它相应的数据。输入数据时不能规定精度如scanf( ”%7.2f”, &a );是不合法的。
Top of Page
第四章 逻辑运算和判断选取控制
1. 关系运算符:
第 3 页新建 文本文档
c提供6种关系运算符(> < ⇐ >= == != )前四种优先级高于后两种。
2. If语句
C提供了三种形式的if语句
If(表达式) 语句
If(表达式) 语句1 else 语句2
If(表达式1) 语句1
Else if(表达式2) 语句2
…
else 语句n
3. 条件运算符
(a>b)?a:b 条件为真,表达式取值a,否则取值b
4. Switch语句
Switch(表达式)
{
case 常量表达式1:语句1; break;
case 常量表达式2:语句2; break;
…
case 常量表达式n:语句n; break;
default :语句n+1;
}
Top of Page
第五章 循环控制
1. 几种循环语句
goto语句(现已很少使用)
while语句 先判断表达式后执行语句
do-while语句 先执行语句后判断表达式
for语句
2. Break语句和continue语句
Break语句用于跳出循环,continue用于结束本次循环。
Top of Page
第 4 页新建 文本文档
第六章 数组
1. 一维数组
c规定只有静态存储(static)和外部存储(extern)数组才能初始化。给数组初始化时可以不指定数组长度。
2. 二维数组
3. 字符数组
部分字符串处理函数
puts(字符数组) 将一个字符串输出到终端。
gets(字符数组) 从终端输入一个字符串到字符数组,并且得到一个函数值,为该字符数组的首地址
strcat(字符数组1,字符数组2) 连接两个字符数组中的字符串,数组1必须足够大。
Strcpy(字符数组1,字符串2) 将字符串2拷贝到字符数组1中。
Strcmp(字符串1,字符串2) 比较字符串,相等返回0,字符串1>字符串2,返回正数,小于返回负数。
Strlen(字符数组) 求字符串长度。
Strlwr( 字符串) 将字符串中的大写字母转换成小写
Strupr( 字符串) 将字符串中的小写字母转换成大写
以上是一些比较常用的字符串处理函数。
Top of Page
第七章 函数
1. 关于形参和实参的说明
① 在函数被调用之前,形参不占内存
② 实参可以是常量、变量或表达式
③ 必须指定形参的类型
④ 实参与形参类型应一致
⑤ 实参对形参的数据传递是”值传递”,即单向传递
2. 函数返回值
如果想让函数返回一个值,在函数中就要用return语句来获得,在定义函数时也要对函数值指定类型,如果不指定,
默认返回整型。
3. 函数调用
1)注意在函数调用时实参和形参的个数、类型应一一对应。对实参表求值的顺序是不确定的,有的系统按自左至右,
有的系统则按自右至左的顺序。这一点要注意。
2)函数调用的方式:函数语句,函数表达式,函数参数
3)如果主调函数和被调函数在同一文件中,并且主调函数在前,那么一般要在主调函数中对被调函数进行说明。除 非:(1)被调函数的返回值类型为整型或字符型(2)被调函数出现在主调函数之前。
第 5 页新建 文本文档
4)对函数的说明和定义是不同的,定义是指对函数功能的确立,包括指定函数名,函数值类型,形参及其类型、函数
体等。说明则只是对已定义的函数返回值类型进行说明,只包括函数名、函数类型以及一个空的括弧,不包括形参和
函数体。
5)c语言允许函数的递归调用(在调用一个函数的过程中又出现直接或间接的调用该函数本身)。
4. 数组作为函数参数
1)数组元素作为函数参数 和一般变量相同
2)数组名作参数应该在主调和被调函数分别定义数组,形参数组的大小可以不定义。注意:数组名作参数,不是单向
传递。
3)多维数组作参数,在被调函数中对形参数组定义时可以省略第一维的大小说明,但不能省略第二维或更高维的说
明。
5. 局部变量和全局变量
从变量作用域角度分,变量可分为局部变量和全局变量。
1)内部变量(局部变量)
在一个函数内定义,只在函数范围内有效的变量。
2)外部变量(全局变量)
在函数外定义,可以为本文件其它函数所共用,有效范围从定义变量的位置开始
到本文件结束。建议尽量少使用全局变量,因为它在程序全部执行过程中都占用
资源,而且使函数的通用性降低了。如果在定义外部变量之前的函数要想使用该
外部变量,则应在该函数中用extern作外部变量说明。
6. 动态存储变量与静态存储变量
从变量值存在的时间(生存期)角度来分,可分为静态存储变量和动态存储变量。静态存储指在程序运行期间给变量
分配固定的存储空间,动态存储指程序运行期间根据需要动态的给变量分配存储空间。
C语言中,变量的存储方法分为两大类:静态存储类和动态存储类,具体包括:自动的(auto),静态的(static),
寄存器的(register),外部的(extern)。
1) 局部变量的存储方式
函数中的局部变量如不作专门说明,都之auto的,即动态存储的,auto可以省略。局部变量也可以定义为static的,
这时它在函数内值是不变的。静态局部变量如不赋初值,编译时系统自动赋值为0,动态局部变量如不赋初值,则它
的值是个不确定的值。C规定,只有在定义全局变量和局部静态变量时才能对数组赋初值。为提高执行效率,c允许将
局部变量值放在寄存器中,这种变量叫register变量,要用register说明。但只有局部动态变量和形式参数可以作为
register变量,其它不行。
2) 全局变量的存储方式
全局变量在函数外部定义,编译时分配在静态存储区,可以在程序中各个函数所引用。多个文件的情况如何引用全局
变量呢?假如在一个文件定义全局变量,在别的文件引用,就要在此文件中用extern对全局变量说明,但如果全局变
量定义时用static的话,此全局变量就只能在本文件中引用了,而不能被其它文件引用。
3) 存储类别小结
从作用域角度分,有局部变量和全局变量
局部变量:自动变量,即动态局部变量(离开函数,值就消失)
静态局部变量(离开函数,值仍保留)
寄存器变量(离开函数,值就消失)
(形参可定义为自动变量和寄存器变量)
全局变量:静态全局变量(只限本文件引用)
第 6 页新建 文本文档
全局变量(允许其它文件引用)
从存在的时间分,有静态存储和动态存储
动态存储:自动变量(本函数内有效)
寄存器变量(本函数内有效)
形参
静态存储:静态局部变量(函数内有效)
静态全局变量(本文件内有效)
全局变量(其它文件可引用)
从变量值存放的位置分
静态存储区:静态局部变量
静态全局变量
全局变量
动态存储区:自动变量和形参
寄存器内:寄存器变量
7. 内部函数和外部函数
内部函数:只能被本文件中的其它函数调用,定义时前加static,内部函数又称静态函数。
外部函数:可以被其它文件调用,定义时前加extern,如果省略,则隐含为外部函数,在需要调用此函数的文件中,
一般要用extern说明。
Top of Page
第八章 预编译处理
c编译系统在对程序进行通常的编译之前,先进行预处理。c提供的预处理功能主要有以下三种:1)宏定义 2)文件
包含 3)条件编译
1. 宏定义
不带参数的宏定义
用一个指定的标识符来代表一个字符串,形式:#define 标识符 字符串
几点说明:
1) 宏名一般用大写
2) 宏定义不作语法检查,只有在编译被宏展开后的源程序时才会报错
3) 宏定义不是c语句,不在行末加分号
4) 宏名有效范围为定义到本源文件结束
5) 可以用#undef命令终止宏定义的作用域
6) 在宏定义时,可以引用已定义的宏名
带参数的宏定义
第 7 页新建 文本文档
定义形式:#define 宏名(参数表) 字符串
这和函数有些类似,但他们是不同的:
1) 函数调用时,先求实参表达式值,再代入形参,而宏只是简单替换,并不求值
2) 函数调用是在程序运行时分配内存的,而宏展开时并不分配内存,也没有返回值的概念
3) 对函数中的实参和形参都要定义类型,而且要求一致,宏名无类型,其参数也没有类型。
4) 函数只有一个返回值,而宏可以得到几个结果
5) 宏替换不占运行时间,只占编译时间,而函数调用占运行时间
2. 文件包含处理
#include “文件1” 就是将文件1的全部内容复制插入到#include位置,作为一个源文件进行编译。
在#include命令中,文件名可以用” “也可以用< >,假如现在file1.c中包含file2.h文件,” “表示系统先在file1.c
所在目录中找file2.h,如果找不到,再按系统指定的标准方式检索目录,< >表示系统直接按指定的标准方式检索目
录。所以用” “保险一点。
3. 条件编译
条件编译指不对整个程序都编译,而是编译满足条件的那部分。条件编译有以下几种形式:
1)#ifdef 标识符
程序段1
#else
程序段2
#endif
它的作用:当标识符在前面已经被定义过(一般用#define),则对程序段1编译,否则对程序段2编译。
2)#ifndef 标识符
程序段1
#else
程序段2
#endif
它的作用和#ifdef相反,当标识符没被定义过,对程序段1编译,否则对程序段2编译。
3)#if 表达式
程序段1
#else
程序段2
#endif
它的作用:当表达式值为真(非0)时,对程序段1编译,否则对程序段2编译。
Top of Page
第九章 指针
指针说白了就是地址。指针变量就是用来存放指针(地址)的变量。
第 8 页新建 文本文档
1. 变量的指针和指向变量的指针变量
读起来很拗口,说白了就是变量的地址和用来存放变量地址的地址变量。因为一个变量在编译的时候系统要为它分配
一个地址,假如再用一个变量来存放这个地址,那么这个变量就叫做指向变量的指针变量,也就是用来存放变量地址
的这么一个变量。所谓”指向”就是指存放××的地址,如指向变量的指针变量,”指向”就是指用来存放变量的地址,
再如指向数组的指针变量,”指向”就是指存放数组的地址。只要理解了这个,指针也就不难了。另外,还有指向字符
串的指针变量,指向函数的指针变量,指向指针的指针变量等。
1) 指针变量的定义
形式:类型标识符 *标识符 如:int *pointer;
要注意两点:*表示pointer是个指针变量,在用这个变量的时候不能写成*pointer, *pointer是pointer指向的变
量。一个指针变量只能指向同一个类型的变量。如上面
pointer只能指向int型变量。
2)指针变量的引用
两个有关的运算符:
& 取地址运算符 &a 就代表变量a的地址
* 指针运算符 *a 就代表变量a的值
2. 数组的指针和指向数组的指针变量
数组的指针指数组的起始地址,数组元素的指针指数组元素的地址。
1)指向数组元素的指针变量的定义与赋值
定义和指向变量的指针变量定义相同,c规定数组名代表数组的首地址,即第一个数组元素地址。
2)通过指针引用数组元素
我们通常引用数组元素的形式是a[i],如果用指针可以这样引用,*(a+i),或定义一个指针变量p,将数组a的首地址
赋给p,p=a;然后用*(p+i)引用。
注意:指针变量p指向数组a首地址,则p++指向数组a的下一元素地址,即a[1]的地址。
3)数组名作函数参数
形参数组和实参数组之间并不是值传递,而是共用同一段地址,所以在函数调用过程中如果形参的值发生变化,则实
参的值也跟着变化。
4)指向多维数组的指针和指针变量
以二维数组为居多。假设定义了一个二维数组a[3][4],那么
a代表整个二维数组的首地址,也代表第0行的首地址,同时也是第0行第0列的元素的首地址。a +0和a[0]代表第0行
首地址,a+1和a[1]代表第一行的首地址。
假设a是一个数组的首地址,那么如果a是一维的,a+I代表第I个元素的地址,如果a是二维的,则a+I代表第I行的首
地址。
那么第一行第二列的元素地址如何表示呢?a[1]+2或&a[1][2]或*(a+1)+2。
我们只要记住:在二维数组中a代表整个数组的首地址,a[I]代表第I行的首地址,a[I]与*(a+I)等价就行了。只要运
用熟练了就没什么复杂的了。
5)指向由m个整数组成的一维数组的指针变量
如:int (*p)[4],p是一个指向包含4个元素的一维数组,如果p先指向a[0],则p+1指向a[1],即p的增值是以一维数
组的长度为单位的,这里是4,举个例子:
假设a[3][4]=,p先指向a[0]也就是数组a的首地址,那么p+1就是a[1]的首地址即元素9的地址,因为在定义p时int
(*p)[4],定义一维数组长度为4,所以p+1就等于加了一个一维数组的长度4。
3. 字符串的指针和指向字符串的指针变量
第 9 页新建 文本文档
1)字符串的表示形式
c中字符串有两种表示形式:一种是数组,一种是字符指针
char string[]=“I love c!”;
char *str=“I love c!”;
其实指针形式也是在内存中开辟了一个数组,只不过数组的首地址存放在字符指针变量str中,千万不要认为str是一
个字符串变量。
2)字符串指针作函数参数
实际上字符串指针就是数组的首地址。
3)字符指针变量与字符数组的区别
① 字符数组由若干元素组成,每个元素存放一个字符,而字符指针变量只存放字符串的首地址,不是整个字符串
② 对数组初始化要用static,对指针变量不用。
③ 对字符数组赋值,只能对各个元素赋值,不能象下面这样:
char str[14];
str=“I love c!”;
对指针变量可以,
char *str;
str=“I love c!”;
注意:此时赋给str的不是字符,而是字符串首地址。
④ 数组在定义和编译时分配内存单元,而指针变量定义后最好将其初始化,否则指针变量的值会指向一个不确定
语法
Hello World 程序
下面是一个在标准输出设备 (stdout) 上,印出 “Hello World程序|Hello World” 字符串的简单程序,这样子的程序,通常作为初学编程语言时的第一个程序:
<source lang=“c”> #include <stdio.h>
int main(void) {
printf("Hello, world!\n"); return 0;
} </source>
进一步了解
C语言由函数和变量组成。C的函数就像是Fortran中的子程序和函数。
在C语言中,程序从main开始执行。main函数通过调用和控制其它函数进行工作。例如上面的printf。程序员可以自己写函数,或从库中调用函数。在上面的return 0;使得main返回一个值给调用程序的外壳,表明程序已经成功运行。
一个C语言的函数由返回值、函数名、参数列表(或void表示没有返回值)和函数体组成。函数体的语法和其它的复合的语句部分是一样的。
复合语句
C语言中的复合语句(或称语句块)的格式为:
<source lang=“c”> {语句;语句;……} </source>
复合语句可以使得几个语句从文法上变成一个语句。
条件语句
C语言有三种条件语句形式。两种是if,另一种是switch。
两种if包括:
<source lang=“c”> if(表达式)
语句;
</source>
以及
<source lang=“c”> if(表达式)
语句;
else
语句;
</source>
表达式的值非零表示条件为真;如果条件为假,程序将跳过if处的语句,直接执行if后面的语句。但是如果if后面有else,则当条件为假时,程序跳到else处执行。if和else后面的语句可以是另个if语句,这种套迭式的结构,允许更复杂的逻辑控制流程得以实现。在一般情况下,else一定与最接近的if成对,必要时可用括号{}越过此限制。比较下面两种情况:
<source lang=“c”> if(表达式)
if (表达式) 语句; else 语句;
</source> <source lang=“c”> if(表达式){
if (表达式) 语句;
} else
语句;
</source> switch通常用于对几种有明确值的条件进行控制。它要求的条件值通常是整数或字符。与switch搭配的条件转移是case。使用case后面的标值,控制程序将跳到满足条件的case处一直往下执行,直到语句结束或遇到break。通常可以使用default把其它例外的情况包含进去。如果switch语句中的条件不成立,控制程序将跳到default处执行;如果省略default子句,则直接执行下一语句。switch是可以嵌套的。
<source lang=“c”> switch (<表达式>) {
case <值1>: <语句> break; case <值2>: <语句> default : <语句>
} </source>
循环语句
C语言有三种形式的循环语句: <source lang=“c”> do
<语句>
while (<表达式>);
while (<表达式>)
<语句>;
for (<表达式1> ; <表达式2> ; <表达式3>)
<语句>;
</source>
在while和do中,语句将执行到表达式的值为零时结束。在do…while语句中,循环体将至少被执行一次。这三种循环结构可以互相转化: <source lang=“c”> for (<表达式1>; <表达式2>; <表达式3>)
<语句>;
</source>
如果<语句>中不使用continue语句的话,相当于 <source lang=“c”> <表达式1>; while (<表达式2>) {
<语句>; <表达式3>;
} </source>
当循环条件一直为真时,将产生死循环。
跳转语句
跳转语句包括四种:goto,continue,break和return。
goto语句是无条件转移语句:
<source lang=“c”>goto 标记;</source>
标记必须在当前函数中定义,使用“标记:”的格式定义。程序将跳到标记处继续执行。由于goto容易产生阅读上的困难,所以应该尽量少用。
continue语句用在循环语句中,作用是结束当前一轮的循环,马上开始下一轮循环。
break语句用在循环语句或switch中,作用是结束当前循环,跳到循环体外继续执行。但是使用break只能跳出一层循环。在要跳出多重循环时,可以使用goto使得程序更为简洁。
当一个函数执行结束后要返回一个值时,使用return。return可以跟一个表达式或变量。如果return后面没有值,将执行不返回值。
在C99中的运算符号
{| class=“wikitable”
width=“322” |
|||
width=“330” |
|||
width=“322” |
|||
width=“330” |
|||
width=“322” |
* / % |
width=“330” |
|
width=“330” |
|||
width=“322” |
|||
width=“330” |
|||
width=“322” |
< ⇐ > >= |
width=“330” |
|
width=“322” |
=====!= |
width=“330” |
|
width=“322” |
|||
width=“330” |
|||
width=“322” |
|||
width=“330” |
|||
width=“322” |
|||
width=“330” |
|||
width=“322” |
|||
width=“330” |
|||
width=“322” |
|||
width=“330” |
|||
width=“322” |
|||
width=“330” |
|||
width=“322” |
= += -= *= /= %= «= »= &= |
= |
|
width=“330” |
|||
width=“322” |
|||
width=“330” |
|||
比较特别的是,位右移(»)运算子可以是算术(左端补最高有效位)或是逻辑(左端补 0)位移。例如,将 11100011 右移 3 位,算术右移后成为 11111100,逻辑右移则为 00011100。因算术位右移较适于处理带负号整数,所以几乎所有的编译器都是算术位右移。
数据类型
基础数据类型
注意:以下是典型的数据位长和范围。但是编译器可能使用不同的数据位长和范围。这取决于使用的编译器。请参考具体的参考手册。
在头文件<limits.h>和<float.h>中说明了基础数据的长度。float,double和long double的范围就是在IEEE 754标准中提及的典型数据。 {| class=“wikitable” ! 关键字 || 位长 || 范围 || printf chars
<CODE>char</CODE> |
1 |
-128..127(或0..255,与体系结构相关) |
|||
<CODE>unsigned char</CODE> |
1 |
0..255 |
|||
<CODE>signed char</CODE> |
1 |
-128..127 |
|||
<CODE>int</CODE> |
|||||
-32768..32767 or<BR>-2147483648..2147483647 |
|||||
<CODE>unsigned int</CODE> |
2 or<BR>4 |
0..65535 or<BR>0..4294967295 |
|||
<CODE>signed int</CODE> |
|||||
-32768..32767 or<BR>-2147483648..2147483647 |
|||||
<CODE>short int</CODE> |
2 |
-32768..32767 |
|||
<CODE>unsigned short</CODE> |
2 |
0..65535 |
|||
<CODE>signed short</CODE> |
2 |
-32768..32767 |
|||
<CODE>long int</CODE> |
4 |
-2147483648..2147483647 |
|||
<CODE>unsigned long</CODE> |
4 |
0..4294967295 |
|||
<CODE>signed long</CODE> |
4 |
-2147483648..2147483647 |
|||
<CODE>long long</CODE> |
8 |
-9223372036854775808..9223372036854775807 |
|||
<CODE>unsigned long long</CODE> |
8 |
0..18446744073709551615 |
|||
<CODE>float</CODE> |
4 |
3.4×10-38..3.4×10+38 (7 sf) |
|||
<CODE>double</CODE> |
8 |
1.7×10-308..1.7×10+308 (15 sf) |
|||
<CODE>long double</CODE> |
8 或以上 |
编译器相关 |
|||
数组
如果一个变量名后面跟着一个有数字的中括号,这个声明就是数组声明。字符串也是一种数组。它们以ASCII的NUL作为数组的结束。要特别注意的是,方括内的索引值是从0算起的。
例如:
:int myvector [100];/* 从myvector[0]至myvector[99]止共100个元素 */ :char mystring [80]; :float mymatrix [3] [2] = {2.0 , 10.0, 20.0, 123.0, 1.0, 1.0}; :int notfull [3][3] = 1,{1,2,3},{4,5}}; (*) :char lexicon [10000] [300];/* 共一万个最大长度为300的字符数组。*/ :int a[3][4];
上面最后一个例子创建了一个数组,但也可以把它看成是一个多维数组。注意数组的下标从0开始。这个数组的结构如下: {| class=“wikitable”
<CODE>a[0][0]</CODE> |
|
<CODE>a[0][2]</CODE> |
|
<CODE>a[1][0]</CODE> |
|
<CODE>a[1][2]</CODE> |
|
<CODE>a[2][0]</CODE> |
|
<CODE>a[2][2]</CODE> |
|
例子(*)创建了一个3*3的二维数组,初始化时有些元素并未赋值。如下: :1 0 0 :1 2 3 :4 5 0<br /> 为0的位置的数值是随机的。
指针
如果一个变量声明时在前面使用 * 号,表明这是个指针型变量。换句话说,该变量存储一个地址,而 * 则是取内容操作符,意思是取这个内存地址里存储的内容。指针是 C 语言区别于其它同时代高级语言的主要特征之一。
指针是一把双刃剑,许多操作可以通过指针自然的表达,但是不正确的或者过分的使用指针又会给程序带来大量潜在的错误。
例如: <source lang=“c”> int *pi; /* 指向整型数据的指针变量 */ int *api[3];/* 由指向整型数据的指针构成的数组,长度为 3 */ char **argv; /* 指向一个字符指针的指针 */ </source> 储存在指针中的地址所指向的数值在程序中可以由 * 读取。例如,在第一个例子中, *pi 是一个整型数据。这叫做引用一个指针。
另一个运算符 &,叫做取地址运算符,它将返回一个变量、数组或函数的存储地址。因此,下面的例子: <source lang=“c”> int i, *pi; /* int and pointer to int */ pi = &i; </source> i 和 *pi 在程序中可以相互交替使用,直到 pi 被改变成指向另一个变量的地址。
字符串
C语言的字符串其实就是char型数组,所以使用字符串并不需要引用库。但是C标准库确实包含了一些用于对字符串进行操作的函数,使得它们看起来就像字符串而不是数组。使用这些函数需要引用标头档<string.h>。
<!—
究竟有没有必要把string.h里的几个函数列出来呢?既然string.h列了,其它的要不要列? 本人认为有凑篇幅之嫌,故暂隐去。建议删除。 如果想打开我也没意见,只有一个希望:请严格恪守ANSIC定义的原型,而不要这样随意。 别忘了C语言是强类型语言。
*strcat(dest, source) - 连接两个字符串,把source加到dest末尾。 *strchr(s, c) - 在字符串c中找出字符s第一次出现的位置。当没有找到时,返回Null。 *strcmp(a, b) - 比较字符串a和b的大小。如果两个字符串相同,返回0;如果a>b,返回正数;如果a<b,返回负数。 *strcpy(dest, source) - 把字符串source全拷贝到字符串dest中。 *strncat(dest, source, n) - 把source中的n个字符追加到dest后面。null后面的值将不会被添加。 *strncmp(a, b, n) - 比较字符串a和b中n个字符的大小。如果两个字符串相同,返回0;如果a>b,返回正数;如果a<b,返回负数。 *strncpy(dest, source, n) - 把字符串source拷贝到字符串dest中。(最多拷贝n个) *strrchr(s, c) - 在s中查找最后一次出现c的位置。返回这个位置。如果找不到,返回null。 —>
文件输入/输出
在C语言中,输入和输出是经由标准函式库中的一组函数来实现的。在ANSI/ISO C中,这些函数被定义在标头档<stdio.h>中。
标准输入/输出
有三个标准输入/输出是标准I/O库预先定义的:
*stdin 标准输入 *stdout 标准输出 *stderr 输入输出错误
<!— 究竟他们3个是不是在运行过程中打开和关闭的,强烈取决于实现,标准未加约束。故不宜明言。 这些定义在运行过程中是自动的打开和关闭的,所以它们并不需要进行显示定义。 —>
下面的这个例子显示了一个过滤程序(filter program)是怎样构成的。
<source lang=“C”> #include <stdio.h>
int main(void) {
int c; while (1) { c = getchar(); if (c==EOF) { perror("getchar()"); return -1; } putchar(c); } return 0;
} </source>
#include <stdio.h>
int main(void) {
int c; while (1) { c = getchar(); if (c==EOF) { perror("getchar()"); return -1; } putchar(c); } return 0;
}
内存管理
C语言的特色之一是:程序员必须亲自处理内存的分配细节。
C语言使用栈来保存函数返回地址/栈祯基址、完成函数的参数传递和函数局部变量的存储。 如果程序需要在运行的过程中动态分配内存,可以利用堆来实现。
基本上C程序的元素存储在内存的时候有3种分配策略:
*静态分配 如果一个变量声明为全局变量或者是函数的静态变量,这个变量的存储将使用静态分配方式。静态分配的内存一般会被编译器放在数据段或代码段来存储,具体取决于实现。这样做的前提是,在编译时就必须确定变量的大小。 以 IA32 的 x86 平台及 gcc 编译器为例,全局及静态变量放在数据段的低端;全局及静态常量放在代码段的高端。
*自动分配 函数的自动局部变量应该随着函数的返回会自动释放(失效),这个要求在一般的体系中都是利用栈来满足的。相比于静态分配,这时候,就不必绝对要求这个变量在编译时就必须确定变量的大小,运行时才决定也不迟,但是C89仍然要求在编译时就要确定,而C99放松了这个限制。但无论是C89还是C99,都不允许一个已经分配的自动变量运行时改变大小。
所以说C函数永远不应该返回一个局部变量的地址。
要指出的是,自动分配也属于动态分配,甚至可以用alloca(3)函数来像分配堆一样进行分配,而且释放是自动的。
*动态分配 还有一种更加特殊的情况,变量的大小在运行时有可能改变,或者虽然单个变量大小不变,变量的数目却有很大弹性,不能静态分配或者自动分配,这时候可以使用堆来满足要求。ANSI C 定义的堆操作函数是malloc(3)、calloc(3)、realloc(3)和free(3)。
使用堆内存将带来额外的开销和风险。
安全问题
C语言的特色之一是:语言不负责内存边界检查。<!—(待完成) —>
库
C语言的标准文档要求了一个平台移植C语言的时候至少要实现的一些功能和封装的集合,称为“标准库”,标准库的声明头部通过预处理器命令#include进行引用。
在C89标准中:
!文件!!简介说明
<assert.h> |
|
<ctype.h> |
|
<errno.h> |
|
<float.h> |
|
<limits.h> |
|
<locale.h> |
|
<math.h> |
|
<setjmp.h> |
|
<signal.h> |
|
<stdarg.h> |
|
<stddef.h> |
|
<stdio.h> |
|
<stdlib.h> |
|
<string.h> |
|
<time.h> |
在95年的修正版中
*<iso646.h> *<wchar.h> *<wctype.h>
在C99中增加了六个函式库
*<complex.h> *<fenv.h> *<inttypes.h> *<stdbool.h> *<stdint.h> *<tgmath.h>
以上是C语言的标准,而各个平台各自又对C库函数进行的各种扩充,就浩如烟海了。如POSIX C、GNU C等。
保留关键字
char |
short |
int |
unsigned |
long |
float |
double |
|
union |
void |
enum |
|
const |
volatile |
typedef |
|
register |
static |
extern |
|
case |
continue |
default |
|
else |
for |
goto |
|
return |
switch |
while |
C99新增关键字
_Bool |
_Complex |
|
_Imaginary |
inline |
restrict |
参考文献