C 字符串函数

C 字符串函数

字符串初始化函数

memset

void *memset(void *s, int c, size_t n);

s中前n个字节用c替换并返回s, 是对较大的结构体或数组进行清零操作的最快方法

实现
void *my_memset(void *s, int c, size_t n)
{
    assert(NULL != s);
    void *temp = s;
    while(n--)
    {
        *((char *)temp) = (char)c;
        temp = (char *)temp + 1;
    }
    return s;
}
Example
int buf[6] = {0};
memset(buf, 1, sizeof(buf)); // Tip1: 参数三使用 sizeof 运算符    Right
memset(buf, 1, 6); // Tip2: buf 为 [16843009 257 0 0 0 0]    Wrong
Tip1

尽量使用 sizeof运算符,能够确保初始化的字节数目,因为不同系统中的数据类型所占字节数目不同。

Tip2

memset函数是按照字节对初始化空间进行初始化的,即参数二是按照单字节往参数一所指区域赋值的,即对于单字节类型(char)可以初始化为任意支持的值,而多多字节类型(int、long等)只能初始化为0,因为对多字节类型的所有字节赋值0的结果都是0,而如果初始化为其他值,就不一定正确,比如示例中的对int数据类型赋值为1,第二个参数会先转换为char类型进行赋值,前8位的二进制为000000001,而buf数组元素类型为int类型,占四个字节,故第一个元素为00000001 00000001 00000001 0000000116843009,第二个元素的前两个字节为00000001 00000001,即257

Tip3

memset相信传入的字节数目限制,并且memset需要工作在一块内存中,而不是一个以0结束的字符串,所以不需要进行字符串结束的检查

字符串长度函数

strlen

size_t strlen(const char *s);

strlen函数返回s所指的字符串的长度。该函数从s所指的第一个字符开始找'\0'字符,一旦找到就返回,返回的长度不包括'\0'字符在内。例如定义char buf[] = "hello";,则strlen(buf)的值是5,但要注意,如果定义char buf[5] = "hello";,则调用strlen(buf)是危险的,会造成数组访问越界。

实现
size_t my_strlen(const char *s)
{   
    assert(NULL != s);
    const char *temp;
    for(temp = s; *temp; ++temp);
    return temp - s;
}
问题
  • sizeof运算符和strlen函数的区别?

    • sizeof是运算符,strlen是库函数。
    • sizeof可以用类型、变量做参数,而strlen只能用 char* 变量做参数,且必须以\0结尾。
    • sizeof是在编译的时候计算类型或变量所占内存的大小,而strlen的结果要在运行的时候才能计算出来,用来计算字符串的长度。
    • 数组做sizeof的参数不退化,传递给strlen就退化为指针了。

参考

字符串拷贝函数

strcpy

char *strcpy(char *dest, const char *src);

把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间, srcdest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。

实现
char *strcpy(char *dest, const char *src)
{
    assert(NULL != dest && NULL != src);
    char *s = dest;
    while((*s++ = *src++) != 0)
        ;
    return (dest);
}

strncpy

char *strncpy(char *dest, const char *src, size_t n);

strncpy函数与strcpy函数类似,除了指定最多拷贝src中的n个字节,如果前n个字节中没有结束字符\0, 则拷贝后的dest字符串没有结束字符。如果src字符串长度小于n,则strncpy会添加结束符\0dest剩余位置。

实现
char *my_strncpy(char *dest, const char *src, size_t n)
{
    assert(NULL != dest && NULL != src);
    char *s = dest;
    while(n > 0 && (*s++ = *src++) != '\0')
        n--;
    while(n-- > 0)
        *s++ = '\0';
    return dest;
}

memcpy

void *memcpy(void *dest, const void *src, size_t n);

memcpy函数从src所指的内存地址拷贝n个字节到dest所指的内存地址,和strncpy不同,memcpy并不是遇到'\0'就结束,而是一定会拷贝完n个字节。这里的命名规律是,以str开头的函数处理以'\0'结尾的字符串,而以mem开头的函数则不关心'\0'字符,或者说这些函数并不把参数当字符串看待,因此参数的指针类型是void *而非char *

