程序14-9 linux/include/string.h


  1 #ifndef _STRING_H_

  2 #define _STRING_H_

  3

  4 #ifndef NULL

  5 #define NULL ((void *) 0)

  6 #endif

  7

  8 #ifndef _SIZE_T

  9 #define _SIZE_T

 10 typedef unsigned int size_t;

 11 #endif

 12

 13 extern char * strerror(int errno);

 14

 15 /*

 16  * This string-include defines all string functions as inline

 17  * functions. Use gcc. It also assumes ds=es=data space, this should be

 18  * normal. Most of the string-functions are rather heavily hand-optimized,

 19  * see especially strtok,strstr,str[c]spn. They should work, but are not

 20  * very easy to understand. Everything is done entirely within the register

 21  * set, making the functions fast and clean. String instructions have been

 22  * used through-out, making for "slightly" unclear code :-)

 23  *

 24  *              (C) 1991 Linus Torvalds

 25  */

    /*

     * 这个字符串头文件以内嵌函数的形式定义了所有字符串操作函数。使用gcc时,同时

     * 假定了ds=es=数据空间,这应该是常规的。绝大多数字符串函数都是经手工进行大量

     * 优化的,尤其是函数strtokstrstrstr[c]spn。它们应该能正常工作,但却不是那

     * 么容易理解。所有的操作基本上都是使用寄存器集来完成的,这使得函数即快又整洁。

     * 所有地方都使用了字符串指令,这又使得代码“稍微”难以理解J

     *

     *              (C) 1991 Linus Torvalds

     */

 26 

    //// 将一个字符串(src)拷贝到另一个字符串(dest),直到遇到NULL字符后停止。

    // 参数:dest - 目的字符串指针,src - 源字符串指针。

    // %0 - esi(src)%1 - edi(dest)

 27 extern inline char * strcpy(char * dest,const char *src)

 28 {

 29 __asm__("cld\n"                      // 清方向位。

 30         "1:\tlodsb\n\t"              // 加载DS:[esi]1字节èal,并更新esi

 31         "stosb\n\t"                  // 存储字节alèES:[edi],并更新edi

 32         "testb %%al,%%al\n\t"        // 刚存储的字节是0

 33         "jne 1b"                     // 不是则向后跳转到标号1处,否则结束。

 34         ::"S" (src),"D" (dest):"si","di","ax");

 35 return dest;                         // 返回目的字符串指针。

 36 }

 37

    //// 拷贝源字符串count个字节到目的字符串。

    // 如果源串长度小于count个字节,就附加空字符(NULL)到目的字符串。

    // 参数:dest - 目的字符串指针,src - 源字符串指针,count - 拷贝字节数。

    // %0 - esi(src)%1 - edi(dest)%2 - ecx(count)

 38 extern inline char * strncpy(char * dest,const char *src,int count)

 39 {

 40 __asm__("cld\n"                      // 清方向位。

 41         "1:\tdecl %2\n\t"            // 寄存器ecx--count--)。

 42         "js 2f\n\t"                  // 如果count<0则向前跳转到标号2,结束。

 43         "lodsb\n\t"                  // ds:[esi]1字节èal,并且esi++

 44         "stosb\n\t"                  // 存储该字节èes:[edi],并且edi++

 45         "testb %%al,%%al\n\t"        // 该字节是0

 46         "jne 1b\n\t"                 // 不是,则向前跳转到标号1处继续拷贝。

 47         "rep\n\t"                    // 否则,在目的串中存放剩余个数的空字符。

 48         "stosb\n"

 49         "2:"

 50         ::"S" (src),"D" (dest),"c" (count):"si","di","ax","cx");

 51 return dest;                         // 返回目的字符串指针。

 52 }

 53

    //// 将源字符串拷贝到目的字符串的末尾处。

    // 参数:dest - 目的字符串指针,src - 源字符串指针。

    // %0 - esi(src)%1 - edi(dest)%2 - eax(0)%3 - ecx(-1)

 54 extern inline char * strcat(char * dest,const char * src)

 55 {

 56 __asm__("cld\n\t"                    // 清方向位。

 57         "repne\n\t"                  // 比较ales:[edi]字节,并更新edi++

 58         "scasb\n\t"                  // 直到找到目的串中是0的字节,此时edi已指向后1字节。

 59         "decl %1\n"                  // es:[edi]指向0值字节。

 60         "1:\tlodsb\n\t"              // 取源字符串字节ds:[esi]èal,并esi++

 61         "stosb\n\t"                  // 将该字节存到es:[edi],并edi++

 62         "testb %%al,%%al\n\t"        // 该字节是0

 63         "jne 1b"                     // 不是,则向后跳转到标号1处继续拷贝,否则结束。

 64         ::"S" (src),"D" (dest),"a" (0),"c" (0xffffffff):"si","di","ax","cx");

 65 return dest;                         // 返回目的字符串指针。

 66 }

 67

    //// 将源字符串的count个字节复制到目的字符串的末尾处,最后添一空字符。

    // 参数:dest - 目的字符串,src - 源字符串,count - 欲复制的字节数。

    // %0 - esi(src)%1 - edi(dest)%2 - eax(0)%3 - ecx(-1)%4 - (count)

 68 extern inline char * strncat(char * dest,const char * src,int count)

 69 {

 70 __asm__("cld\n\t"                    // 清方向位。

 71         "repne\n\t"                  // 比较ales:[edi]字节,edi++

 72         "scasb\n\t"                  // 直到找到目的串的末端0值字节。

 73         "decl %1\n\t"                // edi指向该0值字节。

 74         "movl %4,%3\n"               // 欲复制字节数èecx

 75         "1:\tdecl %3\n\t"            // ecx--(从0开始计数)。

 76         "js 2f\n\t"                  // ecx <0 ?,是则向前跳转到标号2处。

 77         "lodsb\n\t"                  // 否则取ds:[esi]处的字节èalesi++

 78         "stosb\n\t"                  // 存储到es:[edi]处,edi++

 79         "testb %%al,%%al\n\t"        // 该字节值为0

 80         "jne 1b\n"                   // 不是则向后跳转到标号1处,继续复制。

 81         "2:\txorl %2,%2\n\t"         // al清零。

 82         "stosb"                      // 存到es:[edi]处。

 83         ::"S" (src),"D" (dest),"a" (0),"c" (0xffffffff),"g" (count)

 84         :"si","di","ax","cx");

 85 return dest;                         // 返回目的字符串指针。

 86 }

 87

    //// 将一个字符串与另一个字符串进行比较。

    // 参数:cs - 字符串1ct - 字符串2

    // %0 - eax(__res)返回值,%1 - edi(cs)字符串1指针,%2 - esi(ct)字符串2指针。

    // 返回:如果串1 > 2,则返回1;串1 = 2,则返回0;串1 < 2,则返回-1

    // 90行定义了一个局部寄存器变量。该变量将被保存在eax寄存器中,以便于高效访问和操作。

    // 这种定义变量的方法主要用于内嵌汇编程序中。详细说明参见gcc手册指定寄存器中的变量

 88 extern inline int strcmp(const char * cs,const char * ct)

 89 {

 90 register int __res __asm__("ax");    // __res是寄存器变量(eax)

 91 __asm__("cld\n"                      // 清方向位。

 92         "1:\tlodsb\n\t"              // 取字符串2的字节ds:[esi]èal,并且esi++

 93         "scasb\n\t"                  // al与字符串1的字节es:[edi]作比较,并且edi++

 94         "jne 2f\n\t"                 // 如果不相等,则向前跳转到标号2

 95         "testb %%al,%%al\n\t"        // 该字节是0值字节吗(字符串结尾)?

 96         "jne 1b\n\t"                 // 不是,则向后跳转到标号1,继续比较。

 97         "xorl %%eax,%%eax\n\t"       // 是,则返回值eax清零,

 98         "jmp 3f\n"                   // 向前跳转到标号3,结束。

 99         "2:\tmovl $1,%%eax\n\t"      // eax中置1

100         "jl 3f\n\t"                  // 若前面比较中串2字符<1字符,则返回正值结束。

101         "negl %%eax\n"               // 否则eax = -eax,返回负值,结束。

102         "3:"

103         :"=a" (__res):"D" (cs),"S" (ct):"si","di");

104 return __res;                        // 返回比较结果。

105 }

