分类: LINUX
2007-03-18 02:26:54
1.函数的基本知识
#include
#define MAXLINE 1000
int getline(char line[], int max);
int strindex(char source[], char searchfor[]);
char pattern[] = "ouch";
/* 找出所有与模式匹配的行 */
int main()
{
char line[MAXLINE];
int found = 0;
while (getline(line, MAXLINE) > 0)
if (strindex(line, pattern) >= 0)
{
printf("%s", line);
found++;
}
return found;
}
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;
}
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;
}
1)如果函数定义中省略了返回值类型,则默认为int类型
2)程序可以看成是变量定义和函数定义的集合
3)函数之间的通信可以通过参数,函数返回值以及外部变量进行
4)当return语句的后面没有表达式时,函数将不向调用者返回值。当被调用函数执行到最后的右花括号而结束执行时,控制同样也会返回给调用者(不返回值)。
2.返回非整型值的函数
1)atof(s)该函数把字符串s转换为相应的双精度浮点数
2)如果函数带有参数,则要声明它们;如果没有参数,则使用void进行声明。
/* atoi函数: 利用atof函数把字符串s转换为整数 */
int atoi(char s[])
{
double atof(char s[]);
return (int) atof(s);
}
3.外部变量
1)C语言程序可以看成由一系列的外部对象构成,这些外部对象可能是变量或函数。
2)外部变量与函数具有下列性质:通过同一个名字对外部变量的所有引用(即使这种引用来自于单独编译的不同函数)实际上都是引用同一个对象(标准中把这一性质称为外部链接)
4.作用域规则
构成C语言程序的函数与外部变量可以分开进行编译。一个程序可以存放在几个文件中,原先已编译过的函数可以从库中进行加载。
名字的作用域指的是程序中可以棵使用该名字的部分
1)如果要在外部变量的定义之前使用该变量,或者外部变量的定义与变量的使用不在同一个源文件中,则必须在相应的
变量声明中强制性地使用关键字extern.
2)将外部变量的声明与定义严格区分开来很重要。变量声明用于说明变量的属性(主要是变量的类型),而变量定义除此以外
还将引起存储器的分配。
例如:int sp[MAXVAL]; 定义变量sp,并为之分配存储单元
extern int sp[]; 为源文件的其余部分声明了一个int数组类型的外部变量sp(该数组的长度在其他地方确定)
但这个声明并没有建立变量或为它们分配存储单元。
3)在一个源程序的所有源文件中,一个外部变量只能在某个文件中定义一次,而其他问可以通过extern声明来访问它
定义外部变量的源文件中也可以包含对该外部变量的extern声明;外部变量的定义中必须指定数组的长度,但extern声明则不一定要指定数组的长度
在文件file1中:
extern int sp;
extern double val[];
void push(double f){ ... }
double pop(void){ ... }
在文件file2中:
int sp = 0;
double val[MAXVAL];
5.头文件
在文件calc.h中:
#define NUMBER '0'
void push(double);
double pop(void);
int getop(char []);
int getch(void);
void ungetch(int);
在文件main.c中:
#include
#include
#include "calc.h"
#define MAXOP 100
main() {
...
}
在文件getop.c中:
#include
#include
#include "calc.h"
getop() {
...
}
在文件stack.c中:
#include
#include "calc.h"
#define MAXVAL 100
int sp = 0;
double val[MAXVAL];
void push(double){
...
}
double pop(void){
...
}
6.静态变量
1)用static声明限定外部变量与函数,可以将其后声明的对象的作用域限定为编译源文件的剩余部分(通过static限定外部对象,可以达到隐藏外部对象的目的)
2)外部的static声明除用于变量外,也可用于函数;通常情况下,函数名字是全局可访问的,对整个程序的各个部分而言都可见;但是,如果把函数声明为static类型,
则该函数名除了对该函数声明所在的文件可见外,其他文件都无法访问.
3)static区别于自动变量的是,不管其所在函数是否被调用,它一直存在,而不像自动变量那样,随着所在函数的被调用和退出而存在和消失。换句话说,static类型的
内部变量是一种只能在某个特定函数中使用但一直占据存储空间的变量.
7.寄存器变量
1)register声明告诉编译器,它所声明的变量在程序中使用频率较高。其思想是:将register变量放在机器的寄存器中,这样可以使程序更小,执行速度更快
2)register声明只适用于自动变量以及函数的形式参数
例如: fun(register unsigned m, register long n)
{
register int i;
...
}
3)实际使用时,底层硬件环境的实际情况对寄存器变量的使用会有一些限制。每个函数中只有很少的变量可以保存在寄存器中,且只允许某些类型的变量。但是,过量的
寄存器声明并没有什么害处,这是因为编译器可以忽略过量的或不支持的寄存器变量声明。另外,无论寄存器变量实际上是不是存放在寄存器中,它的地址都是不能访问
的。在不同的机器中,对寄存器变量的数目和类型的具体限制也是不同的。
8.程序块结构
在一个好的程序设计风格中,应该避免出现变量名隐藏外部作用域中相同名字的情况,否则,很可能引起混乱和错误。
1)变量i的作用域是if语句的"真"分支,这个i与该程序块外声明的i无关。每次进入程序块时,在程序块内声明以及初始化的自动变量都将被初始化。静态变量只在第一次
进入程序时被初始化一次。
if(n > 0){
int i;
for (i = 0; i < n; i++)
...
}
2)自动变量(包括形式参数)也可以隐藏同名的外部变量与函数。
int x;
int y;
f(double x)
{
double y;
...
}
函数f内的变量x引用的是函数的参数,类型为double;而在函数f外,x是int类型的外部变量。这段代码中的y也是如此。
9.初始化
下面我们对前面讨论的各种存储类的初始化规则做一个总结。
1)在不进行显式初始化的情况下,外部变量和静态变量都将被初始化为0,而自动变量和寄存器变量的初值则没有定义(即初值为无用的信息)
2)对于外部变量与静态变量来说,初始化表达式必须是常量表达式,且只初始化一次(从概念上讲是在程序开始执行前进行初始化)
3)对于自动变量和寄存器变量,则在每次进入函数或程序块时都将被初始化。
a.对于自动变量与寄存器变量来说,初始化表达式可以不是常量表达式:表达式中可以包含任意在此表达式之前已经定义的值,包括函数调用。
b.实际上,自动变量的初始化等效于简写的赋值语句
10.递归
1)递归并不节省存储器的开销,因为递归调用过程中必须在某个地方维护一个存储处理值的栽。
2)递归的执行速度并不快,但递归代码比较紧凑,并且比相应的非递归代码更易于编写与理解,在描述树等递归定义的数据结构时使用递归尤其方便。
11.C预处理器
预处理器是编译过程中单独执行的第一个步骤
1)最常用的预处理器指令是:#include指令(用于在编译期间把指定文件的内容包含进当前文件中)和#define指令(用任意字符序列替代一个标记)
2)在大程序中,#include指令是将所有声明捆绑在一起的较好的方法。它保证所有的源文件都具有相同的定义与变量声明,这样可以避免一些不必要的错误。
3)很自然,如果某个包含文件的内容发生了变化,那么所有依赖于该包含文件的源文件都必须重新编译。
4)#define指令定义的名字的作用域从其定义点开始,到被编译的源文件的末尾处结束。
5)可以通过#undef指令取消名字的宏定义,这样做可以保证后续的调用是函数调用,而不是宏调用:#undef getchar
6)如果在替换文本中,参数名以#作为前缀则结果将被扩展为由实际参数替换该参数的带引号的字符串。
例如,可以将它与字符串连接运算结合起来编写一个调试打印宏:
#define dprint(expr) printf(#expr " = %g\n", expr)
即: dprint(x/y) ----- printf("x/y" " = %g\n", expr) ------ printf("x/y = %g\n", expr)
7)预处理器运算符##为宏扩展提供了一种连接实际参数的手段。如果替换文本中的参数与##相邻,则该参数将被实际参数替换,##与前后的空白符将被删除,并
对替换后的结果重新扫描。例如:
#define paste(front, back) front ## back
因此,宏调用paste(name, 1)的结果将建立记号name1.
8)条件包含
这种条件语句的值是在预处理执行的过程中进行计算
例1:第一次包含头文件hdr.h时,将定义名字HDR;此后再次包含该头文件时,会发现该名字已经定义,这样将直接跳转到#endif处
#if !defined(HDR)
#define HDR
/* hdr.h文件的内容放在这里 */
#endif
例2:下面的这段预处理代码首先测试系统变量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,它们用来测试某个名字是否已经定义。
#ifndef HDR
#define HDR
/* hdr.h文件的内容放在这里 */
#endif