mem系列函数(memset memcpy memmove) 和str系列函数(strlen strcpy strcmp strcat strstr strtok)

 void *memset(void *s, int ch, size_t n);
 函数解释:将s中前n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。
 memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法
 最常见的错误:memset是按字节填充的,对其他多余一个字节的类型填充要注意


************************************************************
 
  void *memcpy(void *dest, const void *src, size_t n);
  1.source和destin所指的内存区域可能重叠,但是如果source和destin所指的内存区域重叠,
  那么这个函数并不能够确保source所在重叠区域在拷贝之前不被覆盖。而使用memmove可以用来处理重叠区域。函数返回指向destin的指针.
  2.如果目标数组destin本身已有数据,执行memcpy()后,将覆盖原有数据(最多覆盖n)。如果要追加数据,则每次执行memcpy后,
  要将目标数组地址增加到你要追加数据的地址。
  注意:source和destin都不一定是数组,任意的可读写的空间均可。
void* memcpy(void*dest,constvoid*src,size_tcount)
{
    assert(dest!=NULL && src!=NULL);
    char* tmp=dest;
    const char* s=src;
    for(size_t i=0;i<count;i++)
{
    tmp[i]=s[i];
}
    return dest;
}


************************************************************
 
  原型:void *memmove( void* dest, const void* src, size_t count );
  头文件:<string.h>
  功能:由src所指内存区域复制count个字节到dest所指内存区域。
  相关函数:memset、memcpy
  memmove用于从src拷贝count个字符到dest,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前

  将重叠区域的字节拷贝到目标区域中。但复制后src内容会被更改。但是当目标区域与源区域没有重叠则和memcpy函数功能相同。

*******************************************************

extern unsigned int strlen(char *s);
 strlen(char*)函数求的是字符串的实际长度,它求得方法是从开始到遇到第一个'\0',如果你只定义没有给它赋初值,
 这个结果是不定的,它会从aa首地址一直找下去,直到遇到'\0'停止。

而sizeof()返回的是变量声明后所占的内存数,不是实际长度,此外sizeof不是函数,仅仅是一个取字节运算符,strlen是函数。

********************************************************

原型声明:char *strcpy(char* dest, const char *src);
头文件:#include <string.h> 和 #include <stdio.h>
功能:把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间
说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
返回指向dest的指针。

为什么返回 char* 类型的dest指针?

(A)return new string("Invalid argument(s)");,说明答题者根本不知道返回值的用途,并且他对内存泄漏也没有警惕心。
从函数中返回函数体内分配的内存是十分危险的做法,他把释放内存的义务抛给不知情的调用者,绝大多数情况下,调用者不会释放内存,
这导致内存泄漏。
(B)return 0;,说明答题者没有掌握异常机制。调用者有可能忘记检查返回值,调用者还可能无法检查返回值(见后面的链式表达式)。
妄想让返回值肩负返回正确值和异常值的双重功能,其结果往往是两种功能都失效。应该以抛出异常来代替返回值,这样可以减轻调用者的负担、
使错误不会被忽略、增强程序的可维护性。
[3]
(A)忘记保存原始的strDest值,说明答题者逻辑思维不严密。
[4]
(A)循环写成while (*strDestCopy++=*strSrc++);,同[1](B)。
(B)循环写成while (*strSrc!='\0') *strDest++=*strSrc++;,说明答题者对边界条件的检查不力。循环体结束后,strDest字符串的末尾没有正确地加上'\0'。
⒉返回strDest的原始值使函数能够支持链式表达式,增加了函数的“附加值”。同样功能的函数,如果能合理地提高的可用性,自然就更加理想。

******************************************************************

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

规则
当s1<s2时,返回为负数
当s1=s2时,返回值= 0
当s1>s2时,返回正数
即:两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇'\0'为止。如:
"A"<"B" "a">"A" "computer">"compare"
特别注意:strcmp(const char *s1,const char * s2)这里面只能比较字符串,即可用于比较两个字符串常量,或比较数组和字符串常量,不能比较数字等其他形式的参数。