106

    //// 字符串1与字符串2的前count个字符进行比较。

    // 参数:cs - 字符串1ct - 字符串2count - 比较的字符数。

    // %0 - eax(__res)返回值,%1 - edi(cs)1指针,%2 - esi(ct)2指针,%3 - ecx(count)

    // 返回:如果串1 > 2,则返回1;串1 = 2,则返回0;串1 < 2,则返回-1

107 extern inline int strncmp(const char * cs,const char * ct,int count)

108 {

109 register int __res __asm__("ax");    // __res是寄存器变量(eax)

110 __asm__("cld\n"                      // 清方向位。

111         "1:\tdecl %3\n\t"            // count--

112         "js 2f\n\t"                  // 如果count<0,则向前跳转到标号2

113         "lodsb\n\t"                  // 取串2的字符ds:[esi]èal,并且esi++

114         "scasb\n\t"                  // 比较al与串1的字符es:[edi],并且edi++

115         "jne 3f\n\t"                 // 如果不相等,则向前跳转到标号3

116         "testb %%al,%%al\n\t"        // 该字符是NULL字符吗?

117         "jne 1b\n"                   // 不是,则向后跳转到标号1,继续比较。

118         "2:\txorl %%eax,%%eax\n\t"   // NULL字符,则eax清零(返回值)。

119         "jmp 4f\n"                   // 向前跳转到标号4,结束。

120         "3:\tmovl $1,%%eax\n\t"      // eax中置1

121         "jl 4f\n\t"                  // 如果前面比较中串2字符<1字符,则返回1结束。

122         "negl %%eax\n"               // 否则eax = -eax,返回负值,结束。

123         "4:"

124         :"=a" (__res):"D" (cs),"S" (ct),"c" (count):"si","di","cx");

125 return __res;                        // 返回比较结果。

126 }

