碧海潮生's profile碧海潮生的小屋PhotosBlogLists Tools Help

Blog


    May 25

    设计笔记——标准库(上)

    “为了问心无愧地得到报酬,你也应该真正掌握第II组
    (指职业程序员必须掌握的标准库)的内容。”
    —— 《C and C++ Code Capsules》

    日常的C代码大部分由库调用组成,甚至I/O程序如printf和scanf
    都是库的一部分,而不是语言。因此,了解标准库势在必行。


    1 从memmove谈起

    在某个项目开发过程中,为了完成帧封装/解封装协议,
    我在代码中使用了memmove函数实现数据块的搬移。

    这部分代码在VC++ 6.0下运行正常,
    但是当我们将这些代码移植到嵌入式系统上时,问题出现了。

    可能是出于实现效率的考虑,我们的固件工程师决定自己实现memmmove。
    给出的代码大致如下:

    void *memmove(void *dst, const void *src, size_t len)
    {
        char *det_ptr;
        const char *src_ptr;

        if (len <= 0)
            return dst;

        dst_ptr = dst;
        src_ptr = src;

        while (len--)
            *dst_ptr++ = *src_ptr++;

        return dest;
    }

    应该说,若需要实现的是memcpy函数,那么这段代码是相当漂亮的。
    但是,标准库的memmove函数说明中明确指出:该函数应能处理
    当“源单元和目的单元缓冲区交迭”的情况。

    而我在编码时也恰恰是因为源单元和目的单元缓冲区交迭,
    才选用了这个函数,而没有采用memcpy。

    于是,当用上面这样的实现代替库函数时,程序的结果就出现了错误。
    当意识到这个问题时,你的第一反应可能是“难道要用malloc?!”。
    答案是否定的!正确且优雅的实现应当是:

    void *memmove(void *dst, const void *src, size_t len)
    {
        char *dst_ptr;
        const char *src_ptr;

        if (len <= 0)
            return dst;

        dst_ptr = dst;
        src_ptr = src;

        if (dst_ptr < src_ptr)
        {
            while (len--)
                *dst_ptr++ = *src_ptr++;
        }
        else
        {
            dst_ptr += len;
            src_ptr += len;

            while (len--)
                *--dst_ptr = *--src_ptr;
        }

        return dst;
    }

    引起这个问题的根本原因是对标准库函数的定义和行为不熟悉。
    学习一门语言不光要熟悉语言本身,对开发环境提供的库函数也要有所了解。

    切记,一个函数的错误实现可能导致整个项目的失败。


    2 C标准库

    标准C库由在15个头文件中声明的函数、类型定义和宏组成。可分为三组。

    第I组:每个C程序员都应该知道的知识
    <ctype.h>    字符处理
    <stdio.h>    输入/输出
    <stdlib.h>    复杂的工具
    <string.h>    文本处理

    第II组:职业程序员的工具
    <assert.h>    断言支持
    <limits.h>    整型系统参数
    <stddef.h>    通用类型和常量
    <time.h>    时间处理

    第III组:需要时可以发挥大作用的工具
    <errno.h>    错误检测
    <float.h>    浮点型系统参数
    <locale.h>    本地化
    <math.h>    数学函数
    <setjmp.h>    非局部跳转
    <signal.h>    信号(中断)处理
    <stdarg.h>    可变参数表

    以下,仅对常用的四个库(ctype.h、stdlib.h、string.h和math.h)作简要说明。


    3 <ctype.h>

    3.1 字符测试函数

    1> 函数原型均为int isxxxx(int)
    2> 参数为int, 任何实参均被提升成整型
    3> 只能正确处理处于[0, 127]之间的值

    isalpha  'A'~'Z', 'a'~'z'
    isdigit  '0'~'9'
    isxdigit '0'~'9', 'A'~'F', 'a'~'f'
    isalnum  isalpha || isdigit

    islower  'a'~'z'
    isupper  'A'~'Z'
       
    isspace
    9  '\t'  (tab: 水平制表位)
    10 '\n'  (line feed: 换行)
    11 '\v'  (home: 垂直制表位)
    12 '\f'  (form feed: 换页)
    13 '\r'  (carriage return: 回车)
    32  ' '  (space: 空格)
       
    isgraph  33~126
    isprint  isgraph || ' '
    ispunct  isgraph && !isalnum
       
    iscntrl  0~31, 127


    3.2 字符映射函数

    1> 函数原型为int toxxxx(int)
    2> 对参数进行检测, 若符合范围则转换, 否则不变

    int    tolower(int);    'A'~'Z' ==> 'a'~'z'
    int    toupper(int);    'a'~'z' ==> 'A'~'Z'


    3.3 附测试代码

    #include <stdio.h>
    #include <ctype.h>

    char (* isxx_name[]) =
    {   
        "isalpha", "isdigit", "isxdigit", "isalnum",
        "islower", "isupper", "isspace",
        "isgraph", "isprint", "ispunct", "iscntrl"
    };

    int (* isxx_list[])(int) =
    {
        isalpha, isdigit, isxdigit, isalnum,
        islower, isupper, isspace,
        isgraph, isprint, ispunct, iscntrl
    };

    int main()
    {
        int i, k;
       
        for (i=0; i<sizeof(isxx_list)/sizeof(isxx_list[0]); i++)
        {
            printf("Testing %s\n", isxx_name[i]);
            for (k=0; k<128; k++)
                if (isxx_list[i](k))
                    printf("%3d %c\n", k, k);
            printf("\n");
        }   
       
        return 0;
    }

    Comments (3)

    Please wait...
    Sorry, the comment you entered is too long. Please shorten it.
    You didn't enter anything. Please try again.
    Sorry, we can't add your comment right now. Please try again later.
    To add a comment, you need permission from your parent. Ask for permission
    Your parent has turned off comments.
    Sorry, we can't delete your comment right now. Please try again later.
    You've exceeded the maximum number of comments that can be left in one day. Please try again in 24 hours.
    Your account has had the ability to leave comments disabled because our systems indicate that you may be spamming other users. If you believe that your account has been disabled in error please contact Windows Live support.
    Complete the security check below to finish leaving your comment.
    The characters you type in the security check must match the characters in the picture or audio.

    To add a comment, sign in with your Windows Live ID (if you use Hotmail, Messenger, or Xbox LIVE, you have a Windows Live ID). Sign in


    Don't have a Windows Live ID? Sign up

    Oct. 21
    Oct. 21
    Sept. 3

    Trackbacks

    The trackback URL for this entry is:
    http://jx-kingwei.spaces.live.com/blog/cns!F7A152EB74B9576E!1504.trak
    Weblogs that reference this entry
    • None