C语言速成手册(五):其它运算符、文件操作、其它函数

条件运算符
    条件运算符的格式如下:
表达式1 ? 表达式2 : 表达式3
    它表示:若表达式1为真(非0),则返回表达式2,否则返回表达式3。

    下面的函数返回两个数中较小的一个:
long min( long a, long b)
{
   return (a<b)?a:b;
}

    很多地方都可能用到条件运算符。再比如求平均数时你可能会这样写:
average = (n>0) ? sum/n : 0;

自加、自减
    a=a+1可以写成a++或++a,a=a-1可以写成a–或–a 。例如,你会经常看到这样的写法:
for ( i=1; i<=10; i++ )
{
}

    如果自加自减不是单独成句,而是放在其它语句中的话,那么a++和++a是有区别的。a++表示“用了后再加”,++a表示“加了后再用”。a–和–a的区别同理。下面的代码输出0 1 1 1。
int a=0;
printf("%d ",a++);
printf("%d ",a);

int b=0;
printf("%d ",++b);
printf("%d",b);

其它运算符
    下面所有的a和b均为整型,则:
C语言  |  Pascal语言
——-+————-
a & b  |  a and b
a | b  |  a or b
a ^ b  |  a xor b
a << b |  a shl b
a >> b |  a shr b

  简写 |    含义
——-+————-
a += b |  a = a + b
a -= b |  a = a – b
a *= b |  a = a * b
a /= b |  a = a / b
a %= b |  a = a % b
a &= b |  a = a & b
a |= b |  a = a | b
a ^= b |  a = a ^ b
a <<= b|  a = a << b
a >>= b|  a = a >> b

各种标准输入输出函数
    下列函数都是stdio.h提供的。stdio.h还提供了一个EOF常量(其实就是-1),用来标识输入数据结束。
         函数                |                    用途
—————————–+———————————————————-
int scanf(str,arg1,…,argn) | 按指定格式读入数据,返回成功被赋值的变量个数。如果已无输入或出错则返回EOF
int printf(str,arg1,…,argn)| 按指定格式输出数据,返回成功输出的字符个数。
int getchar()                | 返回标准输入的下一个字符,如果已无输入或出错则返回EOF。
int putchar(c)               | 向屏幕输出一个字符c,同时返回这个字符,如果出错则返回EOF。
char *gets(str)              | 把这一行输入数据存入字符串str并返回该字符串,如果已无输入或出错则返回NULL
int puts(str)                | 输出字符串并自动输出一个换行符,如果出错则返回EOF。

内存输入输出操作
    stdio.h中提供的sscanf和sprintf两个函数可以把一个字符串当作输入输出对象,其用法与scanf和printf差不多,只不过要多一个参数。你需要把目标字符数组作为函数的第一个参数。
    使用sscanf和sprintf可以方便地进行数字和字符串互转,并实现各种字符串操作。看下面的程序代码:
#include <stdio.h>
int main()
{
    char st[80]="5:43:04";
    int h,m,s;
    
    sscanf(st, "%d:%d:%d", &h, &m, &s);
    printf("Hour:%d Minute:%d Second:%dn", h, m, s);
    
    sprintf(st, "My birthday is %d-%d-%d", 1988, 5, 16);
    printf("%s",st);
    
    return 0;
}

    输出为:
Hour:5 Minute:43 Second:4
My birthday is 1988-5-16

文件输入输出操作
    stdio.h还提供了FILE类型,用于定义文件指针。例如,下面的语句定义了两个待操作的文件:
FILE *in, *out;
    打开一个文件使用fopen函数,该函数的参数为两个字符串。前一个参数指定文件名,后一个参数指定打开模式("r"=读, "w"=写, "a"=在已有文件后继续写 )。函数返回一个文件指针作为此文件的标识供以后使用。

    和文件操作相关的函数有:
         函数                      |                    用途
