4、连接和库函数
1)C语言的一个重要思想是分别编译,即将若干个源程序可以在不同的时间单独进行编译,然后在恰当的时候整合到一起。连接器的工作过程大致如下:
连接器的输入是一组目标模块和库文件,输出是一个载入模块。连接器读入目标模块和库文件,现时生成载入模块。对每个目标模块中的每个外部对象,连接器都要检查载入模块,看是否已有同名的外部对象。如果没有,连接器就将该外部对象添加到载入模块中,如果有,连接器就要开始处理命名冲突。
默认情况下,变量定义是外部的,即:
int i 别的文件中也可以看到。
但是如果声明为:
static int a;
则其作用域只局限在一个源文件中,对于其它源文件,a是不可见的。Static可以对函数,变量进行声明。为了避免可能出现的命名冲突,如果一个函数仅仅被同一个源文件中的其他函数调用,就应当声明该函数为static.
如果一个函数的调用和定义不在同一个文件中,则应当在调用它前进行声明。
一个的好的习惯是将声明放在头文件中,而在需要声明变量的文件中包含头文件。
2)一个典型例子:
#include "iostream"
using namespace std;
int main()
{
int i;
char c;
for(i=0;i<5;i++)
{
scanf("%d",&c);
printf("%d ",i);
}
return 1;
}
c是char型。程序要求输入整数,应该给它传递一个指向整数的指针。而程序中得到的却是一个指向字符的指针,而scanf函数并不能分辨,于是将这个指向字符的指针作为指向整数的指针而接受,并且在指针位置存储一个整数;这样,占了两倍的空间(一般char是一个字节,int是两个字节或更多),所以c将 附近的内存覆盖。本例中,其附近存储的是i的低字节部分,这样,每次读入一个c时,将i的低端部分覆盖为0,而其高端部分也为0,所以就进入了死循环。
当然这些情况在C++中不会出现。
3)getchar()函数是返回整形的,如果将之赋给字符了字符变量,如下
char c;
while((c=getchar())!=EOF)
putchar(c);
可能会出现意想不到的结果。
4)更新顺序文件
为了保证与过去不能同时进行读写操作的程序的向下兼容性,一个输入操作不能随后直接紧跟一个输出操作,反之亦然。如果要同时进行输入与输出操作,必须在其中插入fseek函数。如下所示:
int fwrite(void *buf, int size, int count, FILE *fp)
int fseek(FILE *fp, LONG offset, int origin)
int fread(void *buf, int size, int count, FILE *fp)
5)缓冲输出与内存分配
程序输出有两种方式:一种是即时处理方式,另一种是先暂存进来,然后大块写入。对于第二种方式,通过库函数setbuf来实现。如buf是一个合适大小的数组,则
setbuf(stdout,buf);
将通知输入/输出库,所以写入到stdout的输出就用buf作为缓冲区,直到buf缓冲区满了或程序员用fflush,缓冲区的内容才实际定到stdout中。缓冲区的大小由<stdio.h>中的BUFSIZ定义。如下所示:
int main()
{
int c;
char buf[BUFSIZ];
setbuf(stdout,buf);
while((c=getchar())!=EOF)
putchar(c);
}
然后,程序不对。因为buf缓冲区最后一次清空是在main函数结束后,但此前buf数组已被释放。解决方法:
(1)显示声明为静态:
static char buf[BUFSIZ];//或移到main函数之外。
(2)动态分配缓冲区。
可以作个测试如下:
void hello()
{
int c;
static char buf[3000];
setbuf(stdout,buf);
for(int m=0;m<25;m++)
{
for(int i=0;i<344;i++)
cout<<i<<" ";
cout<<m<<" "<<endl;
}//for
}
int main()
{
hello();
cout<<"finish";
}
5)使用errno检测错误
在调用库函数时,应当首先检测作为错误指示的返回值,确定程序执行已经失败。然后再检查errno来搞清楚原因:
if(返回的错误值)
检查 errno
6)库函数signal
void ( *signal(int sig, void (*func)(int)))(int)
sig 信号数值,该参数以前由raise函数设置,取值为如下:
#define SIGABRT 22
#define SIGFPE 8 /* Floating point trap */
#define SIGILL 4 /* Illegal instruction */
#define SIGINT 2
#define SIGSEGV 11 /* Memory access violation */
#define SIGTERM 15
信号问题棘手,而且具有本质上不可移植性,解决这个问题:让signal处理函数尽可能简单,并将他们组织在一起以便修改。
常将getchar()实现为宏。