在前面编写简单的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参考代码

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