———————————–+———————————————————-
int fscanf(file,str,arg1,…,argn) | 从file指针对应的文件中读入数据,具体行为同scanf
int fprintf(file,str,arg1,…,argn)| 向file指针对应的文件中输出数据,具体行为同printf
int fgetc(file)                    | 从file指针对应的文件中读入数据,具体行为同getchar
int fputc(c,file)                  | 向file指针对应的文件中输出数据,具体行为同putchar
char *fgets(str,n,file)            | 从file指针对应的文件中读入数据到str字符串,读到第n个字符为止
int fputs(str,file)                | 向file指针对应的文件中输出数据,具体行为同puts
int fflush(file)                   | 立即写入文件,同Pascal中的flush
int feof(file)                     | 文件是否结束,同Pascal中的eof
int fclose(file)                   | 关闭文件,同Pascal中的close

    下面的程序是文件操作版A+B问题,你可以看到文件操作具体的实现方法。
#include <stdio.h>
int main()
{
    FILE *in, *out;
    long a, b;
    
    in = fopen("test.in","r");
    fscanf(in, "%d%d", &a, &b);
    fclose(in);
    
    out = fopen("test.out","w");
    fprintf(out, "%d", a+b);
    fclose(out);
    
    return 0;
}

整型上下限
    Pascal可以用maxlongint来表示longint类型的最大值。C语言中也有类似的定义可以直接使用。使用C语言中的相关定义需要在程序代码前加上#include <limits.h>。

   定义    |    表示
———–+—————————–
CHAR_MAX   | char类型大小上限
CHAR_MIN   | char类型大小下限
SHRT_MAX   | short类型的大小上限
SHRT_MIN   | short类型的大小下限
USHRT_MAX  | unsigned short类型的大小上限
INT_MAX    | int类型的大小上限
INT_MIN    | int类型的大小下限
UINT_MAX   | unsigned int类型的大小上限
LONG_MAX   | long类型的大小上限
LONG_MIN   | long类型的大小下限
ULONG_MAX  | unsigned long类型的大小上限
LLONG_MAX  | long long类型的大小上限
LLONG_MIN  | long long类型的大小下限
ULLONG_MAX | unsigned long long类型的大小上限

常用数学函数
    使用下面的函数需要在程序代码前加上#include <math.h>。

   函数             |     用途
——————–+—————–
double sin(x)       |  正弦
double cos(x)       |  余弦
double tan(x)       |  正切
double asin(x)      |  反正弦
double acos(x)      |  反余弦
double atan(x)      |  反正切
double sqrt(x)      |  开平方
double pow(x,y)     |  x的y次方
double exp(x)       |  e的x次方
double exp2(x)      |  2的x次方
double log(x)       |  以e为底的对数
double log2(x)      |  以2为底的对数
double log10(x)     |  以10为底的对数
double fabs(x)      |  x的绝对值
double fmod(x,y)    |  小数版的mod
double floor(x)     |  小于等于x的最大整数
double ceil(x)      |  大于等于x的最小整数
double trunc(x)     |  舍弃小数部分
double round(x)     |  小数部分四舍五入
long lround(x)      |  小数部分四舍五入,返回long
long long llround(x)|  小数部分四舍五入,返回long long

常用字符串函数
    使用以下函数需要在程序代码前加上#include <string.h>。
    参数s1和s2总是两个字符串,参数c总是一个字符。

    函数               |        用途
———————–+—————————–
int strlen(s1)         | 返回s1的长度
char *strcpy(s1,s2)    | 把s2赋值给s1,返回s1
char *strcat(s1,s2)    | 将s2连接到s1后面,返回s1
int strcmp(s1,s2)      | 比较两字符串,s1小则返回负数,相等返回0,s1大返回正数
char *strchr(s1,c)     | 寻找s1第一次出现c的位置,找到则返回指向该位置的指针,没找到则返回NULL
char *strstr(s1,s2)    | 寻找s2第一次出现在s1的位置,找到则返回指向该位置的指针,没找到则返回NULL
char *strpbrk(s1,s2)   | 寻找s2中的任意字符第一次出现在s1的位置,找到则返回指向该位置的指针,没找到则返回NULL

观察下面的程序:
#include <stdio.h>
#include <string.h>
int main()
{
    char st[80]="matrix67";
    strcat(st,".com");
    printf( "%sn", st );
    printf( "%dn", strlen(st) );
    printf( "%dn", strcmp(st,"my blog") );
    printf( "%cn", *strchr(st,'i') );
    printf( "%sn", strpbrk(st,"3.1415927") );
    printf( "%dn", strstr(st,"x6")-st );

    return 0;
}

输出为:
matrix67.com
12
-1
i
7.com
5

