存档

文章标签 ‘c语言’

转载:C语言字符串操作函数

2010年2月26日 没有评论

1. 写一个函数实现字符串反转

版本1 – while版

void strRev(char *s)
{
char temp, *end = s + strlen(s) – 1;
while( end > s)
    {
        temp = *s;
*s = *end;
*end = temp;
–end;
++s;
    }
}

版本2 – for版

void strRev(char *s)
{
char temp;
for(char *end = s + strlen(s) – 1; end > s ; –end, ++s)
    {
        temp = *s;
*s = *end;
*end = temp;
    }
}

版本3 – 不使用第三方变量

void strRev(char *s)
{
for(char *end = s + strlen(s) – 1; end > s ; –end, ++s)
    {
*s ^= *end;
*end ^= *s;
*s ^= *end;
    }
}

版本4 – 重构版本3

void strRev(char *s)
{
for(char *end = s + strlen(s) – 1; end > s ; –end, ++s)
    {
*s ^= *end ^= *s ^= *end;
    }
}

版本5 – 重构版本4

void strRev(char *s)
{
for(char *end = s + strlen(s) – 1; end > s ; *s++ ^= *end ^= *s ^= *end–);
}

版本6 – 递归版

void strRev(const char *s)
{
if(s[0] == ‘\0′)
return;
else
        strRev(&s[1]);
    printf("%c",s[0]);
}

2. 实现库函数strcpy的功能

strcpy函数位于头文件<string.h>中

版本1

strcpy(char * dest, const char * src)
{
char *p=dest;
while(*dest++ = *src++)
        ;
    dest=p;
}

版本2

char * __cdecl strcpy(char * dst, const char * src)
{
char *p = dst;
while( *p ++ = *src ++ )
        ;
return dst;
}

版本3

strcpy(char * dest, const char * src)
{
int i=0;
for(; *(src+i)!=’\0′; i++)
*(dest+i) = *(src+i);
*(dest+i) = ‘\0′;
}

3. 实现库函数atoi的功能

atoi函数位于头文件<stdlib.h>中

版本1 – 附说明

int power(int base, int exp)
{
if( 0 == exp )
return 1;
return base*power(base, exp-1);
}
int __cdecl atoi(const char *s)
{
int exp=0, n=0;
const char *t = NULL;
for(; *s == ‘ ‘ || *s == ‘\t’ || *s == ‘\n’; s++) //找到第一个非空字符
        ;
if( *s >’9′ || *s <’0′ ) //如果第一个非空字符不是数字字符,返回0
return 0;
for(t=s; *t >=’0′ && *t <=’9′; ++t) //找到第一个非数字字符位置 – 方法1
        ;
    t–;
/* 找到第一个非数字字符位置 – 方法2
    t=s;
    while(*t++ >=’0′ && *t++ <=’9′)
        ;
    t -= 2;
*/
while(t>=s)
    {
        n+=(*t – 48)*power(10, exp); //数字字符转化为整数
        t–;
        exp++;
    }
return n;
}

版本2

int __cdecl atoi(const char *s)
{
int exp=0, n=0;
const char *t = NULL;
for(; *s == ‘ ‘ || *s == ‘\t’ || *s == ‘\n’; s++) //略过非空字符
        ;
if( *s >’9′ || *s <’0′ )
return 0;
for(t=s; *t >=’0′ && *t <=’9′; ++t)
        ;
    t–;
while(t>=s)
    {
        n+=(*t – 48)*pow(10, exp);
        t–;
        exp++;
    }
return n;
}

4. 实现库函数strlen的功能

strlen函数位于头文件<string.h>中

版本1 – while版

size_t  __cdecl strlen(const char * s)
{
int i = 0;
while( *s )
    {
        i++;
        s++;
    }
return i;
}

版本2 – for版

size_t  __cdecl strlen(const char * s)
{
for(int i = 0; *s; i++, s++)
        ;
return i;
}

版本3 – 无变量版

size_t  __cdecl strlen(const char * s)
{
if(*s == ‘\0′)
return 0;
else
return (strlen(++s) + 1);
}

版本4 – 重构版本3

size_t  __cdecl strlen(const char * s)
{
return *s ? (strlen(++s) + 1) : 0;
}

5. 实现库函数strcat的功能

strcat函数位于头文件<string.h>中

版本1 – while版

char * __cdecl strcat(char * dst, const char * src)
{
char *p = dst;
while( *p )
        p++;
while( *p ++ = *src ++ )
        ;
return dst;
}

6. 实现库函数strcmp的功能

strcmp函数位于头文件<string.h>中

版本1 – 错误的strcmp

int strcmp(const char * a, const char * b)
{
for(; *a !=’\0′ && *b !=’\0′; a++, b++)
if( *a > *b)
return 1;
else if ( *a==*b)
return 0;
else
return -1;
}

版本2

int __cdecl strcmp (const char * src, const char * dst)
{
int ret = 0 ;
while( ! (ret = *(unsigned char *)src – *(unsigned char *)dst) && *src)
++src, ++dst;
if ( ret < 0 )
                ret = -1 ;
else if ( ret > 0 )
                ret = 1 ;
return( ret );
}

7. 计算字符串中元音字符的个数