127

    //// 在字符串中寻找第一个匹配的字符。

    // 参数:s - 字符串,c - 欲寻找的字符。

    // %0 - eax(__res)%1 - esi(字符串指针s)%2 - eax(字符c)

    // 返回:返回字符串中第一次出现匹配字符的指针。若没有找到匹配的字符,则返回空指针。

128 extern inline char * strchr(const char * s,char c)

129 {

130 register char * __res __asm__("ax"); // __res是寄存器变量(eax)

131 __asm__("cld\n\t"                    // 清方向位。

132         "movb %%al,%%ah\n"           // 将欲比较字符移到ah

133         "1:\tlodsb\n\t"              // 取字符串中字符ds:[esi]èal,并且esi++

134         "cmpb %%ah,%%al\n\t"         // 字符串中字符al与指定字符ah相比较。

135         "je 2f\n\t"                  // 若相等,则向前跳转到标号2处。

136         "testb %%al,%%al\n\t"        // al中字符是NULL字符吗?(字符串结尾?)

137         "jne 1b\n\t"                 // 若不是,则向后跳转到标号1,继续比较。

138         "movl $1,%1\n"               // 是,则说明没有找到匹配字符,esi1

139         "2:\tmovl %1,%0\n\t"         // 将指向匹配字符后一个字节处的指针值放入eax

140         "decl %0"                    // 将指针调整为指向匹配的字符。

141         :"=a" (__res):"S" (s),"0" (c):"si");

142 return __res;                        // 返回指针。

143 }

144

    //// 寻找字符串中指定字符最后一次出现的地方。(反向搜索字符串)

    // 参数:s - 字符串,c - 欲寻找的字符。

    // %0 - edx(__res)%1 - edx(0)%2 - esi(字符串指针s)%3 - eax(字符c)

    // 返回:返回字符串中最后一次出现匹配字符的指针。若没有找到匹配的字符,则返回空指针。

145 extern inline char * strrchr(const char * s,char c)