内存操作函数
    下面的一些函数主要用于字符串操作,因此属于string.h 。假设m1和m2都是void *类型。

         函数                |            用途
—————————–+———————————–
int memcmp (m1, m2, n)     |  比较m1和m2的头n个字节,相同返回0,m1小返回负数,m2小返回正数
void *memmove (m1, m2, n)    |  把m2的前n个字节复制到m1的位置,相当于Pascal中的move
void *memset (m1, c, n)      |  把m1的前n个字节全部设为c,相当于Pascal中的fillchar

    下面这段代码的结果是把st字符串变成了"Matrix67, I love UUUUUUUUUUUU…"。
char st[80]="Matrix67, I love someone else...";
memset(st+17,'U',12);

    当然memset更常用的是初始化数组。例如动态规划前初始化f数组:
long f[1000][1000];
memset( f, 0, sizeof(f) );

stdlib.h提供的其它函数
    函数                 |                        用途
————————-+————————————————-
int abs(n)               |  取绝对值,适用于int
long labs(n)             |  取绝对值,适用于long
long long llabs(n)       |  取绝对值,适用于long long
double atof(str)         |  把字符串str转化为数字,返回double类型
int atoi(str)       &n
bsp;    |  把字符串str转化为数字,返回int类型
long atol(str)           |  把字符串str转化为数字,返回long类型
long long atoll(str)     |  把字符串str转化为数字,返回long long类型
void exit(n)             |  退出程序,返回的错误代码为n(0=正常),相当于Pascal的halt
int rand()               |  产生一个随机数,范围在stdlib.h中指定(通常最小为0,最大为int的上限)
void srand(n)            |  设置随机数发生器的种子为n
void qsort(arr,n,size,fn)|  快排,四个参数分别为数组,长度,类型大小,比较函数。

    比较函数的格式应该为
int 函数名(const void *参数1, const void *参数2)
    如果参数1小于参数2,则该函数返回负数,等于则返回0,大于则正数。因此,你可以自己给“大小”下定义。
    下面的程序合法地调用了qsort函数。注意随机函数后的取余运算,这是生成某个范围内的随机数的常用手段。
#include <stdlib.h>
int comp(const void *i, const void *j)
{
    return *(long *)i - *(long *)j;
}

int main()
{
    long n=1000, i, a[n];
    for (i=0; i<n; i++) a[i]=rand()%10000;
    qsort(a, n, sizeof(long), comp);
    return 0;
}

利用assert帮助调试
    assert可以在程序不满足某个条件时输出错误信息并立即终止运行,这对调试很有帮助。使用assert语句需要包含头文件assert.h。观察下面的程序代码:
#include <stdio.h>
#include <assert.h>

int main()
{
    int n;
    scanf("%d",&n);
    assert(n!=0);
    printf("%f",1.0/n);
    return 0;
}

    当读入的数是0时,程序执行printf前就会提前终止,并且输出错误信息。这可以保证后面的语句正常执行,避免异常错误。这显然比用if语句排除异常错误更好一些。在每一个潜在的异常错误前加上assert,当程序出错时你可以很快确定是哪里的问题。

Matrix67原创
转贴请注明出处

C语言速成手册(四):指针、动态内存分配、标准输入

指针的定义
    定义一个指针的方法如下:
类型名 *指针名;
    例如,下面的语句定义了一个指针:
int *pointer;
    这样,pointer就是一个指针,它指向的是一个int类型的数据。

    一个指针可以是一个合法的内存地址,也可以为0(通常写成NULL)。
    你可以用printf语句输出一个指针,对应的标识为"%p"。下面的代码可以输出上面定义的指针指向的地址。
printf("%p",p);

取地址与引用
    假如a是一个变量,p是一个指针,那么&a返回该变量的地址,*p返回该指针所指的内容(称做“引用”)。
    阅读下面的代码片段:
int *p;
int a = 520;
p = &a;
printf( "%p -> %dn", p, *p );
*p = 1314;
printf( "%p -> %dn", p, *p );
printf( "a = %d", a );

    程序输出如下。当执行了p=&a后,存取*p就相当于是存取变量a了。
0022FF78 -> 520
0022FF78 -> 1314
a = 1314