int strcmp(const char *str1,const char *str2)
{
    /*不可用while(*str1++==*str2++)来比较,当不相等时仍会执行一次++,
    return返回的比较值实际上是下一个字符。应将++放到循环体中进行。*/
    while(*str1 == *str2)
    {
        if(*str1 == '\0')
            return0;
         
        str1++;
        str2++;
    }
    return *str1 - *str2;
}

***************************************************************************


extern char *strcat(char *dest,char *src);
用法

#include <string.h>
在C++中,则存在于<cstring>头文件中。
功能

把src所指字符串添加到dest结尾处(覆盖dest结尾处的'\0')并添加'\0'。
说明

src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
返回指向dest的指针。

 char *mystrcat(char *dst,const char *src) //用自己的方式实现strcat函数功能
   {
       char *p=dst;  //下面的操作会改变目的指针指向,先定义一个指针记录dst
       while(*dst!='\0')dst++;
       while(*src!='\0')*dst++=*src++;
       *dst='\0';
       return p;  //dst现在指向拼接后的最后一位字符,在这里返回dst,会出现错误
    }

*******************************************************************************

函数原型:
extern char *strstr(char *str1, const char *str2);
语法:
* strstr(str1,str2)
str1: 被查找目标 string expression to search.
str2: 要查找对象 The string expression to find.
返回值:若str2是str1的子串,则返回str2在str1的首次出现的地址;如果str2不是str1的子串,则返回NULL。

char *strstr(const char*s1,const char*s2)
{
    const char*p=s1;
    const size_tlen=strlen(s2);
    for(;(p=strchr(p,*s2))!=0;p++)
    {
        if(strncmp(p,s2,len)==0)
            return (char*)p;
    }
    return(0);
}
*****************************************************************************

char *strtok(char s[], const char *delim);

分解字符串为一组字符串。s为要分解的字符串,delim为分隔符字符串。
例如:strtok("abc,def,ghi",","),最后可以分割成为abc def ghi.尤其在点分十进制的IP中提取应用较多。

strtok()用来将字符串分割成一个个片段。参数s指向欲分割的字符串,参数delim则为分割字符串中包含的所有字符。当strtok()在参数s的字符串中发现参数delim中包含的分割字符时,则会将该字符改为\0 字符。在第一次调用时,strtok()必需给予参数s字符串,往后的调用则将参数s设置成NULL。每次调用成功则返回指向被分割出片段的指针。

#include<iostream>
#include<cstring>
using namespace std;
int main()
{
    char sentence[]="This is a sentence with 7 tokens";
    cout << "The string to be tokenized is:\n" << sentence << "\n\nThe tokens are:\n\n";
    char *tokenPtr=strtok(sentence,"");
    while(tokenPtr!=NULL) {
        cout<<tokenPtr<<'\n';
        tokenPtr=strtok(NULL,"");
    }
    //cout << "After strtok,sentence=" << tokenPtr<<endl;
    return 0;
}