#include <stdio.h>
int is_vowel(char a)
{
switch(a)
    {
case ‘a’: case ‘A’:
case ‘e’: case ‘E’:
case ‘i’: case ‘I’:
case ‘o’: case ‘O’:
case ‘u’: case ‘U’:
return 1; break;
default: 
return 0; break;
    }
}
int count_vowel(const char *s)
{
int num;
if(s[0] == ‘\0′)
        num = 0;
else
    {
if(is_vowel(s[0]))
            num = 1 + count_vowel(&s[1]);
else
            num = count_vowel(&s[1]);
    }
return num;
}
int main()
{
char *s=" AobCd ddudIe";
    printf("%d \n", count_vowel(s));
return 0;
}

8. 判断一个字符串是否回文:包含一个单词,或不含空格、标点的短语。如:Madam I’m Adam是回文
版本1

/*
* 程序功能:判断一个单词,或不含空格、标点符号的短语是否为回文(palindrome)
*/
#include <stdio.h>
#include <ctype.h>
int is_palindrome(const char *s)
{
bool is_palindrome=0;
const char *end=s;
if(*end == ‘\0′) /* 如果s为空串,则是回文 */
        is_palindrome=1;
while(*end) ++end; /* end指向串s最后一个字符位置 */
–end;
while(s<=end)
{
        while(*s==’ ‘ || !isalpha(*s)) /* 略去串s中的非字母字符 */
            ++s;
while(*end==’ ‘ || !isalpha(*end))
            –end;
if(toupper(*s) == toupper(*end)) /* 将s中的字母字符转换为大字进行判断 */
        {
            ++s;
            –end;
        } 
        else 
        {
            is_palindrome=0; break;
        } /* 在s<=end的条件下,只要出现不相等就判断s不是回文 */
    }
    if(s>end)
        is_palindrome=1;
    else
        is_palindrome=0;
    return (is_palindrome);
}
int main()
{
const char *s ="Madam  I’ m   Adam";
    printf("%s %s \n", s, is_palindrome(s) ? "is a palindrome!": "is not a palindrome!");
return 0;
}

说明:__cdecl,__stdcall是声明的函数调用协议.主要是传参和弹栈方面的不同.一般c++用的是__cdecl,windows里大都用的是__stdcall(API)

分类: 生活 标签: ,

编程学习:由”strcpy”函数看到需要注意的问题(实现strcpy函数)

2010年2月26日 没有评论

每天一个知识点,进步的脚印…

 

   我们先来看一个题目:

已知strcpy函数的原型是:

        char * strcpy(char * strDest,const char * strSrc);

    1.不调用库函数,实现strcpy函数。
    2.解释为什么要返回char *。

    解答:
    1.strcpy的实现代码

        char * strcpy(char * strDest,const char * strSrc)
        {
                if ((strDest==NULL)||(strSrc==NULL)) //[1]
                        throw "Invalid argument(s)"; //[2]
                char * strDestCopy=strDest;  //[3]
                while ((*strDest++=*strSrc++)!=’\0′); //[4]
                return strDestCopy;
        }

 

答案分析:(错误的做法)
    [1]
    (A)不检查指针的有效性,说明答题者不注重代码的健壮性。
    (B)检查指针的有效性时使用((!strDest)||(!strSrc))或(!(strDest&&strSrc)),说明答题者对C语言中类型的隐式转换没有深刻认识。在本例中char *转换为bool即是类型隐式转换,这种功能虽然灵活,但更多的是导致出错概率增大和维护成本升高。所以C++专门增加了bool、true、false三个关键字以提供更安全的条件表达式。
    (C)检查指针的有效性时使用((strDest==0)||(strSrc==0)),说明答题者不知道使用常量的好处。直接使用字面常量(如本例中的0)会减少程序的可维护性。0虽然简单,但程序中可能出现很多处对指针的检查,万一出现笔误,编译器不能发现,生成的程序内含逻辑错误,很难排除。而使用NULL代替0,如果出现拼写错误,编译器就会检查出来。
    [2]
    (A)return new string("Invalid argument(s)";,说明答题者根本不知道返回值的用途,并且他对内存泄漏也没有警惕心。从函数中返回函数体内分配的内存是十分危险的做法,他把释放内存的义务抛给不知情的调用者,绝大多数情况下,调用者不会释放内存,这导致内存泄漏。
    (B)return 0;,说明答题者没有掌握异常机制。调用者有可能忘记检查返回值,调用者还可能无法检查返回值(见后面的链式表达式)。妄想让返回值肩负返回正确值和异常值的双重功能,其结果往往是两种功能都失效。应该以抛出异常来代替返回值,这样可以减轻调用者的负担、使错误不会被忽略、增强程序的可维护性。
    [3]
    (A)忘记保存原始的strDest值,说明答题者逻辑思维不严密。
    [4]
    (A)循环写成while (*strDest++=*strSrc++);,同[1](B)。
    (B)循环写成while (*strSrc!=’\0′) *strDest++=*strSrc++;,说明答题者对边界条件的检查不力。循环体结束后,strDest字符串的末尾没有正确地加上’\0′。

    2.返回strDest的原始值使函数能够支持链式表达式,增加了函数的“附加值”。同样功能的函数,如果能合理地提高的可用性,自然就更加理想。
    链式表达式的形式如:
        int iLength=strlen(strcpy(strA,strB));
    又如:
        char * strA=strcpy(new char[10],strB);
    返回strSrc的原始值是错误的。其一,源字符串肯定是已知的,返回它没有意义。其二,不能支持形如第二例的表达式。其三,为了保护源字符串,形参用const限定strSrc所指的内容,把const char *作为char *返回,类型不符,编译报错。

分类: 生活 标签: , , ,