C语言字符串操作

该文章主要总结C语言的库函数对字符串的初始化,取长度,拷贝,链接,比较,搜索等基本操作。

1、初始化字符串。

#include <string.h>

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

该函数的返回值:指针s指向哪里,返回值的指针就指向哪里。

memset把s所指的内存的内存地址开始的n个字节都填充为c的值,通常c为0,表示将一段内存清空。

举个例子:char buf[10],如果他是全局变量或静态变量,则自动初始化为0,如果它是函数的局部变量,则初始化的值不确定,可以用memset(buf,0,10)来清零。由malloc分配来的内存初值也是不确定的也可以用memset来清零。


这里有个问题:说全局变量或静态变量初始化后,位于.bss段。何解?


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、拷贝字符串

这里介绍四个函数:

#include <string.h>

char *strcpy(char *dst, const char *src);
char *strncpy(char *restrict dst, const char *restrict src, size_t n);

//从src所指的内存地址拷贝n个字节到dest所指的内存地址
void *memcpy(void *dest, const void *src, size_t n);
void *memmove(void *dest, const void *src, size_t n);

先看两个更基本的字符串拷贝函数,strcpy和strncpy。这两个函数也是位于string.h头文件中。都是实现了把src所指向的字符串拷贝到dst所指向的内存空间中。这里要注意src的参数类型是const的,也就是说src所指向的内存空间是可读不可写的。而dest所指向的内存空间是可读可写的。

再来说下strcpy和strncpy的区别,区别就在n上。

strcpy拷贝字符串时,会一直拷贝,直到遇到字符‘\0’,所以dest所指向的内存空间要足够大,否则可能导致写越界。另外src和dest所指向的内存空间不能有重叠。

而strncpy拷贝字符串时,则会根据n的值,拷贝n个字节到dest中,即,如果拷贝到'\0'就结束,如果拷贝到n各字节,仍然没有碰到'\0',拷贝也会结束,但是为了确保不会出现写越界的情况,需要手动指定第n个字节的值为'\0'。strncpy还有一个特性,如果src所指向的字节完全拷贝了,仍然不足n个字节,那么还差多少个字节就补多少个'\0'。

memcpy函数也是从src所指向的内存地址拷贝n个字节到dest所指向的内存空间中,但是和strncpy不同的是,memcpy并不会遇到'\0'就结束,而是一定要拷贝n个字节。

所以,会有一个很明显的区别,str开头的拷贝字符串函数处理以'\0'结尾的函数,而mem开头的函数则不关心‘\0’字符,或者说mem开头的函数,并不把参数当成字符串看待,从参数类型就可以看出,都是void *,而非str函数中的char *。
memmove也是从src所指内存地址拷贝n个字节到dest所指的内存地址,和memcpy不同的是,src和dest所指的内存地址如果重叠,则无法保证正确拷贝,而memmove却可以正常拷贝。


这里要解释一个关键字:restrict。

restrict是再C99中被引入的,用以告诉编译器可以放心对此函数进行优化,程序猿自己保证这些指针所指的内存空间互不重叠。

(上面的strcpy和strncpy中,strncpy是来自mac本上的man strncpy指令的结果,mac本上已经引入了C99标准。strcpy是拷贝于网页资料,所以参数并没有加入restrict关键字。)

来看一个例子:

void vector_add(const double *x, const double *y, double *result)
{
    int i;
    for (i = 0; i < 64; ++i)
        result[i] = x[i] + y[i];
}

如果这个函数再多处理器的计算机上运行,编译器可以这样优化,把这一个循环拆成两个循环,一个处理器计算i值从0到31,另一个处理器计算i值从32到63。这样两个处理器同时工作,使运算时间缩短一半。但是如果result所指的内存空间和x所指的内存空间重叠的呢?比如result[0]就是x[1],result[1]就是x[2],那么这两个处理器就不能各干各的事了,第二个处理器的工作依赖于第一个处理器最终计算结果,这样处理器并行性就无法利用。为此,C99引入了restrict关键字来解决上述问题。


从连接字符串开始,所使用的函数原型都是引自mac本上man命令,也就是说C99标准,有些参数会有restrict关键字


4、连接字符串

#include <string.h>

char *strcat(char *restrict s1, const char *restrict s2);

char *strncat(char *restrict s1, const char *restrict s2, size_t n);

strcat(strncat)把s2指向的字符串连接到s1指向的字符串后面。在连接过程中,s1指向的字符串后面的'\0'会被覆盖掉。

另外,使用strcat过程中,调用者也必须要考虑s1缓冲区足够大的问题。而strncat通过参数n来指定连接长度,避免溢出问题。

这里要注意strncat和strncpy的区别。strncat是从s2中取n个字符,连接到s1后,如果前n个字符不包含'\0'便会在最后添加一个'\0',所以实际上s1应该提供strlen(s1)+n+1的长度。而strncpy并不理会拷贝完成后,最后的字符是不是'\0',这个需要程序猿自己添加。


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小返回负值,s1比s2大,返回正值

strcmp比较字符串,如果碰到'\0'则结束比较。

strncmp比较字符串,要么遇到'\0'结束,否则会比较全部n个字符。


6、搜索字符串

#include <string.h>

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

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

strchr在字符串s中从前到后寻找字符c,如果找到字符c第一次出现的位置,就返回,返回值指向这个位置。如果找不到字符c就返回NULL。而strrchr感觉和strchr用法类似。

#include <string.h>

char *strstr(const char *s1, const char *s2);
在长字符串s1中寻找子字符串s2,找到则返回第一次出现的位置,否则返回NULL。


7、分割字符串

#include <string.h>

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


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;
}

看一个例子,strtok第一次调用的时候,需要把待分割的字符串首地址传给第一个参数,之后再调用只需要穿NULL,strtok会自动记住上次处理到字符串什么位置。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值