146 {

147 register char * __res __asm__("dx"); // __res是寄存器变量(edx)

148 __asm__("cld\n\t"                    // 清方向位。

149         "movb %%al,%%ah\n"           // 将欲寻找的字符移到ah

150         "1:\tlodsb\n\t"              // 取字符串中字符ds:[esi]èal,并且esi++

151         "cmpb %%ah,%%al\n\t"         // 字符串中字符al与指定字符ah作比较。

152         "jne 2f\n\t"                 // 若不相等,则向前跳转到标号2处。

153         "movl %%esi,%0\n\t"          // 将字符指针保存到edx中。

154         "decl %0\n"                  // 指针后退一位,指向字符串中匹配字符处。

155         "2:\ttestb %%al,%%al\n\t"    // 比较的字符是0吗(到字符串尾)?

156         "jne 1b"                     // 不是则向后跳转到标号1处,继续比较。

157         :"=d" (__res):"0" (0),"S" (s),"a" (c):"ax","si");

158 return __res;                        // 返回指针。

159 }

160

    //// 在字符串1中寻找第1个字符序列,该字符序列中的任何字符都包含在字符串2中。

    // 参数:cs - 字符串1指针,ct - 字符串2指针。

    // %0 - esi(__res)%1 - eax(0)%2 - ecx(-1)%3 - esi(1指针cs)%4 - (2指针ct)

    // 返回字符串1中包含字符串2中任何字符的首个字符序列的长度值。

161 extern inline int strspn(const char * cs, const char * ct)

162 {

163 register char * __res __asm__("si"); // __res是寄存器变量(esi)

164 __asm__("cld\n\t"                    // 清方向位。

165         "movl %4,%%edi\n\t"          // 首先计算串2的长度。串2指针放入edi中。

166         "repne\n\t"                  // 比较al(0)与串2中的字符(es:[edi]),并edi++

167         "scasb\n\t"                  // 如果不相等就继续比较(ecx逐步递减)

168         "notl %%ecx\n\t"             // ecx中每位取反。

169         "decl %%ecx\n\t"             // ecx--,得串2的长度值。

170         "movl %%ecx,%%edx\n"         // 将串2的长度值暂放入edx中。

171         "1:\tlodsb\n\t"              // 取串1字符ds:[esi]èal,并且esi++

172         "testb %%al,%%al\n\t"        // 该字符等于0值吗(串1结尾)?

173         "je 2f\n\t"                  // 如果是,则向前跳转到标号2处。

174         "movl %4,%%edi\n\t"          // 取串2头指针放入edi中。

175         "movl %%edx,%%ecx\n\t"       // 再将串2的长度值放入ecx中。

176         "repne\n\t"                  // 比较al与串2中字符es:[edi],并且edi++

177         "scasb\n\t"                  // 如果不相等就继续比较。

178         "je 1b\n"                    // 如果相等,则向后跳转到标号1处。

179         "2:\tdecl %0"                // esi--,指向最后一个包含在串2中的字符。

180         :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)

181         :"ax","cx","dx","di");

182 return __res-cs;                     // 返回字符序列的长度值。

183 }

184

    //// 寻找字符串1中不包含字符串2中任何字符的首个字符序列。

    // 参数:cs - 字符串1指针,ct - 字符串2指针。

    // %0 - esi(__res)%1 - eax(0)%2 - ecx(-1)%3 - esi(1指针cs)%4 - (2指针ct)

    // 返回字符串1中不包含字符串2中任何字符的首个字符序列的长度值。

185 extern inline int strcspn(const char * cs, const char * ct)

186 {

187 register char * __res __asm__("si"); // __res是寄存器变量(esi)

188 __asm__("cld\n\t"                    // 清方向位。

189         "movl %4,%%edi\n\t"          // 首先计算串2的长度。串2指针放入edi中。

190         "repne\n\t"                  // 比较al(0)与串2中的字符(es:[edi]),并edi++

191         "scasb\n\t"                  // 如果不相等就继续比较(ecx逐步递减)

192         "notl %%ecx\n\t"             // ecx中每位取反。

193         "decl %%ecx\n\t"             // ecx--,得串2的长度值。

194         "movl %%ecx,%%edx\n"         // 将串2的长度值暂放入edx中。

195         "1:\tlodsb\n\t"              // 取串1字符ds:[esi]èal,并且esi++

196         "testb %%al,%%al\n\t"        // 该字符等于0值吗(串1结尾)?

197         "je 2f\n\t"                  // 如果是,则向前跳转到标号2处。

198         "movl %4,%%edi\n\t"          // 取串2头指针放入edi中。

199         "movl %%edx,%%ecx\n\t"       // 再将串2的长度值放入ecx中。

200         "repne\n\t"                  // 比较al与串2中字符es:[edi],并且edi++

201         "scasb\n\t"                  // 如果不相等就继续比较。

202         "jne 1b\n"                   // 如果不相等,则向后跳转到标号1处。

203         "2:\tdecl %0"                // esi--,指向最后一个包含在串2中的字符。

204         :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)

205         :"ax","cx","dx","di");

