#include <stdio.h>
#define IN 1 /* 在单词内 */
#define OUT 0 /* 在单词外 */
/* 统计输入的行数,单词数与字符数 假设单词之间的分隔符只有空格,制表符,换行符 */
int main (){
int c,nl,nw,nc,state;
state = OUT;
nl = nw = nc = 0;
while ((c = getchar()) != EOF) {
++nc;
if (c == '\n') {
++nl;
}
if (c == ' ' || c =='\n' || c == '\t') {
state = OUT;
}else if (state == OUT) {
state = IN;
++nw;
}
}
printf ("%d %d %d\n",nl,nw,nc);
return 0;
}
#include <stdio.h>
#define MAXLINE 1000 /*允许的输入行的最大长度 */
/* 对函数进行声明 */
int getline (char line[],int maxline);
void copy (char to[],char from[]);
/* 打印出最长的输入行 */
int main (){
int len; /* 当前行长度 */
int max; /* 目前为止发现的最长行的长度 */
char line[MAXLINE]; /* 当前的输入行 */
char longest[MAXLINE]; /* 用于保存最长的行 */
max = 0;
while ((len = getline(line,MAXLINE)) > 0) {
if (len > max) {
max = len;
copy(longest,line);
}
}
if (max >0) { /* 存在这样的行 */
printf ("%s",longest);
}
return 0;
}
/* getline函数:将一行读到s中并返回其长度 */
int getline (char s[],int lim) {
int c,i;
for (i = 0;i < lim - 1 && (c = getchar()) != EOF && c != '\n';++i) {
s[i] = c;
}
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
/* copy函数:将from复制到to;这里假定to足够大 */
void copy (char to[],char from[]) {
int i;
/* for (i = 0;to[i] = from[i],from[i] != '\0';i++); */
i = 0;
while ((to[i] = from[i]) != '\0') {
++i;
}
}
在上面的getline函数中声明的变量i与copy函数中声明的变量i没有关系。函数中的每个局部变量只在函数被调用时存在,在函数执行完毕退出时消失。这也是其他语言通常把这类变量成为自动变量的原因。以后我们使用“自动变量”代表“局部变量”。
除自动变量外,还可以定义位于所有函数外部的变量,也就是说,在所有函数中都可以通过变量名访问这种类型的变量。
由于外部变量可以在全局范围内访问,因此,函数间可以通过外部变量交换数据,而不必使用参数表。再者,外部变量在程序执行期间一直存在,
而不是在函数调用时产生,在函数执行完毕时消失。
外部函数必须定义在所有函数之外,且只能定义一次,定义后编译程序将为它分配存储单元。在每个需要访问外部变量的函数中,必须声明相应的外部变量,此时说明其类型。声明时可以用extern语句显示声明,也可以通过上下文隐式声明。
#include <stdio.h>
#define MAXLINE 1000 /*允许的输入行的最大长度 */
int max; /* 目前为止发现的最长行的长度 */
char line[MAXLINE]; /* 当前的输入行 */
char longest[MAXLINE]; /* 用于保存最长的行 */
/* 对函数进行声明 */
int getline ();
void copy ();
/* 打印出最长的输入行 */
int main (){
int len; /* 当前行长度 */
extern int max;
extern char longest[];
while ((len = getline()) > 0) {
if (len > max) {
max = len;
copy();
}
}
if (max >0) { /* 存在这样的行 */
printf ("%s",longest);
}
len = '\0';
return 0;
}
/* getline函数:将一行读到s中并返回其长度 */
int getline (void) {
int c,i;
extern char line[];
for (i = 0;i < MAXLINE - 1 && (c = getchar()) != EOF && c != '\n';++i) {
line[i] = c;
}
if (c == '\n') {
line[i] = c;
++i;
}
line[i] = '\0';
return i;
}
/* copy函数:将from复制到to;这里假定to足够大 */
void copy () {
int i;
extern char line[],longest[];
/* for (i = 0;to[i] = from[i],from[i] != '\0';i++); */
i = 0;
while ((longest[i] = line[i]) != '\0') {
++i;
}
}
tips:我们在讨论外部变量时谨慎地使用了定义(define)与声明(declaration)这两个词。“定义”表示创建变量或分配存储单元,而“声明”指的是说明变量的性质,但并不分配存储单元。
常量表达式是仅仅只包含常量的表达式。这种表达式在编译时求值,而不在运行时求值。
字符串常量也叫字符串字面值,是用双引号括号起来的0个或多个字符组成的字符序列。 “I am a string” 或 “”/* 空字符 */,从技术角度看,字符串常量就是字符数组。
strlen:
#include <stdio.h>
int strlen (char s[]);
int main (){
char *s = "demo";
int length = strlen(s);
printf ("%d",length);
return 0;
}
int strlen (char s[]) {
int i = 0;
while (s[i] != '\0') {
++i;
}
return i;
}
字符常量与仅仅包含一个字符的字符串之间的区别:‘x’与“x”是不同的。
前者是一个整数,其值是字母x在机器字符集中对应的数值(内部表示值);后者是一个包含一个字符(即字母x)以及一个结束符‘\0’的字符数组。
枚举常量是另外一种类型的常量。枚举是一个常量整形值的列表。
enum boolean {NO,YES};
在没有显示说明情况下,enum类型中第一个枚举名的值为0,第二个为1,依次类推。如果只指定了部分枚举名的值,那么未指定值的枚举名的值
将依着最后一个指定值向后递增。
int isLeapYear(int year){
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
return 1;
}
return 0;
}
strcat:
#include <stdio.h>
void strcat(char s[],char t[]);
/* 统计输入的行数,单词数与字符数 */
int main (){
char s[30],t[10] = "abcdef";
// s[0] = '\0';
strcat(s,t);
printf ("%s\n",s);
return 0;
}
/* strcat函数:将字符串t连接到字符串s的尾部;s必须有足够大的空间 */
void strcat (char s[],char t[]) {
int i,j;
i = j = 0;
while (s[i] != '\0') i++; /* 判断是否为字符串s的尾部 */
while ((s[i++] = t[j++]) != '\0'); /* 拷贝t */
}
#include <stdio.h>
/* squeeze函数:从字符串s中删除字符c */
void squeeze (char s[],int c);
int main (){
char ch[] = "abaacccca";
squeeze(ch,'a');
printf ("%s\n",ch);
return 0;
}
void squeeze (char s[],int c) {
int i,j;
for (i = j = 0;s[i] != '\0';++i) {
if (s[i] != c) {
s[j++] = s[i];
}
}
s[j] = '\0';
}
按位运算符
c语言提供了6个位操作运算符。这些运算符只能作用域整形操作数,即只能作用于带符号或无符号的char,short,int与long类型。
&按位与(AND)
|按位或(OR)
^按位异或(XOR)
<<左移
>>右移
~按位求反
按位运算符&经常用于屏蔽某些二进制位,eg:n = n & 0177 该语句将n中除7个低二进制位外的其他各位均值为0。
按位或运算符|常用于将某些二进制位置为1:x = x | SET_ON;
移位运算符<<与>>分别用于将运算的左操作数左移与右移,移动的位数则由右操作数指定(右操作数的值必须是非负值)。
表达式x << 2将x的值左移2,右边空出的2位用0填补,该表达式等价于对左操作数乘以4。
tips:在对unsigned类型的无符号值进行右移位时,左边空出的部分将用0填补;当对signed类型的带符号值进行右移时,某些机器将对左边空出的部分用符号位填补(即 “算术移位”),而另一些机器则对左边空出的部分用0填补(即“逻辑移位”)。
x = x & (~077); /* 将把x的最后6为设置为0。注意,表达式x&~077与机器字长无关*/
getbits(x,p,n),它返回x中右边数第p位开始向右数n位的字段。这里假定最右边的一位是第0位。n与p都是合理的正值。
getbits(x,4,3)返回x中第4,3,2三位的值;
unsigned getbits (unsigned x,int p,int n) {
return (x >> (p + 1 - n)) & ~(~0 << n);
}
其中表达式x>>(p+1-n)将期望得的字段移位到字的最右端。~0的所有位都为1,这里使用语句~0<<n将~0左移n位,并将最右边的n位用0填补。再使用~运算对它取反,这样就建立了最右边n位全为1的屏蔽码。
/* bitcount函数:统计x中值为1的二进制位数。
* 这里将x声明为无符号类型是为了保证将x右移,无论该程序在什么机器上运行,左边空出的位都用0(而不是符号位)填补。
*/
int bitcount (unsigned x) {
int b = 0;
for (b = 0; x != 0;x >>= 1) {
if (x & 01) {
b++;
}
}
return b;
}
二分查找:
int binsearch (int x,int v[],int n) {
int low,high,mid;
low = 0;
high = n - 1;
while (low <= high) {
mid = (low + high) / 2;
if (x < v[mid]) {
high = mid - 1;
}else if (x > v[mid]) {
low = mid + 1;
}else { /* 找到匹配的值 */
return mid;
}
}
return -1; /* 没有匹配的值 */
}
字符串转化为整形:
int atoi (char s[]) {
int i,n,sign;
for (i = 0;isspace(s[i]);i++); /* 跳过空白符 */
sign = s[i] == '-' ? -1 : 1;
if (s[i] == '-' || s[i] == '+') {/* 跳过符号 */
i++;
}
for (n = 0;isdigit(s[i]);i++) {
n = 10 * n + (s[i] - '0');
}
return sign * n;
}
反转字符串:
void reverse (char s[]) {
int c,i,j;
for (i = 0,j = strlen(s) - 1;i < j;i++,j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
将数字转化成字符串
#include <stdio.h>
#include <ctype.h>
#include <string.h>
void reverse(char s[]);
void itoa(int,char[]);
int main (){
int a = -678;
char ch[10];
itoa(a,ch);
printf ("%s\n",ch);
return 0;
}
/* itoa函数:将数字n转换为字符串并保存到s中 */
void itoa (int n,char s[]) {
int i,sign;
if ((sign = n) < 0) { /* 记录符号 */
n = -n; /* 使n成为正数 */
}
i = 0;
do { /*以反序生成数字*/
s[i++] = n % 10 + '0';
}while((n /= 10) > 0);
if (sign < 0) {
s[i++] = '-';
}
s[i] = '\0';
reverse(s);
}
void reverse (char s[]) {
int c,i,j;
for (i = 0,j = strlen(s) - 1;i < j;i++,j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
/* trim函数:删除字符串尾部的空格符,制表符与换行符*/
int trim (char ch[]) {
int n;
for (n = strlen(ch) - 1; n >= 0;n-- ) {
if (ch[n] != ' ' && ch[n] != '\t' && ch[n] != '\n' ) {
break;
}
}
ch[n+1] = '\0';
return n;
}
找出所有与模式匹配的行
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define MAXLINE 1000 /*最大输入行长度*/
int getline(char line[],int max);
int strindex(char source[],char searchfor[]);
char pattern[] ="ould"; /*待查找的模式*/
/*找出所有与模式匹配的行*/
int main (){
char line[MAXLINE];
int found = 0;
while (getline(line,MAXLINE) > 0) {
if (strindex(line,pattern) >= 0) {
printf ("%s",line);
found++;
}
}
return found;
}
/*getline函数:将行保存在s中,并返回该行的长度*/
int getline (char s[],int lim) {
int c,i;
i = 0;
while (--lim > 0 && (c = getchar()) != EOF && c != '\n') {
s[i++] = c;
}
if (c == '\n') {
s[i++] = c;
}
s[i] = '\0';
return i;
}
/*strindex函数:返回t在s中的位置,若未找到返回-1*/
int strindex (char s[],char t[]) {
int i,j,k;
for (i = 0;s[i] != '\0';i++) {
for (j = i,k=0;t[k] != '\0' && s[j] == t[k];j++,k++);
if (k > 0 && t[k] == '\0') {
return i;
}
}
return -1;
}
/* atof函数:把字符串s转化为相应的双精度浮点数 */
double atof (char s[]) {
double val,power;
int i,sign;
for (i = 0;isspace(s[i]);i++); /* 跳过空白符 */
sign = (s[i] == '-') ? -1 : 1;
if (s[i] == '+' || s[i] == '-') {
i++;
}
for (val = 0.0;isdigit(s[i]);i++) {
val = 10.0 * val + (s[i] - '0');
}
if (s[i] == '.') {
i++;
}
for (power = 1.0;isdigit(s[i]);i++) {
val = 10.0 * val + (s[i] - '0');
power *= 10.0;
}
return sign * val / power;
}
c语言中的作用域规则
构成c语言中的函数与外部的变量可以分开进行编译。一个程序可以存放在几个文件中,
原先已编译过的函数可以从库中进行加载。我们感兴趣的问题是:
1:如何进行声明才能确保变量在编译时被正确声明?
2:如何安排声明的位置才能确保程序在加载时各部分正确连接?
3:如何组织程序中的声明才能确保只有一份副本?
4:如果初始化外部变量?
名字的作用域指的是程序中可以使用该名字的部分。对于在函数开头声明的自动变量来说,其作用域是声明该变量名的函数。
不同函数中声明的具有相同名字的各个局部变量之间没有任何的关系。
外部变量或函数的作用域从声明它的地方开始,到其所在的(待编译的)文件的末尾结束。如:如果main,sp,val,push与pos是依次定义在某个文件中的5个函数或外部变量,如下:
main(){........}
int sp = 0;
double val[MAXVAL];
void push(double f){......}
double pop(void){....}
那么,在push与pop中这两个函数中不需要进行任何声明就可以通过名字访问变量sp与val,但是,这二个变量不能用在
main函数中,push与pop函数也不能用在main函数中。
意外一方面,如果要在外部变量的定义之前使用该变量,或者外部变量的定义与变量的使用不在同一个源文件中,则必须在
相应的变量声明中强制性使用关键字extern。
将外部变量的声明与定义严格区分开来很重要。变量声明用于说明变量的属性(主要是变量的类型),而变量的定义除此以外还将引起储存器的分配。
如果将下列语句放在所有函数的外部:
int sp;
double val[MAXVAL];
那么这两条语句将定义外部变量sp与val,并为之分配存储单元,同时这两条语句还可以作为该源文件中其余部分的声明。
而下面的两行语句:
extern int sp;
extern double val[];
为源文件中的其余部分声明了一个int类型的外部变量sp以及一个double数组类型的外部变量val(该数组的长度在其他的地方确定),
但这两个声明并没有建立变量或为它们分配存储单元。
在一个源程序中所有源文件中,一个外部变量只能在某个文件中定义一次,而其他文件可以通过extern声明来访问它(定义外部变量的源文件中也可以包含对该外部变量的extern声明)。外部变量的定义中必须指定数组的长度,但extern声明则不一定要制定数组的长度。
tips:外部变量的初始化只能出现在其定义中。
假定函数push与pop定义在一个文件中,而变量val与sp在另外一个文件中定义并初始化(通常不大可能这样组织程序),则需要通过下面这些定义与声明把这些函数和变量“绑定”在一起:
在文件file1中:
extern int sp;
extern double val[];
void push(double f){.....}
double pop(void){......}
在文件file2中:
int sp = 0;
double val[MAXVAL];
由于文件中file1中的extern声明不仅在函数定义的外面,而且还放在它们的前面,因此它们适用于该文件中的所有函数。对于file1,这样一组声明就够了如果在同一个文件中先使用,后定义变量sp与val,也需要按照这种方式来组织文件。
头文件
对于下面一个计算器的程序:
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#define MAXOP 100 /*操作数或运算符的最大长度*/
#define NUMBER '0' /*表示找到一个数*/
int getop(char[]);
void push(double);
double pop(void);
/*逆波兰计算器 */
int main (){
int type;
double op2;
char s[MAXOP];
while ((type = getop(s)) != EOF) {
switch (type) {
case NUMBER:
push(atof(s));/*atof函数用作将s转发为浮点数*/
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '/':
op2 = pop();
if (op2 != 0.0) {
push(pop() / op2);
}else {
printf ("error:zero divisor\n");
}
break;
case '\n':
printf ("\t%.8g\n",pop());
break;
default:
printf ("error:unknown command %s\n",s);
break;
}
}
return 0;
}
#define MAXVAL 100 /*栈val的最大深度*/
int sp = 0;/*下一个空闲栈的位置*/
double val[MAXVAL]; /*值栈*/
/*push函数:把f压入到值栈中*/
void push(double f) {
if (sp < MAXVAL) {
val[sp++] = f;
}else {
printf("error:stack full,can't push %g\n",f);
}
}
/*pop函数:弹出并返回栈顶的值*/
double pop(void) {
if (sp > 0) {
return val[--sp];
}else {
printf ("error:stack empty\n");
return 0.0;
}
}
#include <ctype.h>
int getch (void);
void ungetch(int);
/*getop函数:获取下一个运算符或数值操作数*/
int getop (char s[]) {
int i,c;
while ((s[0] = c = getch()) == ' ' || c == '\t');
s[1] = '\0';
if (!isdigit(c) && c != '.') {
return c;
}
i = 0;
if (isdigit(c)) { /* 收集整数部分 */
while (isdigit(s[++i] = c = getch()));
}
if (c == '.') {/*收集小数部分*/
while (isgitis(s[++i] = c = getch()));
}
s[i] = '\0';
if (c != EOF) {
ungetch(c);
}
return NUMBER;
}
#define BUFSIZE 100
char buf[BUFSIZE]; /*用于ungetch函数的缓冲区*/
int bufp = 0; /* buf中下一个空闲的位置*/
int getch(void) { /*取下一个字符(可能是压回的字符)*/
return (bufp >0) ? buf[--buf] : getchar();
}
void ungetch (int c) { /*把字符压回到输入中*/
if (bufp >= BUFSIZE) {
printf ("ungetch:too many character\n");
}else {
buf[bufp++] = c;
}
}
下面将上面的计算器程序分割到若干个源文件中的情况。
如果该程序的各种组成部分很长,这么做还是有必要的。
分割:
将主函数main单独放在main.c中;
将push与pop函数以及它们使用的外部变量放在第二个文件stack.c中;
将getop函数放在第三个文件getop.c中;
将getch与ungetch函数放在第四个文件getch.c中。
之所以分割为多个文件,主要考虑在实际的程序中,它们分别来自于单独编译的库。
此外,还必须考虑定义和声明在这些文件之间的共享问题。我们尽可能把共享的部分集中在一起,
这样就只需要一个副本,改进程序时也容易保证程序的正确性。我们把这些公共部分放在calc.h中,在需要使用该都文件时通过
#include指令将它包含进来。这样分割后,程序的形式如下:
我们对下面两个因素进行了折衷:一方面是我们期望每个文件只能访问它完成任务所需要的信息。
另外一方面是现实中维护较多的头文件比较的困难。我们可以得出这样一个结论:
对于某些中等规模的程序,最好只用一个头文件存放程序中各部分共享的对象。较大的程序需要使用更多的头文件,我们需要精心的组织它们。
最后形成的代码为:
calc.h
#define NUMBER '0'
void push(double);
double pop(void);
int getop(char[]);
int getch(void);
void ungetch(int);
getop.c
#include <stdio.h>
#include <ctype.h>
#include "calc.h"
/*getop函数:获取下一个运算符或数值操作数*/
int getop (char s[]) {
int i,c;
while ((s[0] = c = getch()) == ' ' || c == '\t');
s[1] = '\0';
if (!isdigit(c) && c != '.') {
return c;
}
i = 0;
if (isdigit(c)) { /* 收集整数部分 */
while (isdigit(s[++i] = c = getch()));
}
if (c == '.') {/*收集小数部分*/
while (isdigit(s[++i] = c = getch()));
}
s[i] = '\0';
if (c != EOF) {
ungetch(c);
}
return NUMBER;
}
stack.c
#include <stdio.h>
#define MAXVAL 100 /*栈val的最大深度*/
int sp = 0;/*下一个空闲栈的位置*/
double val[MAXVAL]; /*值栈*/
/*push函数:把f压入到值栈中*/
void push(double f) {
if (sp < MAXVAL) {
val[sp++] = f;
}else {
printf("error:stack full,can't push %g\n",f);
}
}
/*pop函数:弹出并返回栈顶的值*/
double pop(void) {
if (sp > 0) {
return val[--sp];
}else {
printf ("error:stack empty\n");
return 0.0;
}
}
getch.c
#include <stdio.h>
#define BUFSIZE 100
char buf[BUFSIZE]; /*用于ungetch函数的缓冲区*/
int bufp = 0; /* buf中下一个空闲的位置*/
int getch(void) { /*取下一个字符(可能是压回的字符)*/
return (bufp >0) ? buf[--bufp] : getchar();
}
void ungetch (int c) { /*把字符压回到输入中*/
if (bufp >= BUFSIZE) {
printf ("ungetch:too many character\n");
}else {
buf[bufp++] = c;
}
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include "calc.h"
#define MAXOP 100 /*操作数或运算符的最大长度*/
/*逆波兰计算器 */
int main (){
int type;
double op2;
char s[MAXOP];
while ((type = getop(s)) != EOF) {
switch (type) {
case NUMBER:
push(atof(s));/*atof函数用作将s转发为浮点数*/
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '/':
op2 = pop();
if (op2 != 0.0) {
push(pop() / op2);
}else {
printf ("error:zero divisor\n");
}
break;
case '\n':
printf ("\t%.8g\n",pop());
break;
default:
printf ("error:unknown command %s\n",s);
break;
}
}
return 0;
}
静态变量
某些变量,比如文件stack.c中定义的变量sp与val以及文件getch.c中定义的变量buf与bufp,它们仅仅供其所在的源文件中的函数使用,其他函数不能访问。用static声明外部变量与函数,可以将其后声明的对象的作用域限定为被编译源文件的剩余部分。通过static限定外部对象,可以达到隐藏外部对象的目的,如,getch-ungetch复合结构需要共享buf与bufp两个变量,这样buf与bufp必须是外部变量,但这两个对象不应该被getch与ungetch函数的调用者所访问。
要将对象指定为静态存储,可以在正常的对象声明之前加上关键字static作为前缀。如果把上述两个函数和两个变量放在一个文件中编译。如:
static char buf[BUFFSIZE] /*ungetch函数使用的缓冲区*/;
static int bufp = 0; /*缓冲区buf的下一个空闲位置*/
int getch(void){.....}
void ungetch(int c){...}
那么其他函数就不能访问变量buf和bufp,因此这两个名字不会和同一程序中的其他文件中的相同的名字相冲突。同样,可以通过把变量sp与val声明为静态类型隐藏这两个由执行栈操作的push和pop函数使用的变量。
外部的static声明通常多用于变量,当然,它也可用于声明函数。通常情况下,函数名字是全局可访问的,对于整个程序的各个部分而言都可见。
但是把函数声明为static类型,则函数名除了对该函数声明所在的文件可见外,其它文件都无法访问。
static也可用于声明内部变量。static类型的内部变量同自动变量一样,都某个特定函数的局部变量,只能在函数中使用,但它与自动变量不同的是,不管在所在函数是否被调用,它一直存在,而不像自动变量那样,随着所在函数的被调用和推出而存在和消失。换句话说,static类型的内部变量是一种只能在某个特定函数中使用的但一直占据存储空间的变量。
初始化
在不进行显示初始化的情况下,外部变量和静态变量都将被初始化为0,而自动变量和寄存器变量的初始值则没有定义。
对于外部变量与静态变量来说,初始化表达式必须是常量表达式,且只初始化一次。(从概念上讲是在程序开始执行前进行初始化)。
数组的初始化可以在声明的后面紧跟一个初始化表达式列表,初始化表达式列表用花括号括起来,各初始化表达式之间通过逗号分隔。如要用一年中各月的天数初始化数组days,其变量的定义如下:
int days[] = {31,28,31,30,31,30,31,31,30,31,30,31};
当省略数组的长度时,编译器将把花括号中初始化表达式的个数为数组的长度。
如果初始化表达式的个数比数组元素少,则对外部变量,静态变量和自动变量来说,没有被初始化的表达式的元素被初始化为0。
字符数组的初始化比较的特殊:可以用一个字符串来代替花括号括起来并用逗号分隔的初始化表达序列。
如:
char pattern[] = “ould”;
它同下面的声明是等价的:
char pattern[] = {‘o’,’u’,’l’,’d’,’\0’};
c预处理器
c语言通过预处理器提供了一些语言功能。
两个最常用的预处理器指令为:#include指令(用在编译期间把指定文件的内容包含进当前文件中)和#define指令(用任意字符序列替代一个标记)。
条件包含
还可以使用条件语句对预处理本身进行控制,这种条件语句的值是在执行预处理的过程中进行计算。这种方式为在编译过程中根据计算所得的条件值选择地包含不同的代码提供了一种手段。
#if语句对其中的常量整形表达式(其中不能包含sizeof,类型转化运算符或enum常量)进行求值,若该表达式的值不等于0,则包含其后的各行,直到遇到#endif,#elif或#else语句进行(预处理语句#elif类似于else if)。在#if语句中可以使用表达式defined(名字),该表达式的值遵循下列规则:
当名字已经定义时,其值为1;否则,其值为0;
如:为了保证hdr.h的文件中的内容只被包含一次,可以将该文件的内容包含在下列形式的条件语句中:
#if !defined(HDR)
#define HDR
/**hdr.h文件的内容放在这里**/
#endif
第一次包含在头文件hdr.h时,将定义名字HDR;此后再次包含该文件时,会发现该名字已经定义,这样将直接跳转到#endif处。
类似的方式也可以用来避免多次重复包含同一文件。如果多个头文件能够一致地使用这种方式,那么每个都文件都可以将它所依赖的任何头文件包含进来,用户不必考虑和处理头文件之间的各种依赖关系。
下面的这段预处理代码首先测试系统变量SYSTEM.然后根据该变量的值确定包含那个版本的头文件:
#if SYSTEM == SYSV
#define HDR "sysv.h"
#elif SYSTEM == BSD
#define HDR "bsd.h"
#elif SYSTEM == MSDOS
#define HDR "msdos.h"
#else
#define HDR "default.h"
#endif
#include HDR
c语言专门定义了两个预处理语句#ifdef (如果已定义)与#ifndef(如果没有定义) ,他们用来测试某个名字是否已经定义。
三名的有关#if的第一个例子改写成下列形式:
#ifndef HDR
#define HDR
/*hdr.h文件的内容放在这里*/
#endif