使用sqlite3与C接口开发数据库程序

最近想学mysql的数据库编程,主要原因是不想要用到数据库的时候只会C#+Sql Server,毕竟咱正转向开源阵地呵呵

偶然在网上发现下面这篇文章介绍一种数据库,挺有意思,所以记录在此

 

来源:  http://feizf.blogbus.com/logs/5130352.html
 

最近我正在Linux平台写一个软件,需要用到一个简单的数据库。 mysql做数据库固然很好,但其数据是存放在服务器的。我想要的基本功能也就是使用C程序创建一个数据库本地文件,然后可以对这个数据库文件执行基本的 sql操作. 就像在Windows平台基于VC6.0的DAO数据库编程一样(创建一个本地文件.mdb).

从网上找到了一个开源免费的数据库开发工具--sqlite, 网上的关于sqlite的介绍有很多,详细见官方网站: http://www.sqlite.com.cn/ . 我发现sqlite正是我需要的. 总结一下几个特点:
1. 开放源代码
2. 程序特别小,在windows下应用程序sqlite.exe仅仅200kb以内。
3. 支持大多数sql指令,速度极快
4. 直接创建一个xxx.db, 就是一个数据库,不需要服务器支持
5. 简洁的C语言API接口

基于上面几点,足可以看出sqlite的强大和优异之处。源代码公开和程序特别小,甚至可以跨平台直接编译"gcc -o sqlite3 *",将这融合到潜入式系统是多么的美妙!

Firefox 中文乱码

来源: www.3dian9.com/archives/1317.html

如下两步:

(1)备份49-sansserif.conf这个字体配置文件

    sudo cp /etc/fonts/conf.d/49-sansserif.conf /etc/fonts/conf.d/49-sansserif.conf.bak

(2)删除49-sansserif.conf文件做完后重启firefox就可以了。

    sudo rm /etc/fonts/conf.d/49-sansserif.conf

重新启动,在打开就好了,Goolge音乐搜索中个歌曲名字就不是乱码了。

gnu indent 美化C程序代码

参考: http://www.worldhello.net/doc/program_rules/indent.html

google搜索就能找到gnu indent.