动态内存分配
    首先介绍sizeof函数(准确地说是一个运算符),它的参数为一个变量名或类型名,返回的是它所占内存空间的大小。下面的代码输出1 8 800 4 1 。
long long a;
double b[100];
_Bool *c;
printf( "%d " , sizeof(char) );
printf( "%d " , sizeof(a) );
printf( "%d " , sizeof(b) );
printf( "%d " , sizeof(c) );
printf( "%d " , sizeof(*c) );

    下面介绍四种动态内存分配函数,使用它们前需要在程序最前面包含头文件stdlib.h。四种函数的格式分别为:
void *malloc ( size );
void *calloc ( n, size );
void free ( pointer );
void *realloc( pointer, size );

    函数malloc将在内存里寻找一个大小为size的连续空间,把分配到的内存地址作为一个指向void类型的指针(默认的无类型指针)返回。如果空间分配失败,函数返回NULL。
    函数calloc将在内存里寻找一个大小为n * size的连续空间,并且把这段内存的数据全部清0,返回数据和malloc一样。如果空间分配失败,函数返回NULL。
    函数free用于释放内存空间,释放后的空间被回收,可以用于以后的malloc或calloc操作。
    函数realloc在保证已有数据不变的情况下改变已有指针的空间大小,返回重新分得的空间的内存地址(有可能和原来不同)。如果空间重新分配失败,函数返回NULL。
    Pascal中的new语句可以用前两个函数代替,free语句则相当于Pascal中的dispose。

    注意,malloc和calloc函数所返回的指针还没确定类型,理论上需要用类型转换。下面的程序合法地为p指针分配空间:
int *p;
p = (int *) malloc( sizeof(int) );
*p = 520;

    事实上,由于赋值时C语言自动转换类型,因此那个类型转换是没有必要的(去掉(int *)没有影响)。

指针与结构
    一个指针可以指向一个结构,一个结构也可以包含一个指针。结构里包含一个指向结构的指针就构成了链表:
struct node{
   int value;
   struct node *next;
}

    这样,定义struct node *a,则(*a).next就是另一个指向node结构的指针。在C语言中,(*x).y的句型很常用,因此有一个专门的记号x->y来代替(*x).y这样繁杂的写法。
    你可以从下面的程序中看到链表的使用。
#include <stdio.h>
#include <stdlib.h>
    
struct node
{
   int value;
   struct node *next;
};

int main()
{
    struct node *head = NULL;
    int i;

    for(i=1;i<=10;i=i+1)
    {        
        struct node *newNode;
        newNode = malloc( sizeof(struct node) );
        newNode->value = i;
        newNode->next = head;
        head = newNode;
    }
    
    struct node *p = head;
    while (p)
    {
        printf( "%dn", p->value );
        p = p->next;
    }
    return 0;
}

指针与函数
    前面说过,C语言中的函数参数和变量只能够供该函数使用。
    下面四个程序代码的输出分别是什么?

    代码一:
#include <stdio.h>
void swap( int a, int b )
{
   int c = a;
   a = b;
   b = c;
}
int main()
{
   int a = 520, b = 1314;
   swap( a , b );
   printf( "%d %d", a, b );
   return 0;
}

    代码二:
#include <stdio.h>
int a = 520, b = 1314;
void swap( int a, int b )
{
   int c = a;
   a = b;
   b = c;
}
int main()
{
   swap( a , b );
   printf( "%d %d", a, b );
   return 0;
}

    代码三:
#include <stdio.h>
int a = 520, b = 1314;
void swap()
{
   int c = a;
   a = b;
   b = c;
}
int main()
{
   swap();
   printf( "%d %d", a, b );
   return 0;
}

    代码四:
#include <stdio.h>
void swap( int *a, int *b )
{
   int c = *a;
   *a = *b;
   *b = c;
}
int main()
{
   int a = 520, b = 1314;
   swap( &a, &b);
   printf( "%d %d", a, b );
   return 0;
}

    答案:前两个程序输出520 1314,后两个程序输出1314 520。
    前两个程序中,待交换的两个数(即使是全局变量)作为参数传给了swap函数,该函数里的操作对函数外无影响。
    第三个程序中,swap函数对全局变量直接进行操作,其影响是全局的。
    最后一个程序巧妙地应用了指针来实现两数交换。函数的参数是指针类型,这个函数不能改变指针本身,但可以改变指针所指的内容。这是写此类函数通常所用的方法。
    为了强调函数void swap( int *a, int *b )中的指针本身不发生改变,很多地方喜欢写成void swap(const int *a, const int *b ) 。

