Via http://www.matrix67.com/blog/archives/218 by Matrix67
目录:
C语言速成手册(一):基本数据类型、标准输出、函数
C语言速成手册(二):布尔值、条件判断、循环
C语言速成手册(三):数组、字符串、结构
C语言速成手册(四):指针、动态内存分配、标准输入
C语言速成手册(五):其它运算符、文件操作、其它函数
C语言速成手册(六):其它问题、后记
一维数组的定义、初始化和使用
定义一个一维数组的格式如下:
数组的下标范围总是从0开始(因此下标最大为数组大小减一)。下面一行语句定义了一个大小为10的长整型数组:
这相当于下面的Pascal语句:
|
var value:array[0..9]of longint; |
C语言的数组范围左端点不能自定义,它的数组下标只能从0开始。
下面几种方式可以在定义数组的同时进行初始化:
|
long value[10] = { 0, 8, 2, 0, 3 } ; long value[10] = { [1]=8, [2]=2, [4]=3 } ; |
上面两个语句是等价的。其中前一种方法依次对数组的前5个数进行初始赋值,后一种方法仅对数组的其中三个位置进行初始化。初始化中没有涉及到的下标所对应的数值自动被设为0。
C语言允许数组的大小为一个变量,但这样的数组在定义时不能像上面一样进行初始化。
这种初始化方法只在定义数组时用,不能用于程序中的赋值。数组之间也不能直接赋值,你不能把数组a整个赋值给数组b。
程序中使用数组的方法和Pascal一样。下面的程序将输出1000以内的素数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
#include <stdio.h> #include <stdbool.h> int main() { bool isPrime[1000]; int i,j; for(i=2;i<1000;i=i+1) isPrime[i]=true; for(i=2;i<1000;i=i+1) { if (isPrime[i]) { printf("%d ",i); for(j=2*i;j<1000;j=j+i) isPrime[j]=false; } } return 0; } |
当一维数组作为函数参数时,数组大小可以不写,以适应不同长度的数组。通常你还需要另一个参数告诉函数你的数组有多大。下面这个函数返回数组中的最大值:
|
long maxValue ( long length, long array[] ) { long i, max = 0; for ( i=0; i&lt;length; i=i+1) if (array[i]>max) max = array[i]; return max; } |
下面的代码合法地调用了上面的函数。
|
long a[5] = { 1, 5, 7, 3, 4 }; printf( "%d" , maxValue( 5,a ) ); |
C语言中的字符串
C语言也使用字符数组作为字符串。定义一个char a[20],就相当于定义了一个长度不超过20的字符串。Pascal中使用a[0]记录字符串的长度,字符串内容从a[1]开始;但C语言并不直接记录字符串长度,a[0]表示字符串的第一个字符,最后以ASCII码0(或写成字符'\0')标记字符串结尾。你可以直接将一个字符串赋给字符数组,也可以在printf中使用%s标识输出一个字符数组。记住,a[2]表示字符串中的第三个字符,因为C的数组下标是从0开始的。
观察下列代码:
|
int i; char a[20]="matrix67.com"; for (i=0;i<20;i=i+1) printf("%d ",a[i]); printf("\n%c\n",a[2]); printf("%s\n",a); printf("%16s\n",a); printf("%.8s\n",a); printf("%16.8s\n",a); |
程序的输出为:
|
109 97 116 114 105 120 54 55 46 99 111 109 0 0 0 0 0 0 0 0 t matrix67.com matrix67.com matrix67 matrix67 |
== 或 + 等运算符对字符串无效。
下面的函数返回字符串的字符数:
|
int stringLength( char a[] ) { int count=0; while ( a[count] ) count=count+1; return count; } |
赋值时,如果字符串太长了,有两种方法可以让你分行写。一是在行末加一个反斜杠表示和下一行相连,二是每一行都用双引号注明(编译器会自动把它连接起来)。下面两个代码是等价的。注意第一个代码中的第二行必须顶格写,否则第二行前面的空格也要算进字符串里。
|
char blogTitle[100]="Matrix67: My Blog - 50% Informatics, 50% \ Mathematics, and 50% Imagination"; char blogTitle[100]="Matrix67: My Blog - 50% Informatics, 50% " "Mathematics, and 50% Imagination"; |
和数组一样,对字符串的赋值只能在定义时使用,程序中不能这样做。
多维数组的定义、初始化和使用
定义一个多维数组的格式如下:
|
类型 数组名[大小1][大小2]...[大小n]; |
例如,下面这个语句定义了一个三维数组:
同样地,每一维的大小都是从0开始算的。因此上面的语句相当于Pascal中的:
|
var matrix:array[0..99][0.99][0..1]of integer; |
多维数组的初始化和一维数组类似。例如,我们经常需要定义方向常量:
|
const int dir[4][2] = { {1,0},{0,1},{-1,0},{0,-1} }; |
这还可以直接写成:
|
const int dir[4][2] = { 1, 0, 0, 1, -1, 0, 0, -1 }; |
多维数组的初始化同样是未定义者自动填零,因此还可以写成:
|
const int dir[4][2] = { [0][0]=1, [1][1]=1, [2][0]=-1, [3][1]=-1 }; |
程序中使用多维数组时必须用多个方括号,即dir[2][1]不能写成dir[2,1]。
当多维数组作为函数的参数时,只有第一维的大小可以不写。因此,下面的三个函数中前两个是合法的,第三个是不合法的。
|
long function_1( int m, int n, long a[20][20] ); long function_2( int m, int n, long a[][20] ); long function_3( int m, int n, long a[][] ); |
为了让参数仍然适用于各种大小的数组,C语言允许这样定义函数:
|
long function_4( int m, int n, long[m][n] ); |
例如,下面的函数递归地计算行列式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
long determinant( int n, long a[n][n] ) { if (n==1) return(a[0][0]); int i,j,k; long ans = 0; long sub[n-1][n-1]; for ( i=0; i<n; i=i+1 ) { for ( j=1; j<n; j=j+1 ) { for ( k=0; k<i; k=k+1 ) sub[j-1][k]=a[j][k]; for ( k=i+1; k<n; k=k+1 ) sub[j-1][k-1]=a[j][k]; } ans = ans + (1-i%2*2)*a[0][i]*determinant(n-1, sub); } return ans; } |
下面的代码片段正确地调用了上面的函数:
|
long a[4][4]={ { 1, 4, -1, 4 }, { 2, 1, 4, 3 }, { 4, 2, 3, 11 }, { 3, 0, 9, 2 } }; printf( "%d" , determinant(4,a) ); |
结构的定义、初始化和使用
Pascal中的记录类型在C语言中叫做“结构”。定义一个结构的方式如下:
|
struct 结构名 { 在此定义若干变量(域) }; |
注意花括号后面需要有一个分号。下面定义一个date结构:
|
struct date { int year; short month,day; }; |
这样你就获得了一个名为struct date的类型名。和变量的定义一样,一个结构的定义只能供当前函数(的当前语句块)中后面的部分使用。因此通常把结构的定义放在所有函数的前面作为一个全局的定义。之后,你便可以写这样的语句:
结构的使用方法同Pascal的记录类型一样。例如,下面的函数用于计算某一天是星期几(Zeller公式):
|
int zeller( struct date t ) { if (t.month<3) { t.year = t.year - 1; t.month = t.month + 12; } int c = t.year / 100; int y = t.year % 100; int ans = ( c/4 - 2*c + y + y/4 + (26*(t.month+1))/10 + t.day - 1 ) % 7; if (ans>0) return ans; else return ans+7; } |
给一个结构赋初始值和数组的初始化差不多。下面两个语句是等价的:
|
struct date myBirthday = { 1988, 5, 16 }; struct date myBirthday = { .year=1988, .month=5, .day=16 }; |
这种方法也可以用于程序中的赋值操作,但需要加上一个类型转换(见这里的“名词动用”一节)。例如,下面三个代码片段都是等价的:
|
myBirthday.year = 1988; myBirthday.month = 5; myBirthday.day = 16; myBirthday = (struct date){ .year=1988, .month=5, .day=16 }; myBirthday = (struct date){ 1988, 5, 16 }; |
下面的语句调用了zeller函数,输出自1583年来的每个13日都是星期几。和本文无关的问题:有人知道为什么我从1583年开始算么?
|
int y,m; for ( y=1583; y<=2000; y=y+1) for ( m=1; m<=12; m=m+1 ) printf( "%d ", zeller( (struct date){y,m,13} ) ); |