206 return __res-cs;                     // 返回字符序列的长度值。

207 }

208

    //// 在字符串1中寻找首个包含在字符串2中的任何字符。

    // 参数:cs - 字符串1的指针,ct - 字符串2的指针。

    // %0 -esi(__res)%1 -eax(0)%2 -ecx(0xffffffff)%3 -esi(1指针cs)%4 -(2指针ct)

    // 返回字符串1中首个包含字符串2中字符的指针。

209 extern inline char * strpbrk(const char * cs,const char * ct)

210 {

211 register char * __res __asm__("si"); // __res是寄存器变量(esi)

212 __asm__("cld\n\t"                    // 清方向位。

213         "movl %4,%%edi\n\t"          // 首先计算串2的长度。串2指针放入edi中。

214         "repne\n\t"                  // 比较al(0)与串2中的字符(es:[edi]),并edi++

215         "scasb\n\t"                  // 如果不相等就继续比较(ecx逐步递减)

216         "notl %%ecx\n\t"             // ecx中每位取反。

217         "decl %%ecx\n\t"             // ecx--,得串2的长度值。

218         "movl %%ecx,%%edx\n"         // 将串2的长度值暂放入edx中。

219         "1:\tlodsb\n\t"              // 取串1字符ds:[esi]èal,并且esi++

220         "testb %%al,%%al\n\t"        // 该字符等于0值吗(串1结尾)?

221         "je 2f\n\t"                  // 如果是,则向前跳转到标号2处。

222         "movl %4,%%edi\n\t"          // 取串2头指针放入edi中。

223         "movl %%edx,%%ecx\n\t"       // 再将串2的长度值放入ecx中。

224         "repne\n\t"                  // 比较al与串2中字符es:[edi],并且edi++

225         "scasb\n\t"                  // 如果不相等就继续比较。

226         "jne 1b\n\t"                 // 如果不相等,则向后跳转到标号1处。

227         "decl %0\n\t"                // esi--,指向一个包含在串2中的字符。

228         "jmp 3f\n"                   // 向前跳转到标号3处。

229         "2:\txorl %0,%0\n"           // 没有找到符合条件的,将返回值为NULL

230         "3:"

231         :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)

232         :"ax","cx","dx","di");

233 return __res;                        // 返回指针值。

234 }

235

    //// 在字符串1中寻找首个匹配整个字符串2的字符串。

    // 参数:cs - 字符串1的指针,ct - 字符串2的指针。

    // %0 -eax(__res)%1 -eax(0)%2 -ecx(0xffffffff)%3 -esi(1指针cs)%4 -(2指针ct)

    // 返回:返回字符串1中首个匹配字符串2的字符串指针。

236 extern inline char * strstr(const char * cs,const char * ct)

237 {

238 register char * __res __asm__("ax"); // __res是寄存器变量(eax)

239 __asm__("cld\n\t" \                  // 清方向位。

240         "movl %4,%%edi\n\t"          // 首先计算串2的长度。串2指针放入edi中。

241         "repne\n\t"                  // 比较al(0)与串2中的字符(es:[edi]),并edi++

242         "scasb\n\t"                  // 如果不相等就继续比较(ecx逐步递减)

243         "notl %%ecx\n\t"             // ecx中每位取反。

244         "decl %%ecx\n\t"    /* NOTE! This also sets Z if searchstring='' */

                                /* 注意!如果搜索串为空,将设置Z标志 */ // 得串2的长度值。

245         "movl %%ecx,%%edx\n"         // 将串2的长度值暂放入edx中。

246         "1:\tmovl %4,%%edi\n\t"      // 取串2头指针放入edi中。

247         "movl %%esi,%%eax\n\t"       // 将串1的指针复制到eax中。

248         "movl %%edx,%%ecx\n\t"       // 再将串2的长度值放入ecx中。

249         "repe\n\t"                   // 比较串1和串2字符(ds:[esi],es:[edi])esi++,edi++

250         "cmpsb\n\t"                  // 若对应字符相等就一直比较下去。

251         "je 2f\n\t"         /* also works for empty string, see above */

                                /* 对空串同样有效,见上面 */ // 若全相等,则转到标号2

252         "xchgl %%eax,%%esi\n\t"      // 1头指针èesi,比较结果的串1指针èeax

253         "incl %%esi\n\t"             // 1头指针指向下一个字符。

254         "cmpb $0,-1(%%eax)\n\t"      // 1指针(eax-1)所指字节是0吗?

255         "jne 1b\n\t"                 // 不是则转到标号1,继续从串1的第2个字符开始比较。

256         "xorl %%eax,%%eax\n\t"       // eax,表示没有找到匹配。

257         "2:"

258         :"=a" (__res):"0" (0),"c" (0xffffffff),"S" (cs),"g" (ct)

259         :"cx","dx","di","si");

260 return __res;                        // 返回比较结果。

261 }

