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

参考文献

宋劲杉老师开源作品【Linux C编程一站式学习】|

C语言笔记|

C语言笔记|