指针与数组
    数组由内存的连续空间构成,因此可以用指

C语言速成手册(三):数组、字符串、结构

一维数组的定义、初始化和使用
    定义一个一维数组的格式如下:
类型 数组名[数组大小];
    数组的下标范围总是从0开始(因此下标最大为数组大小减一)。下面一行语句定义了一个大小为10的长整型数组:
long value[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以内的素数:
#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<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(或写成字符'')标记字符串结尾。你可以直接将一个字符串赋给字符数组,也可以在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%cn",a[2]);
printf("%sn",a);
printf("%16sn",a);
printf("%.8sn",a);
printf("%16.8sn",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];
    例如,下面这个语句定义了一个三维数组:
int matrix[100][100][2];
    同样地,每一维的大小都是从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] );
    例如,下面的函数递归地计算行列式:
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的类型名。和变量的定义一样,一个结构的定义只能供当前函数(的当前语句块)中后面的部分使用。因此通常把结构的定义放在所有函数的前面作为一个全局的定义。之后,你便可以写这样的语句:
struct date today;

    结构的使用方法同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} ) );

Matrix67原创
转贴请注明出处

C语言速成手册(二):布尔值、条件判断、循环

逻辑运算符
    作用   |  C  | Pascal
  ———+—–+———–
    等于   | ==  |  =
   不等于  | !=  |  <>
    小于   | <   |  <
    大于   | >   |  >
  小于等于 | <=  |  <=
  大于等于 | >=  |  >=
     且    | &&  |  and
     或    | ||  |  or
     非    |  !  |  not

C语言的if语句
    C语言中if语句的写法如下:
if (表达式)
{
    表达式为真时执行的内容
}

    之前说过,如果执行的内容只有一句话,花括号可以省略。

    下面的语句将返回整数a的绝对值:
if ( a<0 )
   a= -a;

    下面的语句用于实现排序网络中的比较算子:
if ( a>b )
{
   int c=a;
   a=b;
   b=c;
}

     C语言中也有else if和else的用法,但和Pascal不同的是else的前一条语句末要保留分号。下面的语句用于判断三边长分别为a, b, c的三角形是什么三角形:
if ( a+b>c && b+c>a && a+c>b )
{
  if ( a==b && b==c )
    printf("Regular Triangle");
  else if ( a==b || b==c || a==c )
    printf("Isosceles Triangle");
  else
    printf("Scalene Triangle");
}
else
  printf("Triangle Doesn't Exist.");

C语言的bool类型
    C语言中没有布尔类型。任何一个整型的变量都可以充当布尔变量,用0表示False,其它数(默认为1)表示True。
    观察下列代码片断。
printf("%d n", 3+5 == 8);

int a;
a = 3+5==9;
printf("%d n" , a);
a = 100 <= 100;
printf("%d n" , a);

a = 1;
if(a) printf("a is 1n");
a = 2;
if(a) printf("a is 2n");
a = -1;
if(a) printf("a is -1n");
a = 0;
if(a) printf("a is 0n");
if(!a) printf("a is not 0n");

    这段程序的输出为:
1
0
1
a is 1
a is 2
a is -1
a is not 0

    如果觉得别扭,你可以使用_Bool类型。你可以把_Bool看作整型,但它只能储存0和1两个值。
    下面三句话将输出数字1,这是因为_Bool类型把所有非0数都当做是1。
_Bool a;
a = 100;
printf("%d", a);

    如果你想像Pascal一样使用true和false,那么你可以包含头文件stdbool.h。这样你可以定义变量为bool类型并赋值为true或false。例如:
bool a = true;
if (a) printf("a is true");

    下面是两个完整的程序。第一个程序中_Bool可以替换成int。三种写法都正确,你可以选择一种自己喜欢的写法。
#include <stdio.h>
_Bool isNegative ( int a )
{
   if (a<0) return 1;
   else return 0;
}
int main()
{
   if ( isNegative(-100) )
     printf("-100 is Negative");
   return 0;
}

