g-bios标准string库实现
针对g-bios标准库中的string库函数的实现,及其注意事项,心得体会进行讨论。
这里先来说一下我对strcpy(), strncpy()两个函数的体会。
char *strcpy(char *dest, const char *src)
{
char *pdst = dest;
#if 1 // 参考代码
while (*pdst = *src)
{
pdst++;
src++;
}
#else // 我的代码
while (*src)
{
*pdst++ = *src++;
}
*pdst = '\0';
#endif
return dest;
}
strcpy()是个库函数,实现字符串的拷贝。由于会被频繁调用,因此代码精简度要求高。函数内部实现不需要参数校验,由调用者保证参数的正确传递。这个函数调用者需确保目标空间大于或等于源空间。传入的两个参数中,src源设置为const只读,确保在函数内源数据不被修改。
接下来是代码精简。比较我写的代码与参考代码,我觉得while (*pdst = *src) 比我的while (*src)要好的多。前者既赋了值,又完成了条件判断,相当精简。
char *strncpy(char *dst, const char *src, size_t count)
{
char *pdst = dst;
#if 0 // 修改前代码
size_t n = 0;
while (n < count && *src != '\0')
{
*pdst++ = *src++;
n++;
}
while (n < count)
{
*pdst++ = '\0';
n++;
}
#else // 修改后代码
while ((count > 0) && (*pdst = *src))
{
pdst++;
src++;
count--;
}
while (count > 0)
{
*pdst++ = '\0';
count--;
}
#endif
return dst;
}
strncpy()这个库函数跟strcpy()实现相同的功能--字符串拷贝,由于第3个参数的存在,需要多方面考虑。首先调用者需要确保目的空间大于等于源空间,count小于等于目的空间大小。其次指定的拷贝字符的个数count, 如果count小于源空间大小,则完成strcpy()一样的工作,如果count大于等于源空间大小,拷贝完字符串后,还需在目的空间补上(count-字符串)个'\0'。 这里代码精简如上,后者省去了字符串个数 n的定义,由count自减完成,效率明显提高。
其他的库函数大家一起来研究下...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
小弟自己写了个g-bios string库。
俺的一点心得:
(1)标准string库是不会检查传入的指针的,所以使用string库时程序员要注意检查
指针的正确性;
(2)strncpy,当目标字串的容量大于n+目标字串长度时,会在余下的位置上补‘\0’ ;
(3)memcmp,在比较时,返回的值是int而不是long;
(4)strchr、strrchr 传递要查找的字符时,用的数据类型是int而不是char
(5)memmove 、memcpy都可以实现内存块的拷贝,不同点在于当(源地址+n)>目标地址时memmove会拷贝仍然可以正确完成拷贝,而memcpy,则会发生内存覆盖
扩展的string库还在努力中,o(∩_∩)o...哈哈
memcpy() 与 memmove() 这两个函数与 strcpy(),strncpy()有点类似,但区别很大。memcopy(), memmove()分两种情况:
(1) 目标地址在源地址前面的情况
dest src
|------------------|-------------|
|------------------|
count
(2) 目标地址在源地址后面的情况
src dest
|-------------|---------------------|
|-----------------|
count
memcpy() 在(2)的状态下会覆盖部分内存,因此采用了memmove()来弥补这一缺陷。
void *memcpy(void *pdst, const void *psrc, size_t count)
{
BYTE *iter = pdst;
while (count > 0)
{
*iter++ = *psrc++;
count--;
}
return pdst;
}
这里的count 我理解为字节数,数据拷贝以字节为单位。
void *memmove(void *pdst, const void *psrc, size_t count)
{
BYTE *pd;
BYTE *ps;
if (pdst < psrc)
{
pd = pdst;
ps = psrc;
while (count > 0)
{
*pd++ = *ps++;
count--;
}
}
else
{
pd = pdst + count;
ps = psrc + count;
while (count > 0)
{
*--pd = *--ps;
count--;
}
}
return pdst;
}
例如 a[50], dest=&a[0], src=&a[5], count=10的情况(1)
src=&a[0], dest=&a[5], count=10的情况(2)
用memcpy()在(2)时就把dest开始部分的一段数据给覆盖了,而memmove()则完整地完成了拷贝。
[ 本帖最后由 syukayo 于 2009-11-30 20:35 编辑 ]
我来说说strcmp和strncmp函数:
int strcmp(const char *s1, const char *s2)
如果s1于s2相等(包含'\0')时候返回0,否则返回*s1 - *s2.
int strncmp(const char *s1, const char *s2, int n)
如果s1,s2<=n,且s1,s2相等(包含'\0')时候返回0.
如果s1,s2前n个字符相等时候返回0.
否则返回*s1 - *s2.
memcpy() 不用顾及地址前后,怎么拷贝都不会覆盖原数据。
我认为memcpy拷贝会覆盖内存,并会把先拷贝的数据铺盖掉
src dest
|------------------|---------------------|
|------------------------|
count
这种情况memcpy内存同样会发生错误,编译可以通过。
1 这是我写的代码,为什么标准库中copy完了dest结尾不补零哪?
2
3 void *memcpy(void *dest, void *src, int count)
4 {
5 char *p;
6 char *d;
7
8 p = src;
9 d = dest;
10
11 while (count)
12 {
13 *d++ = *p++;
14 count--;
15 }
16
17 *d = '\0';
18
19 return dest;
20 }
~
对于memmove函数
void *memmove(void *pdst, const void *psrc, size_t count)
{
BYTE *pd;
BYTE *ps;
if (pdst < psrc)
{
pd = pdst;
ps = psrc;
while (count > 0)
{
*pd++ = *ps++; pdst psrc
|------------------|-------------|
|------------------------|
count 这是(pdst < psrc)情况pdst会覆盖到psrc 但没关系memmove只是数据移动
count--; 移动之后源数据破坏了也没关系
} |-----------------------|
}
else
{
pd = pdst + count;
ps = psrc + count; psrc pdst
|-------------|---------------------|
|-----------------|
count
|-------------------|
while (count > 0) 这种情况(pdst >= psrc)这样复制会覆盖掉一部分数据的。
src dest
|-------------|---------------------|
|------------------|
count ^
{ |-----------------| < ---|
*--pd = *--ps; ^
count--; < -----|
#copy like this! no problem!
}
}
return pdst;
}
关于g-bios标准库中的string库函数strrchr函数的实现,我有点疑问。
strrchr函数原型:char *strrchr(const char *psrc, size_t c);
其功能,我的理解是返回源字符串最后一个首字符与变量c匹配的字串。
我的思路是定义一个指针变量记录首字符匹配的位置,形参指针变量遍历整个源字符串,当源字符串遍历完了,定义的指针变量刚好记录了最后一个首字符匹配的地址,于是自己写了一个strrchr函数如下:
char *strrchr(const char *psrc, size_t c)
{
const char *p = NULL;
while (*psrc)
{
if(*psrc == c)
{
p = psrc;
}
psrc++;
}
if(p == NULL)
{
return NULL;
}
else
{
return (char *)p;
}
}
但是看了标准库strrchr函数的实现,发现strrchr函数是先遍历完源字符串,在倒着遍历找出最后那个匹配字串,我有个疑问就是如果源字符串第一个字符就是最后那个首字符与字符c,那不是来回遍历了两次,这样的话,效率上是不是有些问题。自己写的函数,遍历源字符串一次就可以了,但为什么标准库会采取这种方式实现呢,我想标准库这样实现一定是有它的道理的,经讨论,我觉得我的函数实现中当源字符串中与c匹配的字符很多的时候,我那个函数记录首字符位置的指针变量被反复赋值,导致效率上也会出现问题,也许是我的理解有误,希望高手能指点迷津!
strrchr()的功能是reverse反序搜索第一个与c匹配的字符。
strchr 与 strrchr 的问题:
个人认为,strchr是返回所找字符在该字符串中第一次出现时的地址, strrchr是返回所找字符在该字符串中最后出现时的地址。
我的strrchr的刚开始的思路跟forkpower一样,直接找过去,找到最后一个就返回那个地址就是了。不知道哪个更有效点。
另外,forkpower的代码中
const char *p = NULL; 这里既然定义了p
if (p == NULL)
return NULL;
else
return (char *)p;
就没必要那么烦了,直接返回p不就好了。
[ 本帖最后由 syukayo 于 2009-11-30 22:28 编辑 ]
cross 的memcpy
3 void *memcpy(void *dest, void *src, int count)
4 {
5 char *p;
6 char *d;
7
8 p = src;
9 d = dest;
10
11 while (count)
12 {
13 *d++ = *p++;
14 count--;
15 }
16
17 *d = '\0'; 这里补零干吗? 个人认为内存间数据拷贝,以字节为单位,可以是任意类型的数据,不只是char型;
18
19 return dest;
20 }
神勇的syukayo ,火眼金睛一下子就看出了forkpower和cross这2个小妖的问题,崇拜中。。。。。。
偶来说说,我在写string的一点感受。说明如下
例1:
char *my_strrchr(const char *psrc, int sc)
{
const char *temp = psrc;
while (*temp)
temp++;
while (temp > psrc)
{
temp--; //如果temp--,放在这里,那么排除了sc为'\0'的情况,系统的strrch命令支持 strrchr = '\0' 的情况。
//如果要实现这个目的,可以将 temp--;放在下面
if (*temp == sc)
return (char *)temp;
// temp--;
}
}
[ 本帖最后由 HELLO_MAX 于 2009-11-30 23:31 编辑 ]