<think>嗯,用户现在问的是关于string.h库的内容。之前他们问了如何控制C++中的cin读取,现在转向C语言的string.h,可能是在学习过程中从C++转向C,或者需要理解底层字符串处理。我需要先确认用户的需求,他们可能是在学习C语言,或者需要了解标准库函数。 首先,string.h是C语言的标准库,提供字符串处理内存操作函数。用户可能想知道里面有哪些常用函数,怎么使用,或者常见问题。要注意用户可能对CC++的区别不太清楚,尤其是string.hC++的<string>的区别,可能需要简单说明。 之前的对话中,用户的问题涉及输入输出控制,现在转向字符串处理,可能是项目或学习中遇到了相关问题。比如,处理用户输入后的字符串操作,或者内存管理问题。需要覆盖主要函数,如strcpystrcatstrlen,以及安全问题替代方案。 用户可能没有明确说出,但潜在的需求可能是如何安全有效地使用这些函数,避免缓冲区溢出等常见错误。所以,除了介绍函数,还需要提到安全问题,比如推荐使用更安全的函数strncpy,或者提到C11的 Annex K函数(如strcpy_s),尽管这些可能不可移植。 另外,用户可能需要示例代码来理解函数用法,所以提供简单例子会有帮助。同时,指出常见的陷阱,比如strcpy不检查目标缓冲区大小,strcmp的返回值意义,以及字符串结尾的'\0'的重要性。 还需要比较CC++的不同,比如C++的std::string更安全,但用户可能在用C开发,或者维护旧代码。所以强调在C中的使用场景注意事项。 总结回答结构:介绍string.h的作用,分类讲解常用函数,内存操作函数,安全问题,常见错误,替代方案,示例代码,以及总结注意事项。这样结构清晰,帮助用户逐步理解。</think>以下是关于C语言标准库`string.h`的详细介绍: --- ### 一、基本作用 `string.h`是C语言标准库中用于**字符串处理****内存操作**的头文件,包含: - 字符串操作函数(以`str`开头) - 内存块操作函数(以`mem`开头) - 字符查找/分类函数(如`strspn`) --- ### 二、常用字符串函数 | 函数原型 | 功能描述 | 经典问题 | |---------|---------|---------| | `char* strcpy(char* dest, const char* src)` | 字符串复制 | 不检查目标缓冲区大小,可能导致溢出 | | `char* strcat(char* dest, const char* src)` | 字符串拼接 | 同样存在缓冲区溢出风险 | | `size_t strlen(const char* str)` | 获取字符串长度 | 不计算结尾的`\0`,时间复杂度$O(n)$ | | `int strcmp(const char* s1, const char* s2)` | 字符串比较 | 返回值:正数(s1>s2)/0(相等)/负数(s1<s2) | | `char* strstr(const char* haystack, const char* needle)` | 查找子串 | 返回首次出现位置的指针,未找到返回NULL | **示例代码**: ```c char buf[20]; strcpy(buf, "Hello"); // buf内容:H e l l o \0 strcat(buf, "World"); // buf内容:H e l l o W o r l d \0 (已溢出!) ``` --- ### 三、内存操作函数 | 函数原型 | 功能 | 特点 | |---------|------|------| | `void* memcpy(void* dest, const void* src, size_t n)` | 内存拷贝 | 按字节复制,不处理重叠区域 | | `void* memmove(void* dest, const void* src, size_t n)` | 内存移动 | 安全处理重叠区域 | | `int memcmp(const void* s1, const void* s2, size_t n)` | 内存比较 | 逐字节比较二进制内容 | | `void* memset(void* s, int c, size_t n)` | 内存填充 | 常用于初始化内存块 | **安全用法示例**: ```c char src[50] = "Secure coding"; char dest[50]; memcpy(dest, src, strlen(src)+1); // +1复制终止符 ``` --- ### 四、安全风险与改进 1. **经典漏洞**: - `strcpy`/`strcat`缓冲区溢出(如1988年Morris蠕虫病毒) - 未初始化指针导致段错误 2. **安全替代方案**: ```c // 使用长度限制版本 strncpy(dest, src, dest_size-1); dest[dest_size-1] = '\0'; // 手动添加终止符 // C11可选安全函数(需定义__STDC_LIB_EXT1__) errno_t err = strcpy_s(dest, dest_size, src); ``` --- ### 五、特殊功能函数 1. **字符串分割**: ```c char *strtok(char* str, const char* delim); // 非线程安全 ``` 2. **格式化解析**: ```c int sscanf(const char* str, const char* format, ...); // 从字符串读取数据 ``` 3. **错误码转换**: ```c char* strerror(int errnum); // 将错误号转换为可读信息 ``` --- ### 六、常见错误案例 1. **忘记终止符**: ```c char buf[5]; strcpy(buf, "12345"); // 写入6字节(包含\0),缓冲区溢出 ``` 2. **误用sizeof**: ```c char str[] = "test"; printf("%zu", sizeof(str)); // 输出5(正确) char* p = str; printf("%zu", sizeof(p)); // 输出指针大小(错误用法) ``` --- ### 七、最佳实践 1. 优先使用`strncpy`代替`strcpy` 2. 操作前检查目标缓冲区大小 3. 使用`snprintf`进行安全格式化: ```c char buf
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值