262

    //// 计算字符串长度。

    // 参数:s - 字符串。

    // %0 - ecx(__res)%1 - edi(字符串指针s)%2 - eax(0)%3 - ecx(0xffffffff)

    // 返回:返回字符串的长度。

263 extern inline int strlen(const char * s)

264 {

265 register int __res __asm__("cx");    // __res是寄存器变量(ecx)

266 __asm__("cld\n\t"                    // 清方向位。

267         "repne\n\t"                  // al(0)与字符串中字符es:[edi]比较,

268         "scasb\n\t"                  // 若不相等就一直比较。

269         "notl %0\n\t"                // ecx取反。

270         "decl %0"                    // ecx--,得字符串得长度值。

271         :"=c" (__res):"D" (s),"a" (0),"0" (0xffffffff):"di");

272 return __res;                        // 返回字符串长度值。

273 }

274

275 extern char * ___strtok;     // 用于临时存放指向下面被分析字符串1(s)的指针。

276

    //// 利用字符串2中的字符将字符串1分割成标记(tokern)序列。

    // 将串1看作是包含零个或多个单词(token)的序列,并由分割符字符串2中的一个或多个字符

    // 分开。第一次调用 strtok()时,将返回指向字符串1中第1token首字符的指针,并在返

    // token时将一 null字符写到分割符处。后续使用 null 作为字符串1的调用,将用这种方

    // 法继续扫描字符串1,直到没有token 为止。在不同的调用过程中,分割符串2可以不同。

    // 参数:s - 待处理的字符串1ct - 包含各个分割符的字符串2

    // 汇编输出:%0 - ebx(__res)%1 - esi(__strtok)

    // 汇编输入:%2 - ebx(__strtok)%3 - esi(字符串1指针s)%4 - (字符串2指针ct)

    // 返回:返回字符串s中第1token,如果没有找到token,则返回一个null指针。

    // 后续使用字符串s指针为null的调用,将在原字符串s中搜索下一个token

277 extern inline char * strtok(char * s,const char * ct)