实现
void *memcpy(void *dest, const void *src, size_t n)
{
    assert(NULL != dest && NULL != src);
    char *d = dest;
    const char *s = src;
    while(n--)
      *d++ = *s++;
    return dest;
}

memmove

void *memmove(void *dest, const void *src, size_t n);

memmove也是从src所指的内存地址拷贝n个字节到dest所指的内存地址,虽然叫move但其实也是拷贝而非移动。但是和memcpy有一点不同,memcpy的两个参数srcdest所指的内存区间如果重叠则无法保证正确拷贝,而memmove却可以正确拷贝, 是因为首先将src拷贝到一个临时数组中,然后再从临时数组中拷贝到dest

实现
void *memmove(void *dest, const char *src, size_t n)
{
    char *d = dest;
    const char *s = src;
    if(d < s)
    {
        while(n--)
          *d++ = *s++;
    }
    else
    {
        d = d + n -1;
        s = s + n -1;
        while(n--)
            *d-- = *s--;
    }
    return dest;
}

strcpy 与 memcpy区别

参考

字符串连接函数

strcat

char *strcat(char *dest, const char *src);

把src所指字符串添加到dest结尾处(覆盖dest结尾处的’\0’), descsrc所指内存不可以重叠切dest必须有足够的空间容纳src字符串

实现
char *strcat(char *dest, const char *src)
{
    assert(NULL != dest && NULL != src);
    char *d = dest;
    while(*d != '\0')
        ++d;
    while((*d++ = *src++) != '\0');
    return dest;
}

strncat

char *strncat(char *dest, const char *src, size_t n);

src所指字符串的前n个字符添加到dest结尾处,覆盖dest结尾处的'/0',实现字符串连接。

实现
char *strncat(char *dest, const char *src, size_t n)
{
    if(n != 0)
    {
        char *d = dest;
        const char *s = src;
        while(*d != 0)
            d++;
        do{
            if((*d = *s++) == 0)
                break;
            d++;
        }while(--n != 0);
        *d = 0;
    }
    return dest;
}

参考

字符串比较函数

memcmp

int memcmp(const void *s1, const void *s2, size_t n);

memcmp从前到后逐个比较缓冲区s1s2的前n个字节(不管里面有没有'\0'),如果s1s2的前n个字节全都一样就返回0,如果遇到不一样的字节,s1的字节比s2小就返回负值,s1的字节比s2大就返回正值。

实现
int memcmp(const void *s1, const void *s2, size_t n)
{
    assert(NULL != s1 && NULL != s2);
    unsigned char u1, u2;
    for(; n--; s1++, s2++)
    {
        u1 = *(unsigned char *)s1;
        u2 = *(unsigned char *)s2;
        if(u1 != u2)
            return u1 - u2;
    }
    return 0;
}

strcmp

int strcmp(const char *s1, const char *s2);

strcmps1s2当字符串比较,在其中一个字符串中遇到'\0'时结束,按照上面的比较准则,"ABC""abc"小,"ABCD""ABC"大,"123A9""123B2"小。

实现
int strcmp(const char *s1, const char *s2)
{
    assert(NULL != s1 && NULL != s2);
    for(; *s1 == *s2; s1++, s2++)
    if(*s1 == '\0')
        return 0;
    return ((*(unsigned char *)s1 < *(unsigned char *)s2) ? -1: 1);
}

strncmp

int strncmp(const char *s1, const char *s2, size_t n);

strncmp的比较结束条件是:要么在其中一个字符串中遇到'\0'结束(类似于strcmp),要么比较完n个字符结束(类似于memcmp)。例如,strncmp("ABCD", "ABC", 3)的返回值是0,strncmp("ABCD", "ABC", 4)的返回值是正值。

实现
int strncmp(const char *s1, const char *s2, size_t n)
{
    assert(NULL != s1 && NULL != s2);
    for(; n > 0; s1++, s2++, --n)
    {
        if(*s1 != *s2)
            return ((*(unsigned char *)s1 < *(unsigned char *)s2) ? -1: 1);
        else if(*s1 == '\0')
            return 0;
    }
    return 0;
}