#include <stdio.h>
#include <stdbool.h>
bool isNegative ( int a )
{
   if (a<0) return true;
   else return false;
}
int main()
{
   if ( isNegative(-100) )
     printf("-100 is Negative");
   return 0;
}

C语言的for循环
    for循环使用下面的语句:
for ( 语句1; 语句2; 语句3 )
{
    循环内容
}

    其中语句1用于初始化,若语句2为真(非0)则进入循环;每次循环后将执行语句3,此时若仍然满足语句2则继续循环,否则退出。语句123中,某一种语句可能为空,也可能由多条语句组成(多条语句用逗号隔开)。
    下面的语句打印1到10这10个数。以下所有写法都是正确的。通常用第一种写法。
int i;
for (i=1; i<=10; i=i+1)
   printf("%dn", i );

int i=1;
for ( ; i<=10; i=i+1)
   printf("%dn", i );

int i;
for (i=1; i<=10; )
{
   printf("%dn", i );
   i=i+1;
}

int i;
for (i=1; i<=10; printf("%dn", i ), i=i+1 );

    巧妙地应用for循环可以使某些操作非常简单。下面的函数可以计算n的质因子个数:
long countPrimeFactor( long n )
{
  long d, count;
  for (d=2, count=0; n>1; d=d+1 )
     for ( ; n%d==0; count=count+1, n=n/d );
  return count;
}

    下面的语句将产生死循环:
for ( ; ; ) ;

C语言的while循环
    while循环格式如下:
while ( 循环条件 )
{
   循环内容
}

    下面的代码计算1394和6150的最大公约数:
int a=1394, b=6150;
while ( b )
{
   int tmp = a % b;
   a = b;
   b = tmp;
}
printf("The gcd is %d", a);

do..while循环
    do..while循环的格式如下:
do
{
    循环内容
}
while ( 循环条件 );

    将条件放在循环内容的后面,使程序至少执行循环内容一次。

循环控制
    C语言中使用break语句和continue语句来控制循环,其使用方法与Pascal完全一样。

switch语句
    switch语句相当于Pascal中的case语句。
    下面代码的输出为2 3 default done! , 因为switch语句只负责把程序运行的位置跳转到相应的地方。
int n=2;
switch (n)
{
    case 1: printf("1 ");
    case 2: printf("2 ");
    case 3: printf("3 ");
    default: printf("default ");
}
printf("done!");

    你可以用break语句实现Pascal的case,例如下面的写法就可以让程序输出2 done! :