278 {

279 register char * __res __asm__("si");

280 __asm__("testl %1,%1\n\t"            // 首先测试esi(字符串1指针s)是否是NULL

281         "jne 1f\n\t"                 // 如果不是,则表明是首次调用本函数,跳转标号1

282         "testl %0,%0\n\t"            // 若是NULL,表示此次是后续调用,测ebx(__strtok)

283         "je 8f\n\t"                  // 如果ebx指针是NULL,则不能处理,跳转结束。

284         "movl %0,%1\n"               // ebx指针复制到esi

285         "1:\txorl %0,%0\n\t"         // ebx指针。

286         "movl $-1,%%ecx\n\t"         // ecx = 0xffffffff

287         "xorl %%eax,%%eax\n\t"       // 清零eax

288         "cld\n\t"                    // 清方向位。

289         "movl %4,%%edi\n\t"          // 下面求字符串2的长度。edi指向字符串2

290         "repne\n\t"                  // al(0)es:[edi]比较,并且edi++

291         "scasb\n\t"                  // 直到找到字符串2的结束null字符,或计数ecx==0

292         "notl %%ecx\n\t"             // ecx取反,

293         "decl %%ecx\n\t"             // ecx--,得到字符串2的长度值。

294         "je 7f\n\t"                  /* empty delimeter-string */

                                         /* 分割符字符串空 */ // 若串2长度为0,则转标号7

295         "movl %%ecx,%%edx\n"         // 将串2长度暂存入edx

296         "2:\tlodsb\n\t"              // 取串1的字符ds:[esi]èal,并且esi++

297         "testb %%al,%%al\n\t"        // 该字符为0值吗(1结束)

298         "je 7f\n\t"                  // 如果是,则跳转标号7

299         "movl %4,%%edi\n\t"          // edi再次指向串2首。

300         "movl %%edx,%%ecx\n\t"       // 取串2的长度值置入计数器ecx

301         "repne\n\t"                  // al中串1的字符与串2中所有字符比较,

302         "scasb\n\t"                  // 判断该字符是否为分割符。

303         "je 2b\n\t"                  // 若能在串2中找到相同字符(分割符),则跳转标号2

304         "decl %1\n\t"                // 若不是分割符,则串1指针esi指向此时的该字符。

305         "cmpb $0,(%1)\n\t"           // 该字符是NULL字符吗?

306         "je 7f\n\t"                  // 若是,则跳转标号7处。

307         "movl %1,%0\n"               // 将该字符的指针esi存放在ebx

308         "3:\tlodsb\n\t"              // 取串1下一个字符ds:[esi]èal,并且esi++

309         "testb %%al,%%al\n\t"        // 该字符是NULL字符吗?

310         "je 5f\n\t"                  // 若是,表示串1结束,跳转到标号5

311         "movl %4,%%edi\n\t"          // edi再次指向串2首。

312         "movl %%edx,%%ecx\n\t"       // 2长度值置入计数器ecx

313         "repne\n\t"                  // al中串1的字符与串2中每个字符比较,

314         "scasb\n\t"                  // 测试al字符是否是分割符。

315         "jne 3b\n\t"                 // 若不是分割符则跳转标号3,检测串1中下一个字符。

316         "decl %1\n\t"                // 若是分割符,则esi--,指向该分割符字符。

317         "cmpb $0,(%1)\n\t"           // 该分割符是NULL字符吗?

318         "je 5f\n\t"                  // 若是,则跳转到标号5

319         "movb $0,(%1)\n\t"           // 若不是,则将该分割符用NULL字符替换掉。

320         "incl %1\n\t"                // esi指向串1中下一个字符,也即剩余串首。

321         "jmp 6f\n"                   // 跳转标号6处。

322         "5:\txorl %1,%1\n"           // esi清零。

323         "6:\tcmpb $0,(%0)\n\t"       // ebx指针指向NULL字符吗?

324         "jne 7f\n\t"                 // 若不是,则跳转标号7

325         "xorl %0,%0\n"               // 若是,则让ebx=NULL

326         "7:\ttestl %0,%0\n\t"        // ebx指针为NULL吗?

327         "jne 8f\n\t"                 // 若不是则跳转8,结束汇编代码。

328         "movl %0,%1\n"               // esi置为NULL

329         "8:"

330         :"=b" (__res),"=S" (___strtok)

331         :"0" (___strtok),"1" (s),"g" (ct)

332         :"ax","cx","dx","di");

333 return __res;                        // 返回指向新token的指针。

334 }

335

    //// 内存块复制。从源地址src处开始复制n个字节到目的地址dest处。

    // 参数:dest - 复制的目的地址,src - 复制的源地址,n - 复制字节数。

    // %0 - ecx(n)%1 - esi(src)%2 - edi(dest)

336 extern inline void * memcpy(void * dest,const void * src, int n)

337 {

338 __asm__("cld\n\t"                    // 清方向位。

339         "rep\n\t"                    // 重复执行复制ecx个字节,

340         "movsb"                      // ds:[esi]es:[edi]esi++edi++

341         ::"c" (n),"S" (src),"D" (dest)

342         :"cx","si","di");

343 return dest;                         // 返回目的地址。

344 }