The `indent' program can be used to make code easier to read. It can also convert from one style of writing C to another.

 

参照GNU,Kernighan & Ritchie,Berkeley风格,制定了自己风格:

  1. indent命令参数:

    -bad -bap -bbb -bbo -nbc -bl -bli0 -bls -c33 -cd33 -ncdb -ncdw -nce -cli0 -cp33 -cs -d0 -nbfda -di2 -nfc1 -nfca -hnl -ip5 -l75 -lp -pcs -nprs -psl -saf -sai -saw -nsc -nsob -nss -i4 -ts4 -ut

  2. indent配置文件

    如上参数可写入用户目录下的文件:".indent.pro",作为运行indent的确省参数

  3. indent配置说明

    Indent代码格式化说明
    使用的indent参数 含义
    --blank-lines-after-declarations bad 变量声明后加空行
    --blank-lines-after-procedures bap 函数结束后加空行
    --blank-lines-before-block-comments bbb 块注释前加空行
    --break-before-boolean-operator bbo 较长的行,在逻辑运算符前分行
    --blank-lines-after-commas nbc 变量声明中,逗号分隔的变量不分行
    --braces-after-if-line bl "if"和"{"分做两行
    --brace-indent 0 bli0 "{"不继续缩进
    --braces-after-struct-decl-line bls 定义结构,"struct"和"{"分行
    --comment-indentationn c33 语句后注释开始于行33
    --declaration-comment-columnn cd33 变量声明后注释开始于行33
    --comment-delimiters-on-blank-lines ncdb 不将单行注释变为块注释
    --cuddle-do-while ncdw "do --- while"的"while"和其前面的"}"另起一行
    --cuddle-else nce "else"和其前面的"}"另起一行
    --case-indentation 0 cli0 switch中的case语句所进0个空格
    --else-endif-columnn cp33 #else, #endif后面的注释开始于行33
    --space-after-cast cs 在类型转换后面加空格
    --line-comments-indentation n d0 单行注释(不从1列开始的),不向左缩进
    --break-function-decl-args nbfda 关闭:函数的参数一个一行
    --declaration-indentationn di2 变量声明,变量开始于2行,即不必对齐
    --format-first-column-comments nfc1 不格式化起于第一行的注释
    --format-all-comments nfca 不开启全部格式化注释的开关
    --honour-newlines hnl Prefer to break long lines at the position of newlines in the input.
    --indent-leveln i4 设置缩进多少字符,如果为tab的整数倍,用tab来缩进,否则用空格填充。
    --parameter-indentationn ip5 旧风格的函数定义中参数说明缩进5个空格
    --line-length 75 l75 非注释行最长75
    --continue-at-parentheses lp 续行从上一行出现的括号开始
    --space-after-procedure-calls pcs 函数和"("之间插入一个空格
    --space-after-parentheses nprs 在"("后")"前不插入空格
    --procnames-start-lines psl 将函数名和返回类型放在两行定义
    --space-after-for saf for后面有空格
    --space-after-if sai if后面有空格
    --space-after-while saw while后面有空格
    --start-left-side-of-comments nsc 不在生成的块注释中加*
    --swallow-optional-blank-lines nsob 不去掉可添加的空行
    --space-special-semicolon nss 一行的for或while语句,在";"前不加空。
    --tab-size ts4 一个tab为4个空格(要能整除"-in")
    --use-tabs ut 使用tab来缩进

效果:

刚用 indent 美化了根据《C语言接口与实现》中异常一章编写的例子:

预处理之后:

 

int main()
{
 char *buf;

 do { volatile int Except_flag; Except_Frame Except_frame; Except_frame.prev = Except_stack; Except_stack = &Except_frame; Except_flag = _setjmp (Except_frame.env); if(Except_flag == Except_entered) {
  buf = allocate(512);
    if(Except_flag == Except_entered) Except_stack = Except_stack->prev; } else if (Except_frame.exception == &(Allocate_Failed)) { Except_flag = Except_handled;
  fprintf(stderr, "Couldn't allocate the buffer.\n");
 if(Except_flag == Except_entered) Except_stack = Except_stack->prev; } else if (Except_frame.exception == &(Allocate_Success)) { Except_flag = Except_handled;
  fprintf(stderr, "Can allocate the buffer.\n");
 if(Except_flag == Except_entered) Except_stack = Except_stack->prev; } { if(Except_flag == Except_entered) Except_flag = Except_finalized;
  fprintf(stderr, "FINALLY SCOPE.\n");
 if(Except_flag == Except_entered) Except_stack = Except_stack->prev; } if(Except_flag == Except_raised) Except_raise(Except_frame.exception, Except_frame.file, Except_frame.line); } while(0);
}

经 indent 处理后:

 

int
main ()
{
        char *buf;

        do
        {
                volatile int Except_flag;
                Except_Frame Except_frame;

                Except_frame.prev = Except_stack;
                Except_stack = &Except_frame;
                Except_flag = _setjmp (Except_frame.env);
                if (Except_flag == Except_entered)
                {
                        buf = allocate (512);
                        if (Except_flag == Except_entered)
                                Except_stack = Except_stack->prev;
                }
                else if (Except_frame.exception == &(Allocate_Failed))
                {
                        Except_flag = Except_handled;
                        fprintf (stderr, "Couldn't allocate the buffer.\n");
                        if (Except_flag == Except_entered)
                                Except_stack = Except_stack->prev;
                }
                else if (Except_frame.exception == &(Allocate_Success))
                {
                        Except_flag = Except_handled;
                        fprintf (stderr, "Can allocate the buffer.\n");
                        if (Except_flag == Except_entered)
                                Except_stack = Except_stack->prev;
                }
                {
                        if (Except_flag == Except_entered)
                                Except_flag = Except_finalized;
                        fprintf (stderr, "FINALLY SCOPE.\n");
                        if (Except_flag == Except_entered)
                                Except_stack = Except_stack->prev;
                }
                if (Except_flag == Except_raised)
                        Except_raise (Except_frame.exception, Except_frame.file,
                                                  Except_frame.line);
        }
        while (0);
}

 

哇...好好的工具(*^__^*) 嘻嘻……

 

 

gnu binutils 工具解析

内容转自:

www.cublog.cn/u/13991/showart.php

 

GNU binutils是一组二进制工具集。包括:addr2line   ar   gprof   nm   objcopy   objdump   ranlib   size   strings   strip. 本文归纳他们的常用法。

ar


ar用于建立、修改、提取档案文件(archive)。archive是一个包含多个被包含文件的单一文件(也称之为库文件),其结构保证了可以从中检索 并得到原始的被包含文件(称之为archive中的member)。member的原始文件内容、模式(权限)、时间戳、所有着和组等属性都被保存在 archive中。member被提取后,他们的属性被恢复到初始状态。

ar主要用于创建C库文件(关于.o目标文件的生成和共享库的详细介绍,参考gcc笔记)


创建静态库 

    (1) 生成目标文件:  

 

$ gcc -Wall -c file1.c file2.c file3.c

   不用指定生成.o文件名(默认生成file1.o, file2.o, file3.o)。

 

    (2) 从.o目标文件创建静态连接库:

  

$ ar rv libNAME.a file1.o file2.o file3.o

   ar生成了libNAME.a库,并列出库中的文件。

 

   

r :

将flie1.o, file2,o, file3.o插入archive,如故原先archive中已经存在某文件,则先将该文件删除

v :

显示ar操作的附加信息(如被处理的member文件名)

 

注: 对于BSD系统, 还需要在创建静态库之后创建索引: $

ranlib libNAME.a

Linux中不需要这一步(运行它也是无害的).

买了APUE中文版

毕业论文的理论阶段基本结束,这几天等着信息与网络中心提供服务器日志。

人不能闲着阿,是吧?否则不是浪费生命嘛,所以又开始了Unix-C呵呵。

把《Pointers On C》基本看完了,内容很基础,适合初学,加上有C++的基础所以就很快的过了一遍。

 

前天在卓越下单买了《Unix环境高级编程》和《C陷阱与缺陷》,今天中午书就送到了,速度还是很满意的(*^__^*) 嘻嘻……

接下来就准备赶紧把毕设搞定,然后继续我的Unix-C之旅,另外别忘了pku做题。

 

看到好书总有一定要买来的冲动,这不把三本关于C的书都买齐了,艾 ..... 都是money啊

 

另外,打算把大学期间买的一些书在BBS上卖掉咯,但愿还有人要      

 

 

学习计划 Unix-C

鉴于目前毕业论文的压力(还没有开始呢我的毕设),原本的看书计划只能暂时搁置,等这学期忙完再继续。

C:

1. 《C程序设计语言》

     看完电子版,不愧为经典中的经典,有机会一定要去图书馆找到这本书再看一遍。

2. 《C和指针》

     已买,还没有看。

3. 《C专家编程》

     已买,还没有看。

4. 《C陷阱与缺陷》

     没买,不过至少要看电子版的,如果好的话再买

 

Unix & Linux

1. 《Linux程序设计》

      不打算买,已经从图书馆借来看完前六章(前四章的书本例子也已经实际code过),不过为了毕设,还是准备先还了吧,下次从第七章继续

      第五章讲终端,第六章讲curses函数库,都是关于终端下文本在屏幕上的输出控制的,看完,书本例子就算了吧

2. 《Linux高级程序设计》

     不打算买,哪天如果有机会在图书馆碰到的话,就借来看看

3. 《Unix环境高级编程》

     一定要买,不知道是买英文版还是中文版呵呵,从图书馆借来的英文版刚看了两章,感觉写的很好懂

 

Algorithm

算法方面的书已经买了好几本,缺少的就是投入。

 

今记录在此,等忙完毕业论文,再继续

 

ubuntu 8.10 LaTex + 中文字体

今天刚在机器上安装好LaTex以及中文字体,毕业论文即将开始,不知道用不用的上呵呵呵

参考的文章是:blog.upsuper.org.cn/ubuntu-trip-7-latex-return/

首先是到这里下载最新的TexLive的iso文件,文件很大(1.2G左右),我是在Windows下用迅雷下载的,速度很快,这里以texlive2008-20080822.iso.lzma为例。


unlzma texlive2008-20080822.iso.lzma

sudo mkdir /media/texlive
sudo mount -o loop  texlive2008-20080822.iso /media/texlive
cd /media/texlive
sudo ./install-tl
 

然后按"I"开始安装。安装过程是全自动化的。

另外,具体安装过程也可以参考 www.tug.org/texlive/quickinstall.html

特别注意:

you must add the TeX Live binary directory to your PATH:
  PATH=/usr/local/texlive/2008/bin/i386-linux:/usr/local/texlive/2008/bin/i386-linux:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/Adobe/Adobe/Reader8/bin:/usr/local/emelfm2/bin

编辑/etc/environment,在PATH的最前面加上“/usr/local/texlive/2008/bin/i386-linux:”


中文字体支持

首先字体文件可以在Windows系统的C:/Windows/Fonts文件夹下找到,例如simhei.ttf, simkai.ttf等。

主要使用的是一个脚本文件: mkfont.tar.gz

./mkfont.sh simkai.ttf simkai kai

./mkfont.sh simsun.ttf simsun song

.........类似,注意宋体必须要用Windows 98所带的宋体,不然不支持

最后找到主文件夹下有一个“texmf”文件夹,将里面的东西拷贝到“~/.texlive2007/texmf-var”文件夹下即可。

simhei.ttf ---> hei (黑体)

simsun.ttf ---> song (宋体)

simkai.ttf ---> kai (楷体)

simfang.ttf ---> fang (仿宋)

SIMLI.TTF ---> li (隶书)

SIMYOU.TTF ---> you (幼圆)

以上是我转换成功的字体,测试:

\documentclass{article}
\usepackage{CJKutf8}
\begin{document}
\begin{CJK*}{UTF8}{song}
你好!
\end{CJK*}
\begin{CJK*}{UTF8}{kai}
你好!
\end{CJK*}
\begin{CJK*}{UTF8}{hei}
你好!
\end{CJK*}
\begin{CJK*}{UTF8}{fang}
你好!
\end{CJK*}
\end{document}

 

编译器&链接器

头文件
用C语言及其他语言进行程序设计时,我们要用头文件来提供对常量的定义和对系统及库函数调用的声明。
对C语言来说,这些头文件几乎总是在/usr/include目录及其子目录下。
调用C语言编译器时,我们可以使用-I标志来包含保存在子目录或非标准位置的include文件, 例如:
      gcc -I/usr/openwin/include fred.c
它指示编译器不仅在标准位置,也在/usr/openwin/include目录中查找程序fred.c中包含的头文件。

另外,用grep命令来搜索包含某些特定定义和函数原型的头文件是很方便的,例如:

查找包含EXIT_的定义的头文件

grep EXIT_ *.h

 

库文件
库是一组预先编译好的函数的集合,这些函数都是按照可重用的原则编写的,他们通常由一组相互关联的函数组成并执行某项常见的任务。
标准系统库文件一般存储在/lib和/usr/lib目录中。C语言编译器(更确切地说,是链接程序)需要知道要搜索哪些库文件,默认情况下,它只搜索标准C语言库。
库文件的名字总是以lib开头,随后的部分指名这是哪种库(例如,c代表C语言库,m代表数学库)。文件名的最后部分以.开始,然后给出库文件的类型:
  .a   代表传统的静态函数库
  .so  代表共享函数库
我们可以通过给出完整的路径名或用-l标志来指示编译器要搜索的库文件,例如:
  gcc -o fred fred.c /usr/lib/libm.a
这条命令指示编译器编译文件fred.c,将编译产生的程序文件命名为fred,并且除搜索标准的C语言函数库外,还搜索数学库以解决函数引用问题,下面的命令也能产生类似的结果:
  gcc -o fred fred.c -lm
-lm是简写形式,它代表的是标准库目录(/usr/lib)中名为libm.a的函数库。-lm标志的另一个好处是如果有共享库,编译器会自动选择共享库。
另外,我们也可以通过-L标志为编译器增加库的搜索路径。例如:
  gcc -o x11fred -L/usr/openwin/lib x11fred.c -lX11
这条命令用/usr/openwin/lib目录中的libX11库版本来编译和链接程序x11fred.c

当程序需要使用函数库中的某个函数时,它包含一个声明该函数的头文件,编译器和链接器负责将程序代码和函数库结合在一起以组成一个单独的可执行文件。

静态库
也称作归档文件(archive)。
我们可以容易的创建和维护自己的静态库,只要使用ar程序和gcc -c命令对函数分别进行编译,例如:
(1)分别编写两个函数的源文件:
//fred.c
#include<stdio.h>
void fred(int arg)
{
   printf("fred: you passed %d\n", arg);
}

//bill.c
#include<stdio.h>
void bill(char *arg)
{
   printf("bill: you passed %s\n", arg);
}

(2)编译为目标文件
  gcc -c bill.c fred.c
 
生成目标文件: bill.o  fred.c
-c选项的作用是阻止编译器创建一个完整的程序,如果此时试图创建一个完整的程序将不会成功,因为尚未定义main函数。

(3)为库文件创建头文件
//lib.h
void fred(int);
void bill(char *arg);

(4)创建库文件
  ar crv libfoo.a bill.o fred.o
 
(5)主程序
//program.c
#include "lib.h"

int main()
{
   bill("Hello World.");
   return 0;
}

(6)编译链接主程序
  gcc -o program program.c -L. -lfoo
  ./program
  输出: bill: you passed Hello World.
-L.选项指示编译器在当前目录中查找函数库。

 

共享库

共享库
静态库一个缺点是,当我们同时运行多个应用程序并且它们都使用来自同一个函数库的函数时,就会在内存中有同一函数的多份拷贝,在程序文件自身中也有多份同样的拷贝,这将消耗大量宝贵的内存和磁盘空间。
共享库克服了上述不足。
共享库的保存位置与静态库是一样的,在典型的Linux系统中,标准数学库的共享版本是/usr/lib/libm.so。
程序使用共享库时,它的链接方式是这样的:它本身不再包含函数代码,而是引用运行时可访问的共享代码。当编译好的程序被装载到内存中执行时,函数引用被解析并产生对共享库的调用,如果
有必要共享库才被加载到内存中。
对Linux系统来说,负责装载共享库并解析客户程序函数引用的程序(动态装载器)是ld.so, 也可能是ld-linux.so.2或ld-lsb.so.1.
我们可以通过运行工具ldd来查看程序需要的共享库:
  ldd program

 

C标准库函数

来源: http://www.duangw.net/computer/languages/c/clib.html

 

本文包括大部分C标准库函数,但没有列出一些用途有限的函数以及某些可以简单的从其他函数合成的函数,也没有包含多字节和本地化函数。

标准库中的各个函数、类型以及宏分别在以下标准头文件中说明:

<assert.h> <float.h> <math.h> <stdarg.h> <stdlib.h>
<ctype.h> <limits.h> <setjmp.h> <stddef.h> <string.h>
<errno.h> <locale.h> <signal.h> <stdio.h> <time.h>

 

1 输入与输出<stdio.h>

头文件<stdio.h>定义了用于输入和输出的函数、类型和宏。最重要的类型是用于声明文件指针的FILE。另外两个常用的类型是 size_t和fpos_t,size_t是由运算符sizeof产生的无符号整类型;fpos_t类型定义能够唯一说明文件中的每个位置的对象。由头部 定义的最有用的宏是EOF,其值代表文件的结尾。

1.1 文件操作

1.1.1 fopen

#include <stdio.h>
FILE *fopen(const char *filename, const char *mode);
返回:成功为FILE指针,失败为NULL

打开以filename所指内容为名字的文件,返回与之关联的流。

mode决定打开的方式,可选值如下:

"r" 打开文本文件用于读
"w" 创建文本文件用于写,并删除已存在的内容(如果有的话)
"a" 添加;打开或创建文本文件用于在文件末尾写
"rb" 打开二进制文件用于读
"wb" 创建二进制文件用于写,并删除已存在的内容(如果有的话)
"ab" 添加;打开或创建二进制文件用于在文件末尾写
"r+" 打开文本文件用于更新(即读和写)
"w+" 创建文本文件用于更新,并删除已存在的内容(如果有的话)
"a+" 添加;打开或创建文本文件用于更新和在文件末尾写
"rb+"或"r+b" 打开二进制文件用于更新(即读和写)
"wb+"或"w+b" 创建二进制文件用于更新,并删除已存在的内容(如果有的话)
"ab+"或"a+b" 添加;打开或创建二进制文件用于更新和在文件末尾写

后六种方式允许对同一文件进行读和写,要注意的是,在写操作和读操作的交替过程中,必须调用fflush()或文件定位函数如fseek()、fsetpos()、rewind()等。

文件名filename的长度最大为FILENAME_MAX个字符,一次最多可打开FOPEN_MAX个文件(在<stdio.h>中定义)。

 

1.1.2 freopen

#include <stdio.h>
FILE *freopen(const char *filename, const char *mode,
FILE *stream);
返回:成功为stream,失败为NULL

以mode指定的方式打开文件filename,并使该文件与流stream相关联。freopen()先尝试关闭与stream关联的文件,不管成功与否,都继续打开新文件。

该函数的主要用途是把系统定义的标准流stdin、stdout、stderr重定向到其他文件。

 

1.1.3 fflush

#include <stdio.h>
int fflush(FILE *stream);
返回:成功为0,失败返回EOF

对输出流(写打开),fflush()用于将已写到缓冲区但尚未写出的全部数据都写到文件中;对输入流,其结果未定义。如果写过程中发生错误则返回EOF,正常则返回0。

fflush(NULL)用于刷新所有的输出流。

程序正常结束或缓冲区满时,缓冲区自动清仓。

 

1.1.4 fclose

#include <stdio.h>
int flcose(FILE *stream);
返回:成功为0,失败返回EOF

刷新stream的全部未写出数据,丢弃任何未读的缓冲区内的输入数据并释放自动分配的缓冲区,最后关闭流。

 

1.1.5 remove

#include <stdio.h>
int remove(const char *filename);
返回:成功为0,失败为非0值

删除文件filename。

 

1.1.6 rename

#include <stdio.h>
int rename(const char *oldfname, const char *newfname);
返回:成功为0,失败为非0值

把文件的名字从oldfname改为newfname。

 

1.1.7 tmpfile

#include <stdio.h>
FILE *tmpfile(void);
返回:成功为流指针,失败为NULL

以方式"wb+"创建一个临时文件,并返回该流的指针,该文件在被关闭或程序正常结束时被自动删除。

 

1.1.8 tmpnam

#include <stdio.h>
char *tmpnam(char s[L_tmpnam]);
返回:成功为非空指针,失败为NULL

若参数s为NULL(即调用tmpnam(NULL)),函数创建一个不同于现存文件名字的字符串,并返回一个指向一内部静态数组的指针。

若s非空,则函数将所创建的字符串存储在数组s中,并将它作为函数值返回。s中至少要有L_tmpnam个字符的空间。

tmpnam函数在每次被调用时均生成不同的名字。在程序的执行过程中,最多只能确保生成TMP_MAX个不同的名字。注意tmpnam函数只是用于创建一个名字,而不是创建一个文件。

 

1.1.9 setvbuf

#include <stdio.h>
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
返回:成功返回0,失败返回非0

控制流stream的缓冲区,这要在读、写以及其他任何操作之前设置。

如果buf非空,则将buf指向的区域作为流的缓冲区,如果buf为NULL,函数将自行分配一个缓冲区。

size决定缓冲区的大小。

mode指定缓冲的处理方式,有如下值:

  • _IOFBF,进行完全缓冲;
  • _IOLBF,对文本文件表示行缓冲;
  • _IOLNF,不设置缓冲。

 

1.1.10 setbuf

#include <stdio.h>
void setbuf(FILE *stream, char *buf);

如果buf为NULL,则关闭流stream的的缓冲区;否则setbuf函数等价于:

    (void)setvbuf(stream, buf, _IOFBF, BUFSIZ)。

注意自定义缓冲区的尺寸必须为BUFSIZ个字节。

 

1.2 格式化输出

1.2.1 fprintf

#include <stdio.h>
int fprintf(FILE *stream, const char *format,…);
返回:成功为实际写出的字符数,出错返回负值

按照format说明的格式把变元表中变元内容进行转换,并写入stream指向的流。

格式化字符串由两种类型的对象组成:普通字符(它们被拷贝到输出流)与转换规格说明(它们决定变元的转换和输出格式)。每个转换规格说明均以字符%开头,以转换字符结束。如果%后面的字符不是转换字符,那么该行为是未定义的。

转换字符列表如下:

字 符 说明
d, i int;有符号十进制表示法
o unsigned int;无符号八进制表示法(无前导0)
x, X unsigned int;无符号十六进制表示法(无前导0X和0x),对0x用abcdef,对0X用ABCDEF
u unsigned int;无符号十进制表示法
c int;单个字符,转换为unsigned char类型后输出
s char *;输出字符串直到'\0'或者达到精度指定的字符数
f double;形如[-]mmm.ddd的十进制浮点数表示法,d的数目由精度确定。缺省精度为6位,精度为0时不输出小数点
e, E double;形如[-]m.dddddde[+-]xx或者[-]m.ddddddE[+-]xx的十进制浮点数表示法,d的数目由精度确定。缺省精度为6位,精度为0时不输出小数点
g G double;当指数值小于-4或大于等于精度时,采用%e或%E的格式;否则使用%f的格式。尾部的0与小数点不打印
p void *;输出指针值(具体表示与实现相关)
n int *;到目前为止以此格式调用函数输出的字符的数目将被写入到相应变元中,不进行变元转换
% 不进行变元转换,输出%

在%与转换字符之间依次可以有下列标记:

标记 说明
- 指定被转换的变元在其字段内左对齐
+ 指定在输出的数前面加上正负号
空格 如果第一个字符不是正负号,那么在其前面附加一个空格
0 对于数值转换,在输出长度小于字段宽度时,加前导0
# 指定其他输出格式,对于o格式,第一个数字必须为零;对于x/X格式,指定在输出的非0值前加0x或0X;对于e/E/f/g/G格式,指定输出总有一个小数点;对于g/GG格式,还指定输出值后面无意义的0将被保留。
宽度[number] 一个指定最小字段宽的数。转换后的变元输出宽度至少要达到这个数值。如果变元的字符数小于此数值,那么在变元左/右边添加填充字符。填充字符通常为空格(设置了0标记则为0)。
. 点号用于把字段宽和精度分开
精度[number] 对于字符串,说明输出字符的最大数目;对于e/E/f格式,说明输出的小数位数;对于g/G格式,说明输出的有效位数;对于整数,说明输出的最小位数(必要时可加前导0)
h/l/L 长度修饰符,h表示对应的变元按short或unsigned short类型输出;l表示对应的变元按long或unsigned long类型输出;L表示对应的变元按long double类型输出

在格式串中字段宽度和精度二者都可以用*来指定,此时该值可通过转换对应的变元来获得,这些变元必须是int类型。

 

1.2.2 printf

#include <stdio.h>
int printf(const char *format, …);

printf(...)等价于fprintf(stdout, ...)。

 

1.2.3 sprintf

#include <stdio.h>
int sprintf(char *buf, const char *format, …);
返回:实际写到字符数组的字符数,不包括'\0'

与printf()基本相同,但输出写到字符数组buf而不是stdout中,并以'\0'结束。

注意,sprintf()不对buf进行边界检查,buf必须足够大,以便能装下输出结果。

 

1.2.4 snprintf

#include <stdio.h>
int snprintf(char *buf, size_t num, const char *format, …);

除了最多为num-1个字符被存放到buf指向的数组之外,snprintf()和sprintf()完全相同。数组以'\0'结束。

该函数不属于C89(C99增加的),但应用广泛,所以将其包括了进来。

 

1.2.5 vprintf

1.2.6 vfprintf

1.2.7 vsprintf

1.2.8 vsnprintf

#include <stdarg.h>
#include <stdio.h>
int vprintf(char *format, va_list arg);
int vfprintf(FILE *stream, const char *format, va_list arg);
int vsprintf(char *buf, const char *format, va_list arg);
int vsnprintf(char *buf, size_t num, const char *format,
va_list arg);

这几个函数与对应的printf()等价,但变元表由arg代替。参见第7节有关<stdarg.h>头文件的讨论。

vsnprintf是C99增加的。

 

1.3 格式化输入

1.3.1 fscanf

#include <stdio.h>
int fscanf(FILE *stream, const char *format, …);
返回:成功为实际被转换并赋值的输入项数目,
到达文件尾或变元被转换前出错为EOF

在格式串format的控制下从流stream中读入字符,把转换后的值赋给后续各个变元,在此每一个变元都必须是一个指针。当格式串format结束时函数返回。

格式串format通常包含有用于指导输入转换的转换规格说明。格式串中可以包含:

  • 空格或制表符,他们将被忽略;
  • 普通字符(%除外),与输入流中下一个非空白字符相匹配;
  • 转换规格说明:由一个%、一个赋值屏蔽字符*(可选)、一个用于指定最大字段宽度的数(可选)、一个用于指定目标字段的字符h/l/L(可选)、一个转换字符组成。

转换规格说明决定了输入字段的转换方式。通常把结果保存在由对应变元指向的变量中。然而,如果转换规格说明中包含有赋值屏蔽字符*,例如%*s,那 么就跳过对应的输入字段,不进行赋值。输入字段是一个由非空白符组成的字符串,当遇到空白符或到达最大字段宽(如果有的话)时,对输入字段的读入结束。这 意味着scanf函数可以跨越行的界限来读入其输入,因为换行符也是空白符(空白符包括空格、横向制表符、纵向制表符、换行符、回车符和换页符)。

转换字符列表如下:

字符 输入数据;变元类型
d 十进制整数;int *
i 整数;int *。该整数可以是以0开头的八进制数,也可以是以0x/0X开头的十六进制数
o 八进制数(可以带或不带前导0);unsigned int *
u 无符号十进制整数;unsigned int *
x 十六进制整数(可以带或不带前导0x/0X);unsigned int *
c 字符;char *。按照字段宽的大小把读入的字符保存在指定的数组中,不加入字符'\0'。字段宽的缺省值为1。在这种情况下,不跳过空白符;如果要读入下一个非空白符,使用%1s(数字1)
s 有非空白符组成的字符串(不包含引号);char *。该变元指针指向一个字符数组,该字符数组有足够空间来保存该字符串以及在末尾添加的'\0'
e/f/g 浮点数;float *。float浮点数的输入格式为:一个任选的正负号,一串可能包含小数点的数字和一个任选的指数字段。指数字段由字母e/E以及后跟的一个可能带正负号的整数组成
p 用printf("%p")调用输出的指针值;void *
n 将到目前为止此调用所读的字符数写入变元;int *。不读入输入字符。不增加转换项目计数
[...] 用方括号括起来的字符集中的字符来匹配输入,以找到最长的非空字符串;char *。在末尾添加'\0'。格式[]...]表示字符集中包含字符]
[^...] 用不在方括号里的字符集中的字符来匹配输入,以找到最长的非空字符串;char *。在末尾添加'\0'。格式[]...]表示字符集中包含字符]
% 字面值%,不进行赋值

字段类型字符:

  • 如果变元是指向short类型而不是int类型的指针,那么在d/i/n/o/u/x这几个转换字符前可以加上字符h
  • 如果变元是指向long类型的指针,那么在d/i/n/o/u/x这几个转换字符前可以加上字符l
  • 如果变元是指向double类型而不是float类型的指针,那么在e/f/g这几个转换字符前可以加上字符l
  • 如果变元是指向long double类型的指针,那么在e/f/g前可以加上字符L

 

1.3.2 scanf

#include <stdio.h>
int scanf(const char *format, …);

scanf(...)等价于fscanf(stdin, ...)。

 

1.3.3 sscanf

#include <stdio.h>
int sscanf(const char *buf, const char *format, …);

与scanf()基本相同,但sscanf()从buf指向的数组中读,而不是stdin。

 

1.4 字符输入输出函数

1.4.1 fgetc

#include <stdio.h>
int fgetc(FILE *stream);

以unsigned char类型返回输入流stream中下一个字符(转换为int类型)。如果到达文件末尾或发生错误,则返回EOF。

 

1.4.2 fgets

#include <stdio.h>
char *fgets(char *str, int num, FILE *stream);
返回:成功返回str,到达文件尾或发生错误返回NULL

从流stream中读入最多num-1个字符到数组str中。当遇到换行符时,把换行符保留在str中,读入不再进行。数组str以'\0'结尾。

 

1.4.3 fputc

#include <stdio.h>
int fputc(int ch, FILE *stream);
返回:成功为所写的字符,出错为EOF

把字符ch(转换为unsigned char类型)输出到流stream中。

 

1.4.4 fputs

#include <stdio.h>
int fputs(const char *str, FILE *stream);
返回:成功返回非负值,失败返回EOF

把字符串str输出到流stream中,不输出终止符'\0'。

 

1.4.5 getc

#include <stdio.h>
int getc(FILE *stream);

getc()与fgetc()等价。不同之处为:当getc函数被定义为宏时,它可能多次计算stream的值。

 

1.4.6 getchar

#include <stdio.h>
int getchar(void);

等价于getc(stdin)。

 

1.4.7 gets

#include <stdio.h>
char *gets(char *str);
返回:成功为str,到达文件尾或发生错误则为NULL

从stdin中读入下一个字符串到数组str中,并把读入的换行符替换为字符'\0'。

gets()可读入无限多字节,所以要保证str有足够的空间,防止溢出。

 

1.4.8 putc

#include <stdio.h>
int putc(int ch, FILE *stream);

putc()与fputc()等价。不同之处为:当putc函数被定义为宏时,它可能多次计算stream的值。

 

1.4.9 putchar

#include <stdio.h>
int putchar(int ch);

等价于putc(ch, stdout)。

 

1.4.10 puts

#include <stdio.h>
int puts(const char *str);
返回:成功返回非负值,出错返回EOF

把字符串str和一个换行符输出到stdout。

 

1.4.11 ungetc

#include <stdio.h>
int ungetc(int ch, FILE *stream);
返回:成功时为ch,出错为EOF

把字符ch(转换为unsigned char类型)写回到流stream中,下次对该流进行读操作时,将返回该字符。对每个流只保证能写回一个字符(有些实现支持回退多个字符),且此字符不能是EOF。

 

1.5 直接输入输出函数

1.5.1 fread

#include <stdio.h>
size_t fread(void *buf, size_t size, size_t count,
FILE *stream);
返回:实际读入的对象数

从流stream中读入最多count个长度为size个字节的对象,放到buf指向的数组中。

返回值可能小于指定读入数,原因可能是出错,也可能是到达文件尾。实际执行状态可用feof()或ferror()确定。

 

1.5.2 fwrite

#include <stdio.h>
size_t fwrite(const void *buf, size_t size, size_t count,
FILE *stream);
返回:实际输出的对象数

把buf指向的数组中count个长度为size的对象输出到流stream中,并返回被输出的对象数。如果发生错误,则返回一个小于count的值。

 

1.6 文件定位函数

1.6.1 fseek

#include <stdio.h>
int fseek(FILE *stream, long int offset, int origin);
返回:成功为0,出错为非0

对流stream相关的文件定位,随后的读写操作将从新位置开始。

对于二进制文件,此位置被定位在由origin开始的offset个字符处。origin的值可能为SEEK_SET(文件开始处)、SEEK_CUR(当前位置)或SEEK_END(文件结束处)。

对于文本流,offset心须为0,或者是由函数ftell()返回的值(此时origin的值必须是SEEK_SET)。

 

1.6.2 ftell

#include <stdio.h>
long int ftell(FILE *stream);

返回与流stream相关的文件的当前位置。出错时返回-1L。

 

1.6.3 rewind

#include <stdio.h>
void rewind(FILE *stream);

rewind(fp)等价于fssek(fp, 0L, SEEK_SET)与clearerr(fp)这两个函数顺序执行的效果,即把与流stream相关的文件的当前位置移到开始处,同时清除与该流相关的文件尾标志和错误标志。

 

1.6.4 fgetpos

#include <stdio.h>
int fgetpos(FILE *stream, fpos_t *position);
返回:成功返回0,失败返回非0

把流stream的当前位置记录在*position中,供随后的fsetpos()调用时使用。

 

1.6.5 fsetpos

#include <stdio.h>
int fsetpos(FILE *stream, const fpos_t *position);
返回:成功返回0,失败返回非0

把流stream的位置定位到*position中记录的位置。*position的值是之前调用fgetpos()记录下来的。

 

1.7 错误处理函数

当发生错误或到达文件末尾时,标准库中的许多函数将设置状态指示符。这些状态指示符可被显式地设置和测试。另外,(定义在<errno.h>中的)整数表达式errno可包含一个出错序号,该数将进一步给出最近一次出错的信息。

1.7.1 clearerr

#include <stdio.h>
void clearerr(FILE *stream);

清除与流stream相关的文件结束指示符和错误指示符。

 

1.7.2 feof

#include <stdio.h>
int feof(FILE *stream);
返回:到达文件尾时返回非0值,否则返回0

与流stream相关的文件结束指示符被设置时,函数返回一个非0值。

 

1.7.3 ferror

#include <stdio.h>
int ferror(FILE *stream);
返回:无错返回0,有错返回非0

与流stream相关的文件出错指示符被设置时,函数返回一个非0值。

 

1.7.4 perror

#include <stdio.h>
void perror(const char *str);

perror(s)用于输出字符串s以及与全局变量errno中的整数值相对应的出错信息,具体出错信息的内容依赖于实现。该函数的功能类似于:

    fprintf(stderr, "%s: %s\n", s, "出错信息");

可参见第3节介绍的strerror函数。

 

 

2 字符类测试<ctype.h>

头文件<ctype.h>中说明了一些用于测试字符的函数。每个函数的变元均为int类型,变元的值必须是EOF或可用 unsigned char类型表示的字符,函数的返回值为int类型。如果变元满足所指定的条件,那么函数返回非0值(表示真);否则返回值为0(表示假)。这些函数包括 2.1~2.11。

在7位ASCII字符集中,可打印字符是从0x20(' ')到0x7E('~')之间的字符;控制字符是从0(NUL)到0x1F(US)之间的字符和字符0x7F(DEL)。

2.1 isalnum

#include <ctype.h>
int sialnum(int ch);

变元为字母或数字时,函数返回非0值,否则返回0。

 

2.2 isalpha

#include <ctype.h>
int isalpha(int ch);

当变元为字母表中的字母时,函数返回非0值,否则返回0。各种语言的字母表互不相同,对于英语来说,字母表由大写和小写的字母A到Z组成。

 

2.3 iscntrl

#include <ctype.h>
int iscntrl(int ch);

当变元是控制字符时,函数返回非0,否则返回0。

 

2.4 isdigit

#include <ctype.h>
int isdigit(int ch);

当变元是十进制数字时,函数返回非0值,否则返回0。

 

2.5 isgraph

#include <ctype.h>
int isgraph(int ch);

如果变元为除空格之外的任何可打印字符,则函数返回非0值,否则返回0。

 

2.6 islower

#include <ctype.h>
int islower(int ch);

如果变元是小写字母,函数返回非0值,否则返回0。

 

2.7 isprint

#include <ctype.h>
int isprint(int ch);

如果变元是可打印字符(含空格),则函数返回非0值,否则返回0。

 

2.8 ispunct

#include <ctype.h>
int ispunct(int ch);

如果变元是除空格、字母和数字外的可打印字符,则函数返回非0,否则返回0。

 

2.9 isspace

#include <ctype.h>
int isspace(int ch);

当变元为空白字符(包括空格、换页符、换行符、回车符、水平制表符和垂直制表符)时,函数返回非0,否则返回0。

 

2.10 isupper

#include <ctype.h>
int isupper(int ch);

如果变元为大写字母,函数返回非0,否则返回0。

 

2.11 isxdigit

#include <ctype.h>
int isxdigit(int ch);

当变元为十六进制数字时,函数返回非0,否则返回0。

 

2.12 tolower

#include <string.h>
int tolower(int ch);

当ch为大写字母时,返回其对应的小写字母;否则返回ch。

 

2.13 toupper

#include <string.h>
int toupper(int ch);

当ch为小写字母时,返回其对应的大写字母;否则返回ch。

 

 

3 字符串函数<string.h>

在头文件<string.h>中定义了两组字符串函数。第一组函数的名字以str开头;第二组函数的名字以mem开头。只有函数memmove对重叠对象间的拷贝进行了定义,而其他函数都未定义。比较类函数将其变元视为unsigned char类型的数组。

3.1 strcpy

#include <string.h>
char *strcpy(char *str1, const char *str2);

把字符串str2(包括'\0')拷贝到字符串str1当中,并返回str1。

 

3.2 strncpy

#include <string.h>
char *strncpy(char *str1, const char *str2, size_t count);

把字符串str2中最多count个字符拷贝到字符串str1中,并返回str1。如果str2中少于count个字符,那么就用'\0'来填充,直到满足count个字符为止。

 

3.3 strcat

#include <string.h>
char *strcat(char *str1, const char *str2);

把str2(包括'\0')拷贝到str1的尾部(连接),并返回str1。其中终止原str1的'\0'被str2的第一个字符覆盖。

 

3.4 strncat

#include <string.h>
char *strncat(char *str1, const char *str2, size_t count);

把str2中最多count个字符连接到str1的尾部,并以'\0'终止str1,返回str1。其中终止原str1的'\0'被str2的第一个字符覆盖。

注意,最大拷贝字符数是count+1。

 

3.5 strcmp

#include <string.h>
int strcmp(const char *str1, const char *str2);

按字典顺序比较两个字符串,返回整数值的意义如下:

  • 小于0,str1小于str2;
  • 等于0,str1等于str2;
  • 大于0,str1大于str2;

 

3.6 strncmp

#include <string.h>
int strncmp(const char *str1, const char *str2, size_t count);

同strcmp,除了最多比较count个字符。根据比较结果返回的整数值如下:

  • 小于0,str1小于str2;
  • 等于0,str1等于str2;
  • 大于0,str1大于str2;

 

3.7 strchr

#include <string.h>
char *strchr(const char *str, int ch);

返回指向字符串str中字符ch第一次出现的位置的指针,如果str中不包含ch,则返回NULL。

 

3.8 strrchr

#include <string.h>
char *strrchr(const char *str, int ch);

返回指向字符串str中字符ch最后一次出现的位置的指针,如果str中不包含ch,则返回NULL。

 

3.9 strspn

#include <string.h>
size_t strspn(const char *str1, const char *str2);

返回字符串str1中由字符串str2中字符构成的第一个子串的长度。

 

3.10 strcspn

#include <string.h>
size_t strcspn(const char *str1, const char *str2);

返回字符串str1中由不在字符串str2中字符构成的第一个子串的长度。

 

3.11 strpbrk

#include <string.h>
char *strpbrk(const char *str1, const char *str2);

返回指向字符串str2中的任意字符第一次出现在字符串str1中的位置的指针;如果str1中没有与str2相同的字符,那么返回NULL。

 

3.12 strstr

#include <string.h>
char *strstr(const char *str1, const char *str2);

返回指向字符串str2第一次出现在字符串str1中的位置的指针;如果str1中不包含str2,则返回NULL。

 

3.13 strlen

#include <string.h>
size_t strlen(const char *str);

返回字符串str的长度,'\0'不算在内。

 

3.14 strerror

#include <string.h>
char *strerror(int errnum);

返回指向与错误序号errnum对应的错误信息字符串的指针(错误信息的具体内容依赖于实现)。

 

3.15 strtok

#include <string.h>
char *strtok(char *str1, const char *str2);

在str1中搜索由str2中的分界符界定的单词。

对strtok()的一系列调用将把字符串str1分成许多单词,这些单词以str2中的字符为分界符。第一次调用时str1非空,它搜索 str1,找出由非str2中的字符组成的第一个单词,将str1中的下一个字符替换为'\0',并返回指向单词的指针。随后的每次strtok()调用 (参数str1用NULL代替),均从前一次结束的位置之后开始,返回下一个由非str2中的字符组成的单词。当str1中没有这样的单词时返回 NULL。每次调用时字符串str2可以不同。

如:

char *p;
p = strtok("The summer soldier,the sunshine patriot", " ");
printf("%s", p);
do {
    p = strtok("\0", ", "); /* 此处str2是逗号和空格 */
    if (p)
        printf("|%s", p);
} while (p);

显示结果是:The | summer | soldier | the | sunshine | patriot

 

3.16 memcpy

#include <string.h>
void *memcpy(void *to, const void *from, size_t count);

把from中的count个字符拷贝到to中。并返回to。

 

3.17 memmove

#include <string.h>
void *memmove(void *to, const void *from, size_t count);

功能与memcpy类似,不同之处在于,当发生对象重叠时,函数仍能正确执行。

 

3.18 memcmp

#include <string.h>
int memcmp(const void *buf1, const void *buf2, size_t count);

比较buf1和buf2的前count个字符,返回值与strcmp的返回值相同。

 

3.19 memchr

#include <string.h>
void *memchr(const void *buffer, int ch, size_t count);

返回指向ch在buffer中第一次出现的位置指针,如果在buffer的前count个字符当中找不到匹配,则返回NULL。

 

3.20 memset

#include <string.h>
void *memset(void *buf, int ch, size_t count);

把buf中的前count个字符替换为ch,并返回buf。

 

 

4 数学函数<math.h>

头文件<math.h>中说明了数学函数和宏。

宏EDOM和ERANGE(定义在头文件<errno.h>中)是两个非0整常量,用于引发各个数学函数的定义域错误和值域错 误;HUGE_VAL是一个double类型的正数。当变元取值在函数的定义域之外时,就会出现定义域错误。在发生定义域错误时,全局变量errno的值 被置为EDOM,函数的返回值视具体实现而定。如果函数的结果不能用double类型表示,那么就会发生值域错误。当结果上溢时,函数返回 HUGE_VAL并带有正确的符号(正负号),errno的值被置为ERANGE。当结果下溢时,函数返回0,而errno是否被设置为ERANGE视具 体实现而定。

4.1 sin

#include <math.h>
double sin(double arg);

返回arg的正弦值,arg单位为弧度。

 

4.2 cos

#include <math.h>
double cos(double arg);

返回arg的余弦值,arg单位为弧度。

 

4.3 tan

#include <math.h>
double tan(double arg);

返回arg的正切值,arg单位为弧度。

 

4.4 asin

#include <math.h>
double asin(double arg);

返回arg的反正弦值sin-1(x),值域为[-pi/2,pi/2], 其中变元范围[-1,1]。

 

4.5 acos

#include <math.h>
double acos(double arg);

返回arg的反余弦值cos-1(x),值域为[0,pi], 其中变元范围[-1,1]。

 

4.6 atan

#include <math.h>
double atan(double arg);

返回arg的反正切值tan-1(x),值域为[-pi/2,pi/2]。

 

4.7 atan2

#include <math.h>
double atan2(double a, double b);

返回a/b的反正切值tan-1(a/b),值域为[-pi,pi]。

 

4.8 sinh

#include <math.h>
double sinh(double arg);

返回arg的双曲正弦值。

 

4.9 cosh

#include <math.h>
double cosh(double arg);

返回arg的双曲余弦值。

 

4.10 tanh

#include <math.h>
double tanh(double arg);

返回arg的双曲正切值。

 

4.11 exp

#include <math.h>
double exp(double arg);

返回幂函数ex

 

4.12 log

#include <math.h>
double log(double arg);

返回自然对数ln(x),其中变元范围arg > 0。

 

4.13 log10

#include <math.h>
double log10(double arg);

返回以10为底的对数log10(x),其中变元范围arg > 0。

 

4.14 pow

#include <math.h>
double pow(double x, double y);

返回xy,如果x=0且y<=0或者如果x<0且y不是整数,那么产生定义域错误。

 

4.15 sqrt

#include <math.h>
double sqrt(double arg);

返回arg的平方根,其中变元范围arg>=0。

 

4.16 ceil

#include <math.h>
double ceil(double arg);

返回不小于arg的最小整数。

 

4.17 floor

#include <math.h>
double floor(double arg);

返回不大于arg的最大整数。

 

4.18 fabs

#include <math.h>
double fabs(double arg);

返回arg的绝对值|x|。

 

4.19 ldexp

#include <math.h>
double ldexp(double num, int exp);

返回num * 2exp

 

4.20 frexp

#include <math.h>
double frexp(double num, int *exp);

把num分成一个在[1/2,1)区间的真分数和一个2的幂数。将真分数返回,幂数保存在*exp中。如果num等于0,那么这两部分均为0。

 

4.21 modf

#include <math.h>
double modf(double num, double *i);

把num分成整数和小数两部分,两部分均与num有同样的正负号。函数返回小数部分,整数部分保存在*i中。

 

4.22 fmod

#include <math.h>
double fmod(double a, double b);

返回a/b的浮点余数,符号与a相同。如果b为0,那么结果由具体实现而定。

 

 

5 实用函数<stdlib.h>

在头文件<stdlib.h>中说明了用于数值转换、内存分配以及具有其他相似任务的函数。

5.1 atof

#include <stdlib.h>
double atof(const char *str);

把字符串str转换成double类型。等价于:strtod(str, (char**)NULL)。

 

5.2 atoi

#include <stdlib.h>
int atoi(const char *str);

把字符串str转换成int类型。等价于:(int)strtol(str, (char**)NULL, 10)。

 

5.3 atol

#include <stdlib.h>
long atol(const char *str);

把字符串str转换成long类型。等价于:strtol(str, (char**)NULL, 10)。

 

5.4 strtod

#include <stdlib.h>
double strtod(const char *start, char **end);

把字符串start的前缀转换成double类型。在转换中跳过start的前导空白符,然后逐个读入构成数的字符,任何非浮点数成分的字符都会终止上述过程。如果end不为NULL,则把未转换部分的指针保存在*end中。

如果结果上溢,返回带有适当符号的HUGE_VAL,如果结果下溢,那么函数返回0。在这两种情况下,errno均被置为ERANGE。

 

5.5 strtol

#include <stdlib.h>
long int strtol(const char *start, char **end, int radix);

把字符串start的前缀转换成long类型,在转换中跳过start的前导空白符。如果end不为NULL,则把未转换部分的指针保存在*end中。

如果radix的值在2到36间之间,那么转换按该基数进行;如果radix为0,则基数为八进制、十进制、十六进制,以0为前导的是八进制,以 0x或0X为前导的是十六进制。无论在哪种情况下,串中的字母是表示10到radix-1之间数字的字母。如果radix是16,可以加上前导0x或 0X。

如果结果上溢,则依据结果的符号返回LONG_MAX或LONG_MIN,置errno为ERANGE。

 

5.6 strtoul

#include <stdlib.h>
unsigned long int strtoul(const char *start, char **end,
int radix);

与strtol()类似,只是结果为unsigned long类型,溢出时值为ULONG_MAX。

 

5.7 rand

#include <stdlib.h>
int rand(void);

产生一个0到RAND_MAX之间的伪随机整数。RAND_MAX值至少为32767。

 

5.8 srand

#include <stdlib.h>
void srand(unsigned int seed);

设置新的伪随机数序列的种子为seed。种子的初值为1。

 

5.9 calloc

#include <stdlib.h>
void *calloc(size_t num, size_t size);

为num个大小为size的对象组成的数组分配足够的内存,并返回指向所分配区域的第一个字节的指针;如果内存不足以满足要求,则返回NULL。

分配的内存区域中的所有位被初始化为0。

 

5.10 malloc

#include <stdlib.h>
void *malloc(size_t size);

为大小为size的对象分配足够的内存,并返回指向所分配区域的第一个字节的指针;如果内存不足以满足要求,则返回NULL。

不对分配的内存区域进行初始化。

 

5.11 realloc

#include <stdlib.h>
void *realloc(void *ptr, size_t size);

将ptr指向的内存区域的大小改为size个字节。如果新分配的内存比原内存大,那么原内存的内容保持不变,增加的空间不进行初始化。如果新分配的 内存比原内存小,那么新内存保持原内存区中前size字节的内容。函数返回指向新分配空间的指针。如果不能满足要求,则返回NULL,原ptr指向的内存 区域保持不变。

如果ptr为NULL,则行为等价于malloc(size)。

如果size为0,则行为等价于free(ptr)。

 

5.12 free

#include <stdlib.h>
void free(void *ptr);

释放ptr指向的内存空间,若ptr为NULL,则什么也不做。ptr必须指向先前用动态分配函数malloc、realloc或calloc分配的空间。

 

5.13 abort

#include <stdlib.h>
void abort(void);

使程序非正常终止。其功能类似于raise(SIGABRT)。

 

5.14 exit

#include <stdlib.h>
void exit(int status);

使程序正常终止。atexit函数以与注册相反的顺序被调用,所有打开的文件被刷新,所有打开的流被关闭。status的值如何被返回依具体的实现而定,但用0表示正常终止,也可用值EXIT_SUCCESS和EXIT_FAILURE。

 

5.15 atexit

#include <stdlib.h>
int atexit(void (*func)(void));

注册在程序正常终止时所要调用的函数func。如果成功注册,则函数返回0值,否则返回非0值。

 

5.16 system

#include <stdlib.h>
int system(const char *str);

把字符串str传送给执行环境。如果str为NULL,那么在存在命令处理程序时,返回0值。如果str的值非NULL,则返回值与具体的实现有关。

 

5.17 getenv

#include <stdlib.h>
char *getenv(const char *name);

返回与name相关的环境字符串。如果该字符串不存在,则返回NULL。其细节与具体的实现有关。

 

5.18 bsearch

#include <stdlib.h>
void *bsearch(const void *key, const void *base, size_t n,
size_t size,	int (*compare)(const void *, const void *));

在base[0]...base[n-1]之间查找与*key匹配的项。size指出每个元素占有的字节数。函数返回一个指向匹配项的指针,若不存在匹配则返回NULL。

函数指针compare指向的函数把关键字key和数组元素比较,比较函数的形式为:

int func_name(const void *arg1, const void *arg2);

arg1是key指针,arg2是数组元素指针。

返回值必须如下:

  • arg1 < arg2时,返回值<0;
  • arg1 == arg2时,返回值==0;
  • arg1 > arg2时,返回值>0。

数组base必须按升序排列(与compare函数定义的大小次序一致)。

 

5.19 qsort

#include <stdlib.h>
void qsort(void *base, size_t n, size_t size, \
	int (*compare)(const void *, const void *));

对由n个大小为size的对象构成的数组base进行升序排序。

比较函数compare的形式如下:

int func_name(const void *arg1, const voie *arg2);

其返回值必须如下所示:

  • arg1 < arg2,返回值<0;
  • arg1 == arg2,返回值==0;
  • arg1 > arg2,返回值>0。

 

5.20 abs

#include <stdlib.h>
int abs(int num);

返回int变元num的绝对值。

 

5.21 labs

#include <stdlib.h>
long labs(long int num);

返回long类型变元num的绝对值。

 

5.22 div

#include <stdlib.h>
div_t div(int numerator, int denominator);

返回numerator/denominator的商和余数,结果分别保存在结构类型div_t的两个int成员quot和rem中。

 

5.23 ldiv

#include <stdlib.h>
ldiv_t div(long int numerator, long int denominator);

返回numerator/denominator的商和余数,结果分别保存在结构类型ldiv_t的两个long成员quot和rem中。

 

 

6 诊断<assert.h>

6.1 assert

#include <assert.h>
void assert(int exp);

assert宏用于为程序增加诊断功能。当assert(exp)执行时,如果exp为0,则在标准出错输出流stderr输出一条如下所示的信息:

    Assertion failed: expression, file filename, line nnn

然后调用abort终止执行。其中的源文件名filename和行号nnn来自于预处理宏__FILE__和__LINE__。

如果<assert.h>被包含时定义了宏NDEBUG,那么宏assert被忽略。

 

 

7 变长变元表<stdarg.h>

头文件<stdarg.h>中的说明提供了依次处理含有未知数目和类型的函数变元表的机制。

7.1 va_start

7.2 va_arg

7.3 va_end

#include <stdarg.h>
void va_start(va_list ap, lastarg);
type va_arg(va_list ap, type);
void va_end(va_list ap);

假设函数f含有可变数目的变元,lastarg是它的最后一个有名参数,然后在f内说明一个类型为va_list的变量ap,它将依次指向每个变元:

    va_list ap;

在访问任何未命名的变元前必须用va_start宏对ap进行初始化:

    va_start(ap, lastarg);

此后,宏va_arg的每次执行将产生一个与下一个未命名的变元有相同类型和值的值,它同时还修改ap,以使下一次使用va_arg时返回下一个变元:

    va_arg(ap, type);

当所有的变元处理完毕之后,f返回之前,必须调用一次宏va_end:

   va_end(ap);

例子:函数sum_series()的第一个参数是变元项数。

double sum_series(int num, … )
{
    double sum = 0.0, t;
    va_list ap;

    va_start(ap, num);
    for (; num; num--) {
        t = va_arg(ap, double);
        sum += t;
    }
    va_end(ap);
    return sum;
}

 

 

8 非局部跳转<setjmp.h>

头文件<setjmp.h>中的说明提供了一种避免通常的函数调用和返回顺序的途径,特别的,它允许立即从一个多层嵌套的函数调用中返回。

8.1 setjmp

#include <setjmp.h>
int setjmp(jmp_buf env);

setjmp()宏把当前状态信息保存到env中,供以后longjmp()恢复状态信息时使用。如果是直接调用setjmp(),那么返回值为 0;如果是由于调用longjmp()而调用setjmp(),那么返回值非0。setjmp()只能在某些特定情况下调用,如在if语句、switch 语句及循环语句的条件测试部分以及一些简单的关系表达式中。

 

8.2 longjmp

#include <setjmp.h>
void longjmp(jmp_buf env, int val);

longjmp()用于恢复由最近一次调用setjmp()时保存到env的状态信息。当它执行完时,程序就象setjmp()刚刚执行完并返回非 0值val那样继续执行。包含setjmp()宏调用的函数一定不能已经终止。所有可访问的对象的值都与调用longjmp()时相同,唯一的例外是,那 些调用setjmp()宏的函数中的非volatile自动变量如果在调用setjmp()后有了改变,那么就变成未定义的。

 

 

9 信号处理<signal.h>

头文件<signal.h>中提供了一些用于处理程序运行期间所引发的异常条件的功能,如处理来源于外部的中断信号或程序执行期间出现的错误等事件。

9.1 signal

#include <signal.h>
void (*signal(int sig, void (*handler)(int)))(int);

signal()用于确定以后当信号sig出现时的处理方法。如果handler的值是SIG_DFL,那么就采用实现定义的缺省行为;如果 handler的值是SIG_IGN,那么就忽略该信号;否则,调用handler所指向的函数(参数为信号类型)。有效的信号包括:

SIGABRT 异常终止,如调用abort()。
SIGFPE 算术运算出错,如除数为0或溢出。
SIGILL 非法函数映象,如非法指令。
SIGINT 交互式信号,如中断。
SIGSEGV 非法访问存储器,如访问不存在的内存单元。
SIGTERM 发送给本程序的终止请求信号。

signal()返回信号sig原来的的handler;如果出错,则返回SIG_ERR。

当随后出现信号sig时,就中断正在执行的操作,转而执行信号处理函数(*handler)(sig)。如果从信号处理程序中返回,则从中断的位置继续执行。

信号的初始状态由实现定义。

 

9.2 raise

#include <signal.h>
int raise(int sig);

向程序发送信号sig。如果发送不成功,就返回一个非0值。

 

 

10 日期与时间函数<time.h>

头文件<time.h>中说明了一些用于处理日期和时间的类型和函数。其中的一部分函数用于处理当地时间,因为时区等原因,当地时间与 日历时间可能不相同。clock_t和time_t是两个用于表示时间的算术类型,而struct tm则用于存放日历时间的各个成分。tm的各个成员的用途及取值范围如下:

    int tm_sec; /* 秒,0~61 */
    int tm_min; /* 分,0~59 */
    int tm_hour; /* 时,0~23 */
    int tm_mday; /* 日,1~31 */
    int tm_mon; /* 月(从1月开始),0~11 */
    int tm_year; /* 年(从1900年开始) */
    int tm_wday; /* 星期(从周日开始),0~6 */
    int tm_yday; /* 天数(从1月1日开始),0~365 */
    int tm_isdst; /* 夏令时标记 */

其中,tm_isdst在使用夏令时时其值为正,在不使用夏令时时其值为0,如果该信息不能使用,其值为负。

10.1 clock

#include <time.h>
clock_t clock(void);

返回程序自开始执行到目前为止所占用的处理机时间。如果处理机时间不可使用,那么返回-1。clock()/CLOCKS_PER_SEC是以秒为单位表示的时间。

 

10.2 time

#include <time.h>
time_t time(time_t *tp);

返回当前日历时间。如果日历时间不能使用,则返回-1。如果tp不为NULL,那么同时把返回值赋给*tp。

 

10.3 difftime

#include <time.h>
double difftime(time_t time2, time_t time1);

返回time2-time1的值(以秒为单位)。

 

10.4 mktime

#include <time.h>
time_t mktime(struct tm *tp);

将结构*tp中的当地时间转换为time_t类型的日历时间,并返回该时间。如果不能转换,则返回-1。

 

10.5 asctime

#include <time.h>
char *asctime(const struct tm *tp);

将结构*tp中的时间转换成如下所示的字符串形式:

    day month date hours:minutes:seconds year\n\0

如:

    Fri Apr 15 15:14:13 2005\n\0

返回指向该字符串的指针。字符串存储在可被其他调用重写的静态对象中。

 

10.6 ctime

#include <time.h>
char *ctime(const time_t *tp);

将*tp中的日历时间转换为当地时间的字符串,并返回指向该字符串指针。字符串存储在可被其他调用重写的静态对象中。等价于如下调用:

    asctime(localtime(tp))

 

10.7 gmtime

#include <time.h>
struct tm *gmtime(const time_t *tp);

将*tp中的日历时间转换成struct tm结构形式的国际标准时间(UTC),并返回指向该结构的指针。如果转换失败,返回NULL。结构内容存储在可被其他调用重写的静态对象中。

 

10.8 localtime

#include <time.h>
struct tm *localtime(const time_t *tp);

将*tp中的日历时间转换成struct tm结构形式的本地时间,并返回指向该结构的指针。结构内容存储在可被其他调用重写的静态对象中。

 

10.9 strftime

#include <time.h>
size_t strftime(char *s, size_t smax, const char *fmt, \
	const struct tm *tp);

根据fmt的格式说明把结构*tp中的日期与时间信息转换成指定的格式,并存储到s所指向的数组中,写到s中的字符数不能多于smax。函数返回实际写到s中的字符数(不包括'\0');如果产生的字符数多于smax,则返回0。

fmt类似于printf()中的格式说明,它由0个或多个转换规格说明与普通字符组成。普通字符原封不动的拷贝到s中,每个%c按照下面所描述的格式用与当地环境相适应的值来替换。转换规格列表如下:

格式 说明
%a   一星期中各天的缩写名
%A 一星期中各天的全名
%b 缩写月份名
%B 月份全名
%c 当地时间和日期表示
%d 用整数表示的一个月中的第几天(01~31)
%H 用整数表示的时(24小时制,00~23)
%I 用整数表示的时(12小时制,01~12)
%j 用整数表示的一年中各天(001~366)
%m 用整数表示的月份(01~12)
%M 用整数表示的分(00~59)
%p 与AM/PM对应的当地表示方法
%S 用整数表示的秒(00~61)
%U 用整数表示一年中的星期数(00~53,将星期日看作为每周的第一天)
%w 用整数表示一周中的各天(0~6,星期日为0)
%W 用整数表示一年中的星期数(00~53,将星期一看作为每周的第一天)
%x 当地日期表示
%X 当地时间表示
%y 不带公元的年(00~99)
%Y 完整年份表示
%Z 时区名字(可获得时)
%% %本身

 

 

11 由实现定义的限制<limits.h>和<float.h>

头文件<limits.h>中定义了用于表示整类型大小的常量。以下所列的值是可接受的最小值,实际系统中可能有更大的值。

CHAR_BIT 8 char类型的位数
CHAR_MAX UCHAR_MAXSCHAR_MAX   char类型的最大值
CHAR_MIN 0SCHAR_MIN char类型的最小值
INT_MAX 32767 int类型的最大值
INT_MIN -32767 int类型的最小值
LONG_MAX 2147483647 long的最大值
LONG_MIN -2147483647 long类型的最小值
SCHAR_MAX +127 signed char类型的最大值
SCHAR_MIN -127 signed char类型的最小值
SHRT_MAX +32767 short类型的最大值
SHRT_MIN -32767 short类型的最小值
UCHAR_MAX 255 unsigned char类型的最大值
UINT_MAX 65535 unsigned int类型的最大值
ULONG_MAX 4294967295 unsigned long的最大值
USHRT_MAX 65535 unsigned short的最大值

 

以下是<float.h>的一个子集,是与浮点算术运算相关的一些常量。给出的每个值代表相应量的一个最小取值。实际实现可以定义适当的值。

FLT_RADIX 2 指数表示的基数,如2、16
FLT_ROUNDS   加法的浮点舍入规则
FLT_DIG 6 float类型精度(小数位数)
FLT_EPSILON 1E-5 使“1.0 + x != 1.0”成立的最小x
FLT_MANT_DIG   基数为FLT_RADIX的尾数中的数字数
FLT_MAX 1E+37 最大浮点数
FLT_MAX_EXP   使FLT_RADIXn-1可表示的最大n
FLT_MIN 1E-37 最小的规范化浮点数
FLT_MIN_EXP   使10n为规范化数的最小n
DBL_DIG 10 double类型精度(小数位数)
DBL_EPSILON 1E-9 使“1.0 + x != 1.0”成立的最小x
DBL_MANT_DIG   基数为FLT_RADIX的尾数中的数字数
DBL_MAX 1E+37 最大双精度浮点数
DBL_MAX_EXP   使FLT_RADIXn-1可表示的最大n
DBL_MIN 1E-37 最小的规范化双精度浮点数
DBL_MIN_EXP   使10n为规范化数的最小n

 


编译内核操作流程 ──为新手指南

作者:北南南北
来自:LinuxSir.Org
提要: 编译内核主要是通过内核实现某些功能,比如iptables 需要内支持;所有的硬件的支持也是通过内核实现的;本文只是简单的给初学者讲一下编译内核的流程;


++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
正文
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


一、为什么要编译内核;

有时我们为了实现某些功能,比如对iptables的防火墙有些需要内核支持;还有实现一些硬件的支持等,这时我们需要重编内核;


二、内核源码版本的选择;

如果发行版本提供内核源码,最好还是用发行版本提供的;比如Fedora 4.0 提供了很多版本的内核源码;我们安装好后,他自带的配置文件大多能满足我们的需要,对于新手来说,根据自带的内核的配置文件.config ,我们也能学到一点如何配置内核。也没有什么难的,熟能生巧罢了;

如果从 kernel.org 下载最新稳定版本的内核也是可以的;


三、内核源码安装或解压;

对于Fedora Core 4.0 来说,内核源码是放在 /usr/src/kernels 目录中;如果通过在线升级内核,也是放在这个目录中;如果您的系统中的 /usr/src/kernels/ 中没有内容,说明您没有安装内核的源码包 kernel-devel 软件包;

您可以通过软件包管理器来补装 ,请参考 《Fedora / Redhat 软件包管理指南》

如果您用Fedora Core 4.0 ,我建议您在保留老内核的基础上,在线下载最新版本的内核 kerenl-devel 和对应版本的 kernel 或者从 kernel.org 下载,有时可能新的kerenl 已经解决了您所面对的问题,这时就没有必要编译内核了;

如果您是通过在线安装的内核源码包 ,比如通过 apt+synaptic 或者yum 安装的,内核源码会被放到/usr/src/kernel 下的目录中,您要进入相对应的目录进行编译;

在线更新软件包,请参考 《Fedora / Redhat 软件包管理指南》,推荐 apt+synaptic 工具;

如果您是是下载kernel 和kernel-devel 的rpm 包,可以通过来安装;

[root@localhost beinan]# rpm -ivh kernel*.rpm

如果您是从kernel.org 下载的类似 linux-2.6.13.tar.bz2 或者 linux-2.6.13.tar.gz的,您要把下载下来的文件移到 /usr/src 目录中解压; 然后进入解压的目录中进行配置和编译;

[root@localhost beinan]# mv linux-2.6.13.tar.bz2
[root@localhost beinan]# cd /usr/src/
[root@localhost src]# tar jxvf linux-2.6.12.3.tar.bz2

提示:本文以linux-2.6.12.3为例,其实通过发行版的升级版的 kernel-devel 和从kernel.org 下载下来的差不多,大同小异;不同的是通过在线升级的rpm格式的内核源码包,有配置文件.config 可以参考;安装到的目的地是/usr/src/kernel 对FC 4.0来说;如果您要高编译和配置内核,


四、内核的配置;


1、进入目录执行 make mrproper ,对于从 kernel.org 下载而来的tar.bz格式的源码包;

[root@localhost src]# cd linux-2.6.12.3/
[root@localhost linux-2.6.12.3]#
[root@localhost src]# cd linux-2.6.12.3/
[root@localhost linux-2.6.12.3]# make mrproper
[root@localhost linux-2.6.12.3]# make menuconfig

对于Fedora Core 4.0 ,如果您是通过在线安装的 kernel和kernel-devel 新版本的包,比如是2.6.12-1.1398_FC4-i686,你可以直进入 /usr/src/kernel/相应的目录中直接执行 make menuconfig ;利用发行版本提供的 .config 来配置,这样方便点。不要make mrproper ,否则.config 就没有了;这也是为什么要用发行版本提供的内核源码升级包的原因;

其它配置和安装大同小异;模仿总可以吧;


2.简要的配置内核;

进入配置内核的配置模式后,我们可能一无所知,看下面;

内核配置有两种方法,一种是直接置入内核 * ;另一种是编成模块 M ;两种方法各有优点;直接编入内核的,比如设备的启动,不再需要加载模块的这一过程了;而编译成模块,则需要加载设备的内核支持的模块;但直接把所有的东 西都编入内核也不是可行的,内核体积会变大,系统负载也会过重。我们编内核时最好把极为重要的编入内核;其它的如果您不明白的,最好用默认。


1)移动键盘上下左右键,按Enter 进入一个目录。把指针移动到Exit就退出当前目录到上级目录;

2)针对自己机器存在的问题进行修改,比如大内存的支持;

选择自己机器的CPU;

移动键盘到 Processor type and features ---> ,然后按ENTER进入;
找到 Processor family (Pentium-Pro) ---> 按ENTER进入;

进入后我们发现有好多CPU的型号可选;一般的情况下要根据
bash-3.00# cat /proc/cpuinfo 输出的信息来选,比如我们的是Celeron (P4)一代的,应该选如下的,当然默认的 486也是可以正常运行的,既然我们重编一次内核,就得选中对应型号的,也许性能有所提高呢;

Processor family (Pentium-4/Celeron(P4-based)/Pentium-4 M/Xeon)

 

对大内存支持;如果内存是1G或者1G以上,但小于4G的,就要选4G支持;如果超过4G的,要选64G的支持;

High Memory Support (4GB) --->

(X) 4GB
( ) 64GB

还有比如声卡等硬件,需要我们一步一步的查看;如果有不明之处,就要按 [shift]+?的组合键来查看说明。一般的情况下,2.6.x的内核会根据机器的情况自动配出一个文件,只需要我们来查看一下,把重要的地方改改就行了;

再举个例子:比如我现在所用的声卡是intel ac97的,我应该怎么配置呢?

首先要知道自己的声卡的芯片组,我们要通过lspci -v 来查看;

[root@localhost beinan]#lspci -v

 

只查看声卡的,应该用如下的方法:

 

[root@localhost beinan]# lspci -v |grep audio
00:1f.5 Multimedia audio controller: Intel Corp. 82801DB (ICH4) AC'97 Audio Controller (rev 03)

通过上面的输出,我们知道这台机器用的是intel AC97声卡;所以我们要特别注意AC97的配置;

找到 Device Drivers ---> Sound --->
<M> Sound card support 声卡的支持,这个是一定要选中的吧;
<M> Advanced Linux Sound Architecture 对声卡支持的ALSA驱动的支持;
下面有OSS驱动,只是一部份。如果想用OSS的驱动更全的,可以去买;其它的就看如下的选吧;
<M> Sequencer support
<M> Sequencer dummy client
<M> OSS Mixer API
<M> OSS PCM (digital audio) API[*] OSS Sequencer API
<M> RTC Timer support[*] Verbose printk
[ ] Debug

大多是默认的就好,如果您不知道是做什么用的,或者怎么用;

然后我们再向下看有

Generic devices ---> 进入里面
<M> Dummy (/dev/null) soundcard
<M> Virtual MIDI soundcard
<M> MOTU MidiTimePiece AV multiport MIDI
<M> UART16550 serial MIDI driver
<M> Generic MPU-401 UART driver
ISA devices ---> 如果您用ISA声卡就在这里面选;
PCI devices ---> 如果您用PCI声卡,就在这里面选,集成声卡也在这里;
USB devices ---> 这是USB声卡内核支持选项;我有一个这样的声卡,但没有试过;
PCMCIA devices ---> 这是PCMCIA声卡的选项,我还没有看过这样的声卡呢;如果您有,就在这里面动动手吧。

 

因为我用的是Intel 集成的声卡,所以要在PCI中选择,我们在 中可以看到有两个与INTEL有关的;

 

<M> Intel/SiS/nVidia/AMD/ALi AC97 Controller 这个才是Intel AC97声卡的;
< > Intel/SiS/nVidia/AMD MC97 Modem (EXPERIMENTAL) 这个是机器集成的INTEL猫的蜂鸣器的;

 

因为我发现如果把猫的蜂鸣器的驱动也选上,可能造成两个冲突。所以只能选上面的那个;

我们再回到 Open Sound System ---> 中看看,与我用的声卡是不是有关的?

<M> Open Sound System (DEPRECATED)
<M> Intel ICH (i8xx) audio support
<M> OSS sound modules
<M> Loopback MIDI device support
<M> Microsoft Sound System support

 

我们也可以看到Open Sound System中也有好多的声卡驱动,大家根据前面的lspci -v 来选择吧。


3)对于操作系统所采用的文件系统的支持要编入内核,最好不要编成模块;(重要)

比如我的Fedora core 4.0 所采用的文件系统用的是ext3 ,所以我要把它直接编入内核;好处是不受模块丢失或者损坏而不能启动系统;而有时您把系统所采用的文件系统编译成模块,出现VFS错误,也有这方面的事, 可能是您没有把ext3加入到相应的加载模块的配置文件中,所以我们为了减少麻烦,把风险降到最低,还是要直接置入内模的好;

File systems --->
<*> Ext3 journalling file system support
[*] Ext3 extended attributes
[*] Ext3 POSIX Access Control Lists
[*] Ext3 Security Labels

 

如果您还有其它的硬盘分区要读取,比如是reiserfs、ext2、fat、fat32、ntfs等,这样的可以编成模块来支持;

再举一例:如果您的的操作系统用的是reiserfs的文件系统,当然就要把reiserfs的直接编入内核,其它的可以编成模块来支持了;


4)对于硬盘及RAID的支持,要直接编入内核;

比如ATA、SATA、SCSI及RAID的支持直接内核支持;有时编完内核后,启动时不能识别硬盘和RAID,大多事情出在这里;Slackware中 在这方面有的是模块支持,我们可以把它由模块M改成内核*来支持; 如果您不明白,就按默认进行;SATA的硬盘的支持除了选中SATA的支持、IDE设备的支持以外,还要选中SCSI的支持;

5)对于咱们所没有的设备,可以在内核中不选,熟能生巧罢了;

比如我没有ISDN设备 ,所以就把ISDN去掉;

ISDN subsystem --->
< > Linux telephony support

如果您没有1394的设备 ,当然可以把1394的支持也去掉;等等。。。。。。。

如果您有USB的设备,要把USB方面好好看看;比如大家常用的移动硬盘;USB猫等,还有扫描仪等;

内核配置就说这么多吧,太多了,我也说不清楚,水平有限啊;

配置好后先要保存

Save Configuration to an Alternate File

出来一个

Enter a filename to which this configuration ,should be saved as an alternate. Leave blank to abort.
.config

按回车就行了,这样就保存住了;

然后退出 Exit ,这时也会出现保存 ;

如果你想把.config保存起来,可以再复制一份到安全一点的目录,以备后用;

五、编译内核;

[root@localhost linux-2.6.12.3]# make
[root@localhost linux-2.6.12.3]# make modules_install

这样就编译好了,并把模块也安装在了 /lib/modules目录中了,请看:

[root@localhost linux-2.6.12.3]# ls /lib/modules/

2.6.11-1.1369_FC4   2.6.12.3


六、安装内核及配置grub或lilo ;


1、复制bzImage等相关文件,并创建initrd文件;

[root@localhost linux-2.6.12.3]# cp arch/i386/boot/bzImage /boot/vmlinuz-2.6.12.3
[root@localhost linux-2.6.12.3]# cp System.map /boot/System.map-2.6.12.3
[root@localhost linux-2.6.12.3]# cd /boot
[root@localhost linux-2.6.12.3]# /sbin/mkinitrd initrd-2.6.12.3.img   2.6.12.3

我们把 编译出来的bzImage,拷入到/boot目录,拷贝成 vmlinuz-2.6.12.3;
并且用 mkinitrd 来创建imitrd-xxx.img 文件,其中xxx为内核的版本号,是通过 查看 /lib/modules来版本来对应的,我们是编译出来的是 2.6.12.3,所以就运行上面的命令创建,创建的出来的是initrd-2.6.12.3.img ;不创建这个文件,有时是启动不起来的,比如提示VFS错误等;


2、查看系统引导管理器grub或者lilo的配置文件。

如果想要让新内核能让系统引导管理器grub和lilo的菜单上能看得到,必须改 grub.conf或者lilo.conf,但我们必须保留老内核的在grub和lilo的启动菜单,毕竟我们编内核不能百分百的成功,对不对??安全第一吧;

我只说grub的,我没有lilo,也不会用。所以咱们还是GRUB吧,FC 4 系统引导管理器是 GRUB,所以我们谈谈GRUB的设置;

查看 /etc/grub.conf;

比如我的grub.conf的内容是这样的;

# grub.conf generated by anaconda
#
# Note that you do not have to rerun grub after making changes to this file
# NOTICE:  You do not have a /boot partition.  This means that
#          all kernel and initrd paths are relative to /, eg.
#          root (hd0,7)
#          kernel /boot/vmlinuz-version ro root=/dev/hda8
#          initrd /boot/initrd-version.img
#boot=/dev/hda
default=0
timeout=5
#splashimage=(hd0,7)/boot/grub/splash.xpm.gz
#hiddenmenu
title Fedora Core (2.6.11-1.1369_FC4)
        root (hd0,7)
        kernel /boot/vmlinuz-2.6.11-1.1369_FC4 ro root=LABEL=/ rhgb quiet
        initrd /boot/initrd-2.6.11-1.1369_FC4.img
title WinXP
        rootnoverify (hd0,0)
        chainloader +1

我们要把老内核的启动保留下来,以防不测,我们只加入新的内核的启动;所以我加上这样一段;

title Fedora Core (2.6.12.3)
root (hd0,7)
kernel /boot/vmlinuz-2.6.12.3 ro root=LABEL=/ rhgb quiet
initrd /boot/initrd-2.6.12.3.img

咱们再来看一下改过后的配置文件;

# grub.conf generated by anaconda
#
# Note that you do not have to rerun grub after making changes to this file
# NOTICE:  You do not have a /boot partition.  This means that
#          all kernel and initrd paths are relative to /, eg.
#          root (hd0,7)
#          kernel /boot/vmlinuz-version ro root=/dev/hda8
#          initrd /boot/initrd-version.img
#boot=/dev/hda
default=0
timeout=5
#splashimage=(hd0,7)/boot/grub/splash.xpm.gz
#hiddenmenu

title Fedora Core (2.6.12.3)
        root (hd0,7)
        kernel /boot/vmlinuz-2.6.12.3 ro root=LABEL=/ rhgb quiet
        initrd /boot/initrd-2.6.12.3.img


title Fedora Core (2.6.11-1.1369_FC4)
        root (hd0,7)
        kernel /boot/vmlinuz-2.6.11-1.1369_FC4 ro root=LABEL=/ rhgb quiet
        initrd /boot/initrd-2.6.11-1.1369_FC4.img
title WinXP
        rootnoverify (hd0,0)
        chainloader +1

然后重新启动机器,如果出现VFS错误,可能就是我写重要的那个地方出了错误;

注意:如果重新启用内核后,原来安装的显示卡驱动,比如 NVIDIA和ATI的驱动还要重新安装;