int n=2;
switch (n)
{
    case 1:
       printf("1 ");
       break;
    case 2:
       printf("2 &
#34;);
       break;
    case 3:
       printf("3 ");
       break;
    default: printf("default ");
}
printf("done!");

goto语句
    你可以在程序的任何位置插入一个标签:
标签名:
    并在同一函数里使用下面的语句实现无条件跳转:
goto 标签名;
    例如,下面的代码将产生死循环,并输出满屏幕的求爱信息。
a: printf(" I Love You !!!!!!! ");
goto a;

Matrix67原创
转贴请注明出处

C语言速成手册(一):基本数据类型、标准输出、函数

语句和语句块
    和Pascal一样,C语言的每条语句以分号结尾。
    和Pascal一样,单词和语句间的空格、回车符对编译没有影响。
    C语言的语句块用花括号标识,也就是说字符 { 相当于Pascal的begin,字符 } 相当于Pascal的end; 。语句块中最后一条语句末尾的分号不能省略。

    如果语句块里只有一条语句,语句块的标识可以省略。这就好像Pascal代码:
for i:=1 to n do
begin
    writeln('I Love You');
end;

    里面的begin和end可以省略一样。

注释的写法
    两种情形被认为是注释。符号// 的后面(只限一行),以及符号 /* 和 */ 之间(可以跨行)。例如:
/*  =====================
    = THIS IS A COMMENT =
    =====================   */

//  This is a comment.

常用数据类型
C                  |  Pascal
——————-+————
short              |  shortint
int                |  integer
long               |  longint
long long          |  int64
unsigned short     |  byte
unsigned int       |  word
unsigned long      |  dword
unsigned long long |  qword
float              |  real
double             |  double
char               |  char

定义变量
    定义变量使用这样的格式:
类型名 变量名1, 变量名2, ... , 变量名n;
    变量名的命名规则与Pascal相同:只能用大小写字母、数字或下划线,第一个字符不用数字。所不同的是,C语言中定义的名称要区分大小写。
    例如,下面的两条语句定义了三个整型变量和一个字符变量。
int a,b,c;
char ch;

    和Pascal不同,变量的声明不一定要在整个代码前。你可以在程序中任意一个地方定义一个新的变量。定义的变量只能在当前函数(中的当前语句块)后面的代码中使用。也就是说,不同函数之间的变量不能混用,在某循环里定义的变量在循环外面是无效的。定义在函数外的变量将作为全局变量供后面的函数使用。

定义常量
    为了告诉编译器某个变量不会改变,你可以在变量定义前加一个const。例如,下面的语句定义了一个常数:
const int maxn=2000;

基本数学运算
  作用   |  C  | Pascal
———+—–+———–
   加    | +   |  +
   减    | –   |  –
   乘    | *   |  *
   除    | /   |  / 或 div
  取余   | %   |  mod

    除法的结果是整数还是小数取决于参与运算的数是整数还是小数。10 / 4等于2,但写成 10.0 / 4 或 10 / 4.0 就得2.5了。

关于字符类型
    C语言中的字符用一对单引号' '标注。例如,'A'表示字符A。
    无法打印或可能产生冲突的字符用反斜杠加一个字符来表示,这叫做转义符。常用转义符如下:
n  换行
t  Tab
a  响铃
"  双引号
'  单引号
?  问号
\  反斜杠

    因此,'''表示一个单引号,虽然它看上去像是两个字符。

    字符类型可以依照ASCII码进行数学运算。例如,字符变量ch可能被赋值为'A'+2(这样ch就等于'C'),而'0'+'1'则等于'a'。

C语言中的赋值
    和Pascal不一样,C语言的赋值只用一个等号,等号前没有冒号。
    声明变量时后面可以跟一个等号赋初始值。
    下面的语句都是合法的:
int a=3, b=106, c=a+b, d;
char ch = c - '0';
d = a * b;

类型转换与“名词动用”
    C语言中,不同类型的变量可以相互赋值,程序将自动转换类型(即使是数字与字符之间)。
    C语言中也存在“使动”用法(类似于Pascal中使用int64(a)扩展变量长度的用法)。假如a是整型,(double)a表示“实数版的a”;假如ch是一个字符,(int)ch就相当于Pascal中的ord(ch)。
    考虑下面的代码:
int a;
int b;
char c;
double d,e;

a = 'z';
b = 20.736;
c = b + 28;
d = a / b;
e = (double) a / b;

    这段代码中,a最终被赋值为122,b得到的值是20,而c的值则为字符'0';d获得的值为6.0,而e的值是6.1 。

标准输出
    输出使用printf函数。printf函数的使用方法如下:
printf (字符串, 待输出表达式1, 待输出表达式2, ... , 待输出表达式n );
    C语言的字符串用一对双引号"  "注明,里面允许有转义符。printf将把字符串输出到屏幕上。字符串中可以有若干个标识,这些标识帮后面待输出的内容“占一个位置”。常用的标识格式为%[x][.y](c|d|f|e),其中x表示输出占的宽度,y表示保留位数;c,d,e,f四个字母表示输出类型,你需要选择一个。c表示输出字符,d(也可以用 i )表示输出整数,f表示输出小数,e用科学计数法表示小数。printf后面的参数依次“填入”这些标识。注意d和e,f不能混用,也就是说,你不能把一个小数输出成整数格式,或把整数输出成小数格式(除非事先转了类型)。
    特别地,%%表示输出一个百分号。

    观察下列代码片段
printf ("Welcome to Matrix67.com n");
printf ( "4 + 5 = %dnand 4 - 5 = %dnn", 4 + 5 , 4 - 5 );

int a = 102;
printf ( "2 * a = %.5dn" , 2 * a );
printf ( "2 * a = %fn", (double) 2 * a );
printf ( "2 * a = %8dn"  , 2 * a );
printf ( "2 * a = %8.5dn", 2 * a );
printf ( "a = '%c'nn", a );

double b = 7;
printf ( 
4;a / b = %fn", a / b );
printf ( "a / b = %en", a / b );
printf ( "a / b = %.9fn", a / b );
printf ( "a / b = %.3fn", a / b );
printf ( "a / b = %8.3fn", a / b );
printf ( "b / a = %.3f%%nn", b / a * 100 );

char c = 'A';
printf ( "c = %cn", c );
printf ( "c = %3cn", c );
printf ( "c = %dn", c );
printf ( "%c%c%c%c%c%c%d.comn",c+12,c+32,c+51,c+49,c+40,c+55,a-35);

    程序输出的结果为:
Welcome to Matrix67.com
4 + 5 = 9
and 4 - 5 = -1

2 * a = 00204
2 * a = 204.000000
2 * a =      204
2 * a =    00204
a = 'f'

a / b = 14.571429
a / b = 1.457143e+001
a / b = 14.571428571
a / b = 14.571
a / b =   14.571
b / a = 6.863%

c = A
c =   A
c = 65
Matrix67.com

函数的定义、返回和调用
    定义一个函数的格式如下:
函数返回类型 函数名( 参数类型1 参数名1, 参数类型2 参数名2, ... , 参数类型n 参数名n )
{
    函数内容
}

    如果某个函数不返回任何数据(相当于Pascal中的“过程”),函数返回类型要写成void。如果不写返回类型,函数默认返回类型为int。
    如果某个函数不带任何参数,参数表一般留空(也可以用一个void代替)。
    为了强调某个参数在整个函数中始终不变,类型前可以标明const。

    函数的返回使用下面的语句:
return 表达式;
    执行这条语句将立即终止该函数的运行。

    下面定义的一个函数可以返回三个数的平均值:
double average (double a , double b , double c)
{
  double sum=a+b+c;
  return sum/3;
}

    C语言也支持函数的内联,方法是在函数返回类型前加inline。例如:
inline double average (double a , double b , double c)
{
  double sum=a+b+c;
  return sum/3;
}

    函数的调用方法和Pascal一样。如果调用函数所带的参数类型和定义的不一样,程序会自动转换类型。下面的语句合法地调用了刚才定义的函数:
num = average( 2, 6.5, 4.23 );
    有一点不同的是,当所调用的函数不带参数时仍然要写括号,例如:
void writeMessage()
{
   printf("Welcome to Matrix67.com");
}

void output()
{
   writeMessage();
}

    C语言同样支持递归调用。由于C语言也只能调用前面定义过的函数,因此C同样需要类似于向前引用的方法。具体方法是把需要提前引用的函数的第一行复制一份提到前面去。下面的两种做法都是正确的,其中第一种方法允许在output函数里调用后面的average,第二种则允许在这句话后面的所有函数中调用它。
void output()
{
   double average (double a , double b , double c);
   printf( "%f",average( 2, 6.5, 4.23 ) );
}
double average (double a , double b , double c)
{
  double sum=a+b+c;
  return sum/3;
}

double average (double a , double b , double c);
void output()
{
   printf( "%f",average( 2, 6.5, 4.23 ) );
}
double average (double a , double b , double c)
{
  double sum=a+b+c;
  return sum/3;
}

    事实上,向前引用时参数名已经没有意义,因此参数名可以省略,直接写成这样:
double average (double , double , double );

    C语言中也允许在函数中定义子函数(函数的嵌套)。标准的C语言不支持在函数中定义子函数(函数的嵌套),虽然某些编译器可能支持。

一个完整的程序代码的构成
    代码前几行用于包含头文件,每行包含一个,格式如下:
#include <头文件名>
    常用的头文件有stdlib.h, stdio.h, string.h, math.h等等,分别提供一些常用函数、输入输出函数、字符串函数和数学相关函数。注意我们之前用的printf函数是属于stdio.h里的,因此要使用该函数必须在代码最开头加入#include <stdio.h>。
    接下来是若干个函数。这些函数里必须有一个名为main的函数,它返回的值是一个int类型,代表程序的退出代码(0=正常退出)。程序会自动寻找这个函数作为主函数执行。

    下面的代码是一个完整的C程序代码,这是我们的第一个完整的程序代码:
#include <stdio.h>

double average (double a , double b , double c)
{
  double sum=a+b+c;
  return sum/3;
}

int main()
{
   double a=24;
   double b=102;
   double c=77;
   printf("Matrix%d.com原创n转贴请注明出处", (int)average(a,b,c));

   return 0;
}