345

    //// 内存块移动。同内存块复制,但考虑移动的方向。

    // 参数:dest - 复制的目的地址,src - 复制的源地址,n - 复制字节数。

    // dest<src则:%0 - ecx(n)%1 - esi(src)%2 - edi(dest)

    // 否则:%0 - ecx(n)%1 - esi(src+n-1)%2 - edi(dest+n-1)

    // 这样操作是为了防止在复制时错误地重叠覆盖。

346 extern inline void * memmove(void * dest,const void * src, int n)

347 {

348 if (dest<src)

349 __asm__("cld\n\t"                    // 清方向位。

350         "rep\n\t"                    // ds:[esi]es:[edi],并且esi++edi++

351         "movsb"                      // 重复执行复制ecx字节。

352         ::"c" (n),"S" (src),"D" (dest)

353         :"cx","si","di");

354 else

355 __asm__("std\n\t"                    // 置方向位,从末端开始复制。

356         "rep\n\t"                    // ds:[esi]es:[edi],并且esi--edi--

357         "movsb"                      // 复制ecx个字节。

358         ::"c" (n),"S" (src+n-1),"D" (dest+n-1)

359         :"cx","si","di");

360 return dest;

361 }

362

    //// 比较n个字节的两块内存(两个字符串),即使遇上NULL字节也不停止比较。

    // 参数:cs - 内存块1地址,ct - 内存块2地址,count - 比较的字节数。

    // %0 - eax(__res)%1 - eax(0)%2 - edi(内存块1)%3 - esi(内存块2)%4 - ecx(count)

    // 返回:若块1>2 返回1;块1<2,返回-1;块1==2,则返回0

363 extern inline int memcmp(const void * cs,const void * ct,int count)

364 {

365 register int __res __asm__("ax");    // __res是寄存器变量。

366 __asm__("cld\n\t"                    // 清方向位。

367         "repe\n\t"                   // 如果相等则重复,

368         "cmpsb\n\t"                  // 比较ds:[esi]es:[edi]的内容,并且esi++edi++

369         "je 1f\n\t"                  // 如果都相同,则跳转到标号1,返回0(eax)

370         "movl $1,%%eax\n\t"          // 否则eax1

371         "jl 1f\n\t"                  // 若内存块2内容的值<内存块1,则跳转标号1

372         "negl %%eax\n"               // 否则eax = -eax

373         "1:"

374         :"=a" (__res):"0" (0),"D" (cs),"S" (ct),"c" (count)

375         :"si","di","cx");

376 return __res;                        // 返回比较结果。

377 }

378

    //// n字节大小的内存块(字符串)中寻找指定字符。

    // 参数:cs - 指定内存块地址,c - 指定的字符,count - 内存块长度。

    // %0 - edi(__res)%1 - eax(字符c)%2 - edi(内存块地址cs)%3 - ecx(字节数count)

    // 返回第一个匹配字符的指针,如果没有找到,则返回NULL字符。

379 extern inline void * memchr(const void * cs,char c,int count)

380 {

381 register void * __res __asm__("di"); // __res是寄存器变量。

382 if (!count)                          // 如果内存块长度==0,则返回NULL,没有找到。

383         return NULL;

384 __asm__("cld\n\t"                    // 清方向位。

385         "repne\n\t"                  // 如果不相等则重复执行下面语句,

386         "scasb\n\t"                  // al中字符与es:[edi]字符作比较,并且edi++

387         "je 1f\n\t"                  // 如果相等则向前跳转到标号1处。

388         "movl $1,%0\n"               // 否则edi中置1

389         "1:\tdecl %0"                // edi指向找到的字符(或是NULL)。

390         :"=D" (__res):"a" (c),"D" (cs),"c" (count)

391         :"cx");

392 return __res;                        // 返回字符指针。

393 }

394

    //// 用字符填写指定长度内存块。

    // 用字符c填写s指向的内存区域,共填count字节。

    // %0 - eax(字符c)%1 - edi(内存地址)%2 - ecx(字节数count)

395 extern inline void * memset(void * s,char c,int count)

396 {

397 __asm__("cld\n\t"                    // 清方向位。

398         "rep\n\t"                    // 重复ecx指定的次数,执行

399         "stosb"                      // al中字符存入es:[edi]中,并且edi++

400         ::"a" (c),"D" (s),"c" (count)

401         :"cx","di");

402 return s;

403 }

404

405 #endif

406