memcmp 与 strcmp 区别

参考

字符串搜索函数

strchr

char *strchr(const char *s, int c);

strchr在字符串s中从前到后查找字符c,找到字符c第一次出现的位置时就返回,返回值指向这个位置,如果找不到字符c就返回NULL

实现
char *strchr(const char *s, int c)
{
    assert(NULL != s);
    const char ch = c;
    for(; *s != ch; s++)
        if(*s == '\0')
            return 0;
    return (char *)s;
}

strrchr

char *strrchr(const char *s, int c);

strrchrstrchr类似,从前到后查找字符c,找到字符c最后一次出现的位置时返回,返回值指向这个位置,如果找不到字符c就返回NULL也可以理解为从右向左找字符c,找到字符c第一次出现的位置就返回,函数名中间多了一个字母r可以理解为Right-to-left

实现
char *strrchr(const char *s, int c)
{
    assert(NULL != s);
    char *ret = 0;
    do{
        if(*s == (char)c)
            ret = s;
    }while(*s++);
    return ret;
}

strstr

char *strstr(const char *haystack, const char *needle);

strstr在一个长字符串中从前到后找一个子串(Substring),找到子串第一次出现的位置就返回,返回值指向子串的开头,如果找不到就返回NULL。这两个参数名很形象,在干草堆haystack中找一根针needle,按中文的说法叫大海捞针,显然haystack是长字符串,needle是要找的子串。

实现
char *strstr(const char *haystack, const char *needle)
{
    assert(NULL != haystack && NULL != needle);
    const char *p = haystack;
    const size_t len = strlen(needle);
    for(; (p = strchr(p, *needle)) != 0; p++)
    {
        if(strncmp(p, s2, len) == 0)
            return (char *)p;
    }
    return 0;
}

参考

字符串分割函数

strtok

char *strtok(char *str, const char *delim);

strtok通过分隔符delim将字符串str分割成多个tokens, 其中delim可以为多个分隔符,strtok遇到其中任何一个分隔符就会分割字符串。

示例
#include <stdio.h>
#include <string.h>

int main(void)
{
    char str[] = "root:x::0:root:/root:/bin/bash:";
    char *token;

    token = strtok(str, ":");
    printf("%s\n", token);
    while ( (token = strtok(NULL, ":")) != NULL)
        printf("%s\n", token);

    return 0;
}
// Output 
/* 
 * $ ./a.out 
 * root
 * x
 * 0
 * root
 * /root
 * /bin/bash
*/
实现
char *strtok(char *str, const char *delim)
{
    static char *p = 0;
    if(str)
        p = str;
    else if (!p)
        return 0;
    str = p + strspn(p, delim);
    p = str + strcspn(str, delim);
    if(p == str)
        return p = 0;
    p = *p ? *p = 0, p + 1 : 0;
    return str;
}

参考

字符串数值转换函数

atoi

int atoi(const char *nptr);

atoi把一个字符串开头可以识别成十进制整数的部分转换成int型,相当于下面要讲的strtol(nptr, (char **) NULL, 10);,除了 atoi不能检查出错。

实现
int atoi2(char *str)
{
    // Handle error if str is NULL
    if (*str == NULL)
        return 0;

    // Handle error if spaces in str head
    while(isspace(*str))
        str++;

    int res = 0; // Initialize result
    int sign = 1; // Initialize sign as positive
    int i = 0;

    // If number is negative , then update sign
    if(str[0] == '-')
    {
        sign = -1;
        i++;
    }

    // Interate through all digits and updates the result
    for(; str[i] != '\0'; ++i)
    {
        // Handle error if str contains non-numeric characters
        if (isNumberorChar(str[i]) == false)
            break;
        res = res * 10 + str[i] - '0';
    }

    // Return result with sign
    return sign * res;
}

参考

参考

猜你喜欢

转载自blog.csdn.net/u011221820/article/details/79893732