一维数组的定义、初始化和引用 一维数组的定义、初始化和引用 1.一维数组的定义方式为:
类型说明符 数组名[常量表达式]
(1)数组名的命名方法与变量名相同,遵循标识符命名规则;
(2)数组是用方括号括起来的常量表达式,不能用圆括号;
(3)常量表达式表示数组元素的个数,即数组的长度,数组的下标从0开始,下标的最大值为:
常量表达式-1;
(4)常量表达式中可以包括常量和符号常量,不能包括变量。
可以用赋值语句或输入语句使数组中的元素得到值,但要占用运行时间。可以使数组在运行之前初始化,即在编译阶段使之得到初值。
2.对数组初始化可以用以下方法实现:
(1)在定义数组时对数组元素赋以初值。如:
static int a[10]={0,1,2,3,4,5,6,7,8,9};
经过上面的定义和初始化后,a[0]=0,a[1]=1,…,a[9]=9。
(2)初始化时可以只对一部分元素赋初值。例如:
static int a[10]={0,1,2,3,4};
定义的数组有10个元素,但只对其中前5个元素赋了初值,后5个元素初值为0。
(3)如果想使一个数组的元素值全部为0,可以用下面的方法:
static int a[10]={0,0,0,0,0,0,0,0,0,0};
不能用:
static int a[10]={0*10};
如果对static型数组不赋初值,系统会对定义的所有数组元素自动赋以0值。
(4)在对全部数组元素赋初值时,可以不指定数组长度。
3.一维数组的引用方法是:
C语言规定不能一次引用整个数组,引用时只能逐个元素引用,数组元素的表示形式为:
数组名[下标]
下标可以是整型常量或整型表达式。如:
a[0]=a[5]+a[7]-a[2*3];
§二维数组的定义、初始化和引用
1.二维数组定义的一般形式为
类型说明符 数组名[常量表达式][常量表达式]
C语言采用上述定义方法,我们可以把二维数组看做是一种特殊的一维数组:它的元素又是一维数组。在C语言中,二维数组中元素的排列顺序是:先按行存放,再按列存放,即在内存中先顺序存放第一行的元素,再存放第二行的元素。
2.二维数组的初始化:二维数组可以用下面的方法初始化:
(1)分行给二维数组赋初值。如:
static int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
以上赋值把第一个花括号内的数据赋给第一行元素,第二个花括号内数据赋给第二元素…,即按行赋值。
(2)可以将所有的数据写在一个花括号内,按数组排列的顺序对各元素赋值。
(3)可以对数组的部分元素赋初值。如:
static int a[3][4]={{1},{5},{9}};
以上赋值的结果是:数组第一列的元素分别赋了初值1,5,9,其余元素的值都是0。
(4)如果对二维数组的全部元素都赋初值,则定义数组时对第一维的长度可以不指定,但第二维的长度不能省。
3.二维数组的引用:二维数组的元素可以表示为:
数组[下标][下标]
在引用二维数组时,必须是单个元素,不能是整个数组名。下标可以是一个表达式,但不能是变量。如果下标是一个表达式,注意表达式的值不能超出数组定义的上、下限。
§字符串与字符数组
1.字符数组的定义
定义方法与前面介绍的类似,只是将数组定义为字符型即可。例如:
char c[10];
这里定义了一个包含10个元素的字符数组c。
2.字符数组的初始化
对字符数组初始化,可以采用以下方法:
(1)逐个字符赋给数组中各元素;
(2)用字符串常量使字符数组初始化。
3.字符串
在C语言中,字符串是作为字符数组来处理的,字符串可以存放在字符型一维数组中,故可以把字符型一维数组作为字符串变量。
字符串常量是用双引号括起来的一串字符。
C语言中约定用′\0′作为字符串的结束标志,它占内存空间,但不计入串的长度,′\0′的代码值为0。
系统对字符串常量也自动加一个′\0′作为结束符。例如″c
language″共有10个字符,但在内存中占11个字节,最后一个字节存放′\0′。
4.字符数组的输入输出
(1)逐个字符输入和输出
①在标准输入输出printf和scanf中使用%c格式描述符;
②使用getchar和putchar函数。例如:
for(i=0;i<10;i++)
scanf(″%c″,&str[i]); str[i]=′\0′;
for(i=0;i<10;i++)
printf(″%c″,str[i]);
(2)字符串整体输入输出
①在标准输入输出函数printf和scanf中使用%s格式描述符;
输入形式:
char str[6];
scanf(″%s″,str);
其中str为字符数组名,代表着str字符数组的起始地址,输入时系统自动在每个字符串后加入结束符′\0′。若同时输入多个字符串,则以空格或回车符分隔。
输入形式:
printf(″%s″,str);
输字符串时,遇第一个′\0′即结束。但可人为控制输出串所占的域宽,例如:
printf(″%6s″,str);printf(″%-6s″,str);
②使用gets和puts函数输入输出一行
gets函数用来从终端键盘读字符,直到遇换行符为止,换行符不属字符串的内容。
调用形式:
gets(str);
str为字符数组名或字符指针,字符串输入后,系统自动将′\0′置于串尾代替换行符。若输入串长超出数组定义长度时,系统报错。
puts函数用来把字符串的内容显示在屏幕上。
调用形式:
puts(str);
str的含义同上。输出时,遇到第一个′\0′结束并自动换行,字符串中可以含转义字符。
§字符串的指针以及指向字符串的指针变量
1.用指针方法实现一个字符串的存储和运算如:
char*strp=″china″;
此处定义了一个字符指针变量strp,变量中存放的是字符串第一个字符的地址。
C语言对字符串常量是按字符数组处理的,它实际上在内存开辟了一个字符数组用来存放字符串变量,并把字符串首地址赋给字符指针变量strp。在输出时用
printf(″%s
″,strp);
通过字符数组名或字符指针变量可以输出一个字符串。而对一个数值型数组,是不能企图用数组名输出它的全部元素的。
2.字符指针变量与字符数组
虽然用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点:
(1)字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串的首地址),决不是将字符串放到字符指针变量中。
(2)对字符数组只能对各个元素赋值,不能用以下办法对字符数组赋值。
char str[14];
str=″I love China!″;
而对字符指针变量,可以采用下面方法赋值:
char*a;
a=″I love China!″;
但注意赋给a的不是字符,而是字符串的首地址。
(3)赋初值时,对以下的变量定义和赋初值:
char*a=″I love China!″;
等价于:
char*a;
a=″I love China!″;
而对数组初始化时:
static char str[14]={″I love China!″};
不能等价于
char str[14];
str[]={″I love China!″};
即数组可以在变量定义时整体赋初值,但不能在赋值语句中整体赋值。
(4)在定义一个数组时,在编译时即已分配内存单元,有确定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以放一个地址值,也就是说,该指针变量可以指向一个字符型数据,但如果未对它赋予一个地址值,这时该指针变量并未具体指向哪一个字符数据。
(5)指针变量的值是可以改变的。
3.字符串处理函数
C语言中没有对字符串进行合并、比较和赋值的运算符,但几乎所有版本的C语言中都提供了有关的库函数。例如:
(1)strcat函数:连接两个字符数组中的字符串
(2)strcpy函数:字符拷贝函数
(3)strcmp函数:字符比较函数
(4)strlen函数:测试字符串长度的函数
(5)strlwr函数:将字符串中大写字母转换成小写字母
(6)strupr函数:将字符中小写字母转换成大写字母
§传给main函数的参数
在此之前,我们在编写main函数时,其后一对圆括号是空的,没有参数。其实,在支持C的环境中,可以在运行C程序时,通过运行C程序的命令行,把参数传送给C程序。
§通过实参向函数传递函数名或指向函数的指针变量
1.指向函数的指针变量的定义
在C语言中函数名代表该函数的入口地址,因此可以定义一种指向函数地址的指针变量。
2.函数名或指向函数的指针变量作为参考
函数名或指向函数的指针变量可以作为实参传送给函数。这时,对应的形参是类型相同的指针变量。
§函数的递归调用
C语言中的函数可以递归调用,即:可以直接或间接地自己调用自己。前者称简单递归,后者称间接递归。
§局部变量和全局变量
在函数内部或复合语句内部定义的变量称为局部变量。函数的形参属于局部变量。在函数外部定义的变量称为全局变量。有时,局部变量也称为内部变量,全局变量也称为外部变量。
§变量的存储属性(自动、静态、寄存器、外部),变量的作用域和生存期
C语言中用来说明变量存储属性的关键字共有四个:auto(自动)、static(静态)、register(寄存器)、extern(外部)。
1.局部变量的存储属性
局部变量可以是自动类别(用auto,register说明),也可以是静态类别(用static说明)。形参只能是自动存储类别,不允许是static类别。
当局部变量未指明类别时,被自动说明成自动(auto)变量。这类局部变量称为自动变量。其值存放在内存的动态存储区,因此在退出其作用域后,变量被自动释放,其值不予保留。
当局部变量说明成寄存器(register)类型时,与自动(auto)变量一样属于自动类别,所不同的是此时变量的值保留在CPU中的寄存器中。
当变量说明成static(静态)类型时,称这样的变量为静态局部变量。在程序运行期间,它占据一个永久的存储单元,在函数退出后,变量的值仍旧保留。
2.全局变量的存储属性
全局变量都属于静态存储类别,可以用extern和static对它们进行说明。
当一个文件中要引用另一文件中的全局变量或在全局变量定义之前要引用它时,可用exˉtern说明。相当于扩大全局变量的作用域。
用static(静态)说明的全局变量称为静态全局变量,它仅能由本文件引用,即使在其文件中用extern说明也不能使用,它相当于限制了全局变量作用域的扩展。
§内部函数和外部函数
1.在C语言中,所有的函数在本质上都是外部函数。
2.函数定义时用extern进行说明称为外部函数。函数的隐含类别为外部函数,外部函数可以被其他文件调用。
3.定义函数时用static进行说明称为静态函数,也称内部函数。静态函数也是外部函数,只是它仅局限于它所在的文件,其他文件不能调用。
§宏定义(不带参数的宏定义,带参数的宏定义)
1.编译预处理就是对C源程序进行编译前,由″编译预处理程序″对预处理命令行进行处理的过程。
2.C语言中,凡是以″#″开头的行,都称为″编译预处理″命令行。
C语言中的编译预处命令有:#define,#undef,#include,#if,#else,#elif,#endif,#ifdef,#ifndef,#line,#pragma,#error。这些预处理命令组成的预处理命令行必须在一行的开头以″#″号开始,每行的未尾不得加″;″号,以区别于C语句、定义和说明语句。
3.不带参数的宏定义:命令的一般形式为:
#define 标识符 字符串
定义中的″标识符″为用户定义的标识符,称为宏名。在预编译时,C预编译程序将出现的宏名替换成″字符串″的内容,这一过程也称为宏展开。
4.带参数的宏定义:命令的一般形式为
#define宏名(形式参数) 字符串
定义中的″字符串″应包含括号中所指定的形式参数。注意宏名与括号之间不要加空格,否则就成为不带参数的宏定义了。
5.预编译时,遇到带实参的宏名,则按命令行中指定的字符串从左到右进行置换,原则是:
遇到实参则以实参代替,非形参字符原样保留,从而形成展开后的内容。
§“文件包含”处理
1.文件包含的一般形式为:
#include″头文件名″#include
头文件名一般由C语言提供,也可以是用户自己编写的,头文件通常用.h作为后缀。
2.当头文件名用双引号括起来时,系统首先在使用此命令的文件所在的目录中查找被包含的文件,找不到时,再按系统指定的标准方式检索其它目录;当头文件名用尖括号括起来时,则直接按系统指定的标准检索方式查找被包含的文件。
3.预编译时,将被包含文件的内容全部复制到包含文件中,这就是文件″包含处理″。
§条件编译
C语言中的条件编译命令可对源程序代码的各部分有选择地进行编译。条件编译主要有如下3种使用格式:
使用格式1:
#ifdef标识符
程序段1
#else
程序段2
#endif
使用格式2:
#ifndef标识符
程序段1
#else
程序段2
#endif
使用格式3:
#if表达式
程序段1
#else
程序段2
#endif
使用格式1表示如果已定义了“标识符”,则编译“程序段1”,否则编译“程序段2”;使用格式2表示如没有定义了“标识符”,则编译“程序段1”,否则编译“程序段2”;使用格式3表示如“表达式”的值为“真”时,则编译“程序段1”,否则编译“程序段2”。
§结构体与共用体类型数据的定义方法和引用方法
1.结构体类型数据的定义方法:
可以采用下面三种方法来定义:
(1)先定义结构体类型再定义变量名先定义一个结构体类型,一般形式为:
struct结构体名
{成员列表};
(2)在定义类型的同时定义变量:一般形式为:
struct结构体名
{
成员列表
}变量名列表;
(3)直接定义结构类型变量:一般形式:
struct
{
成员列表
}变量名列表;
即不出现结构体名。
2.结构体类型变量的引用:
在定义了结构体变量后,可以采用下列方法引用结构体类型的变量:
结构体变量名.成员名
这里″.″是成员(分量)运算符,它在所有的运算符中优先级最高。
3.共用体类型变量的定义形式为:
union共用体名
{成员列表
}变量列表;
4.共用体变量的引用:可以用下面的形式引用共用体变量:
共用体变量名.成员名
§用指针和结构体构成链表,单向链表的建立、输出、删除与插入
1.链表是一种常用的重要的数据结构,它是动态地进行存储分配的一种结构。
2.所谓建立链表是指从无到有地建立起一个链表,即一个一个地输入各结点数据,并建立起前后相链的关系。
3.所谓输出链表就是将链表各结点的数据依次输出。
4.所谓删除链表事实上就是删除链表中的某个结点。
5.所谓插入链表就是在链表中某个位置插入一个或几个结点。
§位运算的含义及应用
1.位的概念
大多数计算机系统的内存储器是由众多的存储单元构成的。在微机中,每个存储单元是1个字节,它由8位二进制数构成,可以表示2 8
=256种信息,各位的编号从0~7,最左边的位(第7位)是最高位,最右边的位(第0位)是最低位。
本章中的位就是指上述提到的二进制位,本章中的位运算就是指对这些二进制的位进行逻辑运算、移位运算等操作。
2.数的编码
数在计算机中是以二进制表示的,但是它并不是简单地以它本身的数值的二进制形式来直接表示,而要进行一定的编码,以方便计算机进行处理。常用的编码有原码、反码、补码三种。
3.真值与原码
我们将一个十进制数的二进制表示称为这个十进制数的真值,它代表了这个十进制数本身的数值。下表列出了一些数的真值。 真值举例
数
二进制表示 真值(16位)
0
1
┇
7 0
1
┇
111 0000000000000000
0000000000000001
┇
0000000000000111 ┇
15
┇ ┇
1111
┇ ┇
0000000000001111
┇
225
┇
4095
┇
65535
11111111
┇
111111111111
┇
1111111111111111 0000000011111111
┇
0000111111111111
┇
1111111111111111
用真值表示的数只能是正数,对于负数,要用“一”号标明,例如:
-7的真值为-0000000000000111 -65535的真值为-1111111111111111
这势必造成用计算机表示数时的不便,故引入了原码表示法。
在原码表示法中,最高位代表符号位,用“1”表示负数,“0”表示正数;余下的数位用来表示真值的绝对值。
数字零存在着两种表示方法:+0与-0。
4.反码
若采用反码表示,则对应的原码应按照以下方法进行转换:
(1)如果真值为正,则它的反码与原码相同;
(2)如果真值为负,则反码的符号位为1,其余各位就是对原码取反(即原码的1变为0,原码的0变为1)。
5.补码
(1)为什么要引入补码
补码具有许多独特的优点:首先它可以变减法运算为加法运算,使得计算时步骤统一,速度提高;其次,在这种系统下的“0”只有惟一的一种表示方法,这就是现代的计算机系统中大多采用补码的原因。
(2)补码的规定
①正数的原码、补码、反码均相同;
②计算负数的补码时,先置符号位为1,再对剩余原码的位数逐位取反,最后对整个数加1。在微机上以8位二进制数为一字节的存储单元中采用补码系统,它可以存放的最小整数为-128,最大整数为+127。若采用两个字节来表示一个整数,则可表示的最小整数为-32768,最大整数为+32767。
§简单的位运算
C语言提供了位(bit)运算的功能,这使它能像汇编语言一样用来编写系统程序。位运算符共有六种; 位运算符
位运算符 含义& 位与 位或∧ 位异或~
位取反<<
位左移>> 位右移本节介绍前四种运算,即位的逻辑运算。
1.按位与运算
①概念
“按位与”运算符“&”是双目运算符,其功能是将参与运算两数的各对应二进位相与。只能对应的两个二进位均为1时,结果位才为1,否则为0。
②“按位与”与运算的特殊用途Ⅰ.清零
如果想将一个存储单元清零,即使其全部二进位为0,可按这样的方法计算:
找一个数,它的补码形式中各位的值符合如下条件:原来的数中为1的位,新数中相应位为0(注意,并不要求原数为0的位上,新数相应位为1,新数相应位可以是0或1);对二者进行&运算。Ⅱ.取一个数中某些字节
对于一个整数a(占2个字节),如要想得到其中的低字节,只需将a与特定的一个数按位与即可。
③要想将一个数的某一位保留下来,可将该数与一个特定的数进行&处理。
2.按位或运算
“按位或”运算符“
”是双目运算符,其功能是将参与运算两数的各对应的二进位相或。只要对应的两个二进位有一个为1时,结果位就为1。参与运算的整数均以补码出现。
3.按位异或运算
①概念
“按位异或”运算符“∧”是双目运算符,其功能是将参与运算两数的各对应位相异或,当两对应的二进位相异时,结果为1,否则为0。参与运算整数仍以补码出现。
②“异或”运算的特殊应用Ⅰ.使特定位翻转
Ⅱ.与0相“异或”,保留原值。Ⅲ.交换两个值。
4.“取反”运算
“~”是一个一元运算符,即它的运算量只有一个,用来对一个二进制数按位取反,即将0变1,1变0。比如说,~025就是对八进制数25(即二进制数000000000010101)按位求反。
§移位运算
1.移位运算符
移位运算是对操作数以二进制位为单位进行左移或右移,如表所示。 移位操作
运算符 名 称 例 子 运算功能
>> 右移位
b>>3 b右移3位
<
c<<2 c左移2位
2.左移运算
左移运算符“<
a<<4
就把a的各二进位向左移动4位。如a=00000011(十进制3),左移4位后为00110000(十进制48)。
3.右移运算
右移运算符“>>”是双目运算符,其功能是把“>>”左边的运算数的各二进位全部右移若干位,“>>”右边的数指定移动的位数。例如:
a=15,a>>2
表示把000001111右移为00000011(十进制3)。应该说明的是,对于有符号数,在右移时,符号位将随同移动。当为正数时,最高位补0;而为负数时,符号位为1,最高位是补0或1取决于编译系统的规定。Turbo
C规定补1。
右移运算相当于将运算对象除2.
§位赋值运算
位赋值运算符如表所示。
位赋值运算符
运算符 名 称 例
子 等价于
&= 位与赋值 a&=b a=2&b
|= 位或赋值 a|=b a=a|b
∧= 位异或赋值 a∧=b a=a∧b
>>= 右移赋值
a>>b a=a>>b
<<= 左移赋值
a<<=b a=a<
位赋值运算的过程为:
(1)先对两个操作数进行位操作;
(2)然后把结果赋予第一个操作数,因此第一个操作数必须是变量。
位赋值运算与算术赋值运算相似,它们都统称复合赋值运算。
§文件类型指针(FILE类型指针)
C语言中的文件分缓冲型文件和非缓冲型文件两种,此处只讨论缓冲型文件。对于缓冲型文件,每个被使用的文件都在内存中开辟一个区,用来存放文件的有关信息(如文件名字、文件状态及文件当前位置等)。这些信息保存在有关结构体类型的变量中。该结构体类型由系统定义,取名为FILE。
§文件的打开与关闭(fopen,fclose)
1.和其他高级语言一样,C语言对文件读写之前应该″打开″该文件,在使用结束之后应″关闭″该文件。文件的操作方式如下表所示。
2.C语言中打开文件用fopen()函数,fopen()函数的调用方式通常为:
FILE *fp;
fp=fopen(文件名,使用文件方式);
文件的操作方式如下图所示:
文件操作方式
操作方式 属性 操作方式的功能
rrr 只读 为输入打开一个字符文件
www 只写 为输出打开一个字符文件
AaA 追加 向字符文件尾增补数据
rb 只读 为输入 打开一个二进制文件
wb 只写 为输出打开一个二进制文件
ab 追加 向二进制文件尾增补数据
r+ 读写 为读/写打开一个字符文件
w+ 读写 为读/写打开一个新的字符文件
a+ 读写 为读/写打开一个字符文件
rb+ 读写 为读/写打开一个二进制文件
wb+ 读写 为读/写打开一个新的二进制文件
ab+ 读写 为读/写打开一个二进制文件
一般使用如下方法打开一个文件:
if((fp=fopen(″file″,″r″))==NULL)
{ printf(″Cannot open the file!
″); exit(0);
}
也就是当执行fopen函数时,如能顺利打开,则将此文件信息区的起始地址赋给指针变量fp;如打开失败,则fp的值为NULL,在屏幕上显示“Cannot
open the file!”,再执行exit(0)。
3.对打开的文件使用完后用fclose函数关闭它,fclose函数调用的一般形式为:
fclose(文件指针);
§文件的读写,文件的定位
文件打开以后,就可以对它进行读写操作了,对文件的读写可以用以下几个函数实现:
1.fputc函数和fgetc函数
fputc函数把一个字符写到磁盘文件去,其一般形式为:fputc(ch,fp);
其中ch是要输出的字符,它可以是一个字符常量,也可以是一个字符变量。fp是文件指针变量,它从fopen函数得到返回值。
fgetc函数从指定的文件读入一个字符,该文件必须是以读或写方式打开的,其一般调用形式为:ch=fgetc(fp);
fp为文件型指针变量,ch为字符变量。fgetc函数带回一个字符,赋给ch。
2.fread函数和fwrite函数
fread函数可以用来一次读入一组数据。fread函数可以用来一次向文件中写一组数据。它们的调用形式为:
fread(buffer,size,count,fp)
fwrite(buffer,size,count,fp)
其中:buffer是一个指针,size为要读写的字节数,count为要进行读写多少个size字节的数据项,fp为文件型指针。
3.fprintf函数和fscanf函数
fprintf函数、fscanf函数与printf函数、scanf函数作用相似,都是格式化读写函数。只有一点不同:fprintf函数和fscanf函数的读写对象不是终端而是磁盘文件。一般调用形式为:fprintf(文件指针,格式字符串,输出列表);fscanf(文件指针,格式字符串,输出列表);
4.rewind函数和fseek函数
文件中有一个位置指针,指向当前的读写位置。如果顺序读写一个文件,每次读写一个字符,则读写完字符后,该位置指针自动移动指向下一个字符位置。如果想改变这样的规律,强制使位置指针指向其他指定的位置,可以用调用形式rewind和fseek函数。
rewind函数的作用是使位置指针重新返回文件的开头,此函数没有返回值。
fseek函数可以改变文件的位置指针。fseek函数的调用形式为:
fseek(文件类型指针,位移量,起始点);
″起始点″用0,1或2代替,0代表文件开始,1为当前位置,2为文件末尾。″位移量″指以″起始点″为基点,向前移动的字节数。
§文件的定位
文件位置指针是一个形象化的概念,用于表示当前读或写的数据在文件中的位置。
(1)rewind函数
rewind函数又称为“反绕”函数。函数格式:rewind(fp);
函数功能:此函数无返回值,其功能为使文件的位置指针回到fp所指的文件的开头处。
(2)fseek函数
fseek函数用来移动文件位置指针到指定的位置上,使其以后的读写操作从此位置开始。函数格式:fseek(fp,offset,origin);说明:
①fp:为文件指针;
②offset:以字节为单位的位移量,表示以起始点为基点向前移动的字节数,如位移量为负,则表示向后移,这里的向前移是指从文件开头向文件末尾移动的方向;
③origin:起始点,用于指定是以哪个位置为基准,起始点可用标识符来表示,也可用数字来表示。下表列出了代表起始点的标识符及对应的数字。
代表起始点的标识符及对应的数字
起始点标识符 起始点数字 代表的起始点
SEEK.SET 0 文件开始
SEEK.CUR 1 文件当前位置
SEEK.END 2 文件结束
注意:当fseek执行成功时,返回0;当执行失败时,返回非零数