51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

编写简单的C运行库(二)

  在前面编写简单的C运行库(一)中主要实现了调用main函数前的初始化、获取参数和环境变量、退出程序等工作。接下来我们真正实现c标准库中的一些函数(主要是文件操作、字符串操作函数)。不过我们对这些函数的实现力争简单,对于效率方面考虑的不是很多,因为目的主要还是学习神秘的库是怎么实现的。

文件操作 {#文件操作}

  c中的标准I/O库都是带有缓存的,我们在这里为了实现的简单,将缓存省略了,直接包装了有关文件操作的系统调用。现在我们直接看文件打开的函数:

|---------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 | static int open(const char *pathname, int flags, int mode) { int ret; __asm__ volatile( "int $0x80" :"=a"(ret) :"0"(5),"b"(pathname),"c"(flags),"d"(mode) ); if (ret >= 0) return ret; return -1; } |

  open函数中直接调用了嵌入汇编调用了系统调用。对于系统调用的返回值,如果是负数,直接返回-1,否则直接返回。这个函数是系统调用的一个包装,本质其实就是个系统调用。然后我们在open函数的基础上实现c标志库函数中的fopen函数。

|---------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | FILE *fopen(const char *path, const char *mode) { int fd = -1; int flags = 0; int access = 00700; /*创建文件的权限*/ if (strcmp(mode, "w") == 0) flags |= O_WRONLY | O_CREAT | O_TRUNC; if (strcmp(mode, "w+") == 0) flags |= O_RDWR | O_CREAT | O_TRUNC; if (strcmp(mode, "r") == 0) flags |= O_RDONLY; if (strcmp(mode, "r+") == 0) flags |= O_RDWR | O_CREAT; fd = open(path, flags, access); return (FILE *)fd; } |

  由于我没有像标志I/O库那样实现缓存,所以我直接把FILE定义为int型,这样我们用FILE就相当于用了文件描述符。从上面的代码中可以知道我设置了文件的创建权限只有文件创建者有读写执行的权限,还有就是我只实现了以只读、只写、读写方式打开文件,对于追加等方式没有实现。然后函数read、fread和write、fwrite都可以用相同的方式实现,还有fputc,fputs也是已一样的。

输出函数 {#输出函数}

  I/O函数中比较麻烦的要属实现printf、fprintf这些可变参数的函数,当然这些函数都是调用vfprintf函数实现的,所以只要实现了vfprintf函数,其它的函数实现就比较简单了。

  首先来看下我实现的vfprintf函数代码:

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | int vfprintf(FILE *stream, const char *format, va_list ap) { int n = 0, flag = 0, ret; char str[20]; while (*format) { switch (*format) { case '%': if (flag == 1) { fputc('%', stream); flag = 0; n ++; } else flag = 1; break; case 'd': if (flag == 1) { itoa((int)va_arg(ap, int), str, 10); ret = fputs(str, stream); n += ret; } else { fputc('d', stream); n ++; } flag = 0; break; case 's': if (flag == 1) { ret = fputs((char *)va_arg(ap, char *), stream); n += ret; } else { fputc('s', stream); n ++; } flag = 0; break; case '\n': /*换行*/ fputc(0x0d, stream); n ++; fputc(0x0a, stream); n ++; break; default: fputc(*format, stream); n ++; } format ++; } return n; } |

  vfprintf主要麻烦的是对格式化字符串的分析,我们在这里使用一种比较简单的算法:

  (1)定义模式:翻译模式/普通模式

  (2)循环整个格式字符串

    a) 如果遇到%

      i 普通模式:进入翻译模式

      ii 翻译模式: 输出%, 退出翻译模式

    b) 如果遇到%后面允许出现的特殊字符(如d和s)

      i 翻译模式:从不定参数中取出一个参数输出,退出翻译模式

      ii 普通模式:直接输出该字符串

    c) 如果遇到其它字符(除\n):无条件退出翻译模式并输出字符

    d) 如果遇到'\n'字符,如果直接输出是不能达到换行的效果的,必须要同时输出回车换行才行

  从上面的实现vfprintf的代码中可以看出,并不支持特殊的格式控制符,例如位数、进度控制等,仅支持%d与%s这样的简单转换。真正的vfprintf格式化字符串实现比较复杂,因为它支持诸如"%f"、"%x"已有的各种格式、位数、精度控制等。我觉得上面实现的代码已经充分的展示了vfprintf的实现原理和它的关键技巧,所以没有必要一个一个的都实现。现在来实现printf的就简单多了,下面是printf的实现代码:

|------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 | int printf(const char *format, ...) { int n; va_list ap; va_start(ap, format); n = vfprintf(stdout, format, ap); va_end(ap); return n; } |

  对于可变参数的编程,将在C语言中的可变参数编程中详细讲解,包括它的实现原理。所以只要了解了可变参数的编程,对于实现printf函数来说就真的没什么难度了,纯粹就是调用vfprintf函数而已。如果实现了printf函数,那么对于实现scanf、fscanf也是同样的原理。

stdio.c参考代码 {#stdio-c参考代码}

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | #include "minicrt.h" int CrtInitIo() { return 0; } static int open(const char *pathname, int flags, int mode) { int ret; __asm__ volatile( "int $0x80" :"=a"(ret) :"0"(5),"b"(pathname),"c"(flags),"d"(mode) ); if (ret >= 0) return ret; return -1; } static int write(int fd, const void *buf, int count) { int ret; __asm__ volatile( "int $0x80" :"=a"(ret) :"0"(4),"b"(fd),"c"(buf),"d"(count) ); if (ret >= 0) return ret; return -1; } static int read(int fd, void *buf, int count) { int ret; __asm__ volatile( "int $0x80" :"=a"(ret) :"0"(3),"b"(fd),"c"(buf),"d"(count) ); if (ret >= 0) return ret; return -1; } static int close(int fd) { int ret; __asm__ volatile( "int $0x80" :"=a"(ret) :"0"(6),"b"(fd) ); if (ret >= 0) return ret; return -1; } int lseek(int fd, int offset, int whence) { int ret; __asm__ volatile( "int $0x80" :"=a"(ret) :"0"(19),"b"(fd),"c"(offset),"d"(whence) ); if (ret >= 0) return ret; return -1; } int fread(void *ptr, int size, int nmemb, FILE *stream) { return read((int)stream, ptr, size * nmemb); } int fwrite(const void *ptr, int size, int nmemb, FILE *stream) { return write((int)stream, ptr, size * nmemb); } FILE *fopen(const char *path, const char *mode) { int fd = -1; int flags = 0; int access = 00700; /*创建文件的权限*/ if (strcmp(mode, "w") == 0) flags |= O_WRONLY | O_CREAT | O_TRUNC; if (strcmp(mode, "w+") == 0) flags |= O_RDWR | O_CREAT | O_TRUNC; if (strcmp(mode, "r") == 0) flags |= O_RDONLY; if (strcmp(mode, "r+") == 0) flags |= O_RDWR | O_CREAT; fd = open(path, flags, access); return (FILE *)fd; } int fclose(FILE *fp) { return close((int)fp); } int fseek(FILE *stream, long offset, int whence) { return lseek((int)stream, offset, whence); } int fputc(int c, FILE *stream) { if (fwrite(&c, 1, 1, stream) != 1) return EOF; else return c; } int fputs(const char *s, FILE *stream) { int len; len = strlen(s); if (fwrite(s, 1, len, stream) != len) return EOF; else return len; } int vfprintf(FILE *stream, const char *format, va_list ap) { int n = 0, flag = 0, ret; char str[20]; while (*format) { switch (*format) { case '%': if (flag == 1) { fputc('%', stream); flag = 0; n ++; } else flag = 1; break; case 'd': if (flag == 1) { itoa((int)va_arg(ap, int), str, 10); ret = fputs(str, stream); n += ret; } else { fputc('d', stream); n ++; } flag = 0; break; case 's': if (flag == 1) { ret = fputs((char *)va_arg(ap, char *), stream); n += ret; } else { fputc('s', stream); n ++; } flag = 0; break; case '\n': /*换行*/ fputc(0x0d, stream); n ++; fputc(0x0a, stream); n ++; break; default: fputc(*format, stream); n ++; } format ++; } return n; } int fprintf(FILE *stream, const char *format, ...) { int n; va_list ap; va_start(ap, format); n = vfprintf(stream, format, ap); va_end(ap); return n; } int printf(const char *format, ...) { int n; va_list ap; va_start(ap, format); n = vfprintf(stdout, format, ap); va_end(ap); return n; } |

相关文章:

参考链接:https://www.cnblogs.com/chengxuyuancc/archive/2013/06/07/3122772.html


赞(0)
未经允许不得转载:工具盒子 » 编写简单的C运行库(二)