龙空技术网

Linux C 编程 - C 标准库函数:字符串操作函数

大话幽默一刻 347

前言:

此刻咱们对“c语言返回字符串的函数”大致比较看重,姐妹们都需要剖析一些“c语言返回字符串的函数”的相关内容。那么小编同时在网摘上汇集了一些对于“c语言返回字符串的函数””的相关文章,希望咱们能喜欢,大家一起来了解一下吧!

字符串操作函数

程序按功能划分可分为数值运算,符号处理和I/O操作三类,符号处理程序占相当大的比例,符号处理程序无处不在,编译器,浏览器等程序的主要功能都是符号处理。无论多复杂的符号处理都是由各种基本的字符串操作组成的。

1.初始化字符串

#include<string.h> void *memset(void *s, int c, size_t n);返回值:s指向哪,返回的指针就指向哪

memset 函数把s所指向的内存地址开始的n个字节都填充为c的值。通常c的值为0,把一块内存区清零。例如定义char buf[10]; 如果它是全局变量或静态变量,则自动初始化为0(位于.bss段),如果它是函数的局部变量,则初值不确定,可以用memset(buf, 0, 10)清零,由malloc分配的内存初值也是不确定的,也可以用memset清零。

2.取字符串的长度

#include<string.h> size_t strlen(const char *s);返回值:字符串的长度

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

3.拷贝字符串

strcpy和strncpy函数,拷贝以'\0'结尾的字符串,strncpy还带一个参数指定最多拷贝多少个字节,此外,strncpy并不保证缓冲区以'\0'结尾。

#include <string.h> void *memcpy(void *dest, const void *src, size_t n);void *memmove(void *dest, const void *src, size_t n);返回值:dest指向哪,返回的指针就指向哪

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

memmove 也是从src所指的内存地址拷贝n个字节到dest所指向的内存地址,虽然叫move但其实也是拷贝而非移动。但是和memcpy有一点不同,memcpy的两个参数src和dest所指向的内存空间如果重叠则无法保证正确拷贝,而memmove却可以正确拷贝。假设定义了一个数组char buf[20] = "hello world\n"; 如果想把其中的字符串往后移动一个字节(变成"hhello world\n"), 调用memcpy(buf + 1, buf, 13)是无法保证正确拷贝的:

以下memcpy用法是错误的:

#include <stdio.h>#include <string.h>int main(){    char src[20] = "hello world\n";    memcpy(src+1, src, 13);    printf("%s\n", src);    return 0;}

将memcpy改成memmove

#include <stdio.h>#include <string.h>int main(){    char src[20] = "hello world\n";    memmove(src+1, src, 13);    printf("%s\n", src);    return 0;}       

4.连接字符串

#include<string.h>char *strcat(char *dest, const char *src);char *strcat(char *dest, const char *src, size_t n);

strcat 把 src 所指的字符串连接到dest所指的字符串后面,例如:

char d[10] = "foo";char s[10] = "bar";strcat(d, s);printf("%s %s\n", d, s);

调用strcat 函数后,缓冲区s的内容没变,缓冲区d中保存着字符串"foobar", 注意原来"foo"后面的'\0'被连接上来的字符串"bar" 覆盖掉了,"bar" 后面的"\0" 仍保留。

strcat和strcpy有同样的问题,调用者必须确保dest缓冲区足够大,否则会导致缓冲区溢出错误。strncat 函数通过参数n指定一个长度,就可以避免缓冲区溢出错误。注意这个参数n的含义和strncpy的参数n不同,它并不是缓冲区dest的长度,而是表示最多从src缓冲区中取n个字符(不包括结尾的'\0')连接到dest后面。如果src中前n个字符没有出现'\0', 则取前n个字符再加一个'\0'连接到dest后面,所以strncat总是保证dest缓冲区以'\0'结尾,这一点又和strncpy不同,strncpy并不保证dest缓冲区以'\0'结尾。所以,提供给strncat函数的dest缓冲区的大小至少应该是strlen(dest)+n+1个字节,才能保证不溢出。

5.比较字符串

#include <string.h>int memcmp(const void *s1, const void *s2, size_t n);int strcmp(const char *s1, const char *s2);int strncmp(const char *s1, const char *s2, size_t n);

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

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

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

6.搜索字符串

#include <string.h>char *strchr(const char *s, int c);char *strrchr(const char *s, int c);返回值:如果找到字符c, 返回字符串s 中指向字符c的指针,如果找不到就返回NULL。

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

#include <string.h>char *strstr(const char *haystack, const char *needle);返回值:如果找到子串,返回值指向子串的开头,如果找不到就返回NULL。

strstr在一个长字符串中从前到后找一个子串,找到子串第一次出现的位置就返回,返回值指向子串的开头,如果找不到就返回NULL。

7.分割字符串

很多文件格式或协议格式中会规定一些分割符或者界定符,例如/etc/passwd文件中保存着系统的账号信息:

$ cat /etc/passwdroot:x:0:0:root:/root:/bin/bashdaemon:x:1:1:daemon:/usr/sbin:/bin/shbin:x:2:2:bin:/bin:/bin/sh...

每条记录占一行,也就是说记录之间的分隔符是换行符,每条记录又由若干个字段组成,这些字段包括用户名,密码,用户id,组id, 个人信息,主目录,登录Shell, 字段之间的分割符是:号。解析这样的字符串需要根据分隔符把字符串分割成几段,C 标准库提供的strtok函数可以很方便地完成分割字符串的操作。

#include <string.h>char *strtok(char *str, const char *delim);char *strtok_r(char *str, const char *delim, char **saveptr);返回值: 返回指向下一个Token的指针,如果没有下一个Token 了就返回NULL。

参数str是带分割的字符串,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;}
$ ./a.out rootx0root/root/bin/bash

strtok 的行为可以这样理解: 冒号是分割符,把"root:x::0:root:/root:/bin/bash:" 这个字符串分割成"root", "x", "", "0", "root", "/root", "/bin/bash", "", 等几个Token, 但空字符串的Token被忽略。第一次调用要把字符串首地址传给strtok的第一个参数,以后每次调用第一个参数只要传NULL就可以了,strtok 函数自己会记住上次处理到字符串的什么位置(通过strtok函数中的一个静态变量记住的)。

Man Page 指出了用strtok 和 strtok_r 函数需要注意的问题:

这两个函数要改写字符串以达到分割的效果

这两个函数不能用于常量字符串,因为试图改写.rodata段会产生段错误

在做了分割之后,字符串中的分隔符就被'\0' 覆盖了

strtok函数使用了静态变量,它不是线程安全的,必要时应该用可重入的strtok_r 函数

标签: #c语言返回字符串的函数