字符(串)函数与内存函数。
字符串函数
Ladies and gentlemen,下面开始是我们要讲的宇宙究极无敌的函数:
strlen strcpy strcat strcmp strstr strtor strerro
首先我们来从这些函数,依一找共同点。

瞪大你们的眼睛,看每个函数都这str,这说明了他们都是字符的一家族。了解前面的字母,这样就只需要知道后面的就好记了。
strlen
1.len 看着看着我们是不是很眼熟,是不是我们用来表示长度的变量名称。聪慧的你肯定已经联想到了这是一个能求字符长度的函数。
先让我们看看函数的原型:
size_t strlen(const char *str);
size_t 是无符号整数类型,const char* str 说明了传过来的指针参数所指向的数据无法更改。
聪明的宝子们肯定已经看出来了。
接下来的是我们要牢记的一些神奇秘诀:
//1.方法一 计数器的方式
int my_strlen(const char* str)
{
int count = 0;
while (*str)
{
str++;
count++;
}
return count;
}
//2.方法二 :递归的方式
int my_strlen(const char* str)
{
if (*str == '\0')
{
return 0;
}
else
{
return 1 + my_strlen(str + 1);
}
}
//3.方法三:指针-指针的方法
int my_strlen( char* str)
{
char* p = str;
while (*p != '\0')
{
p++;
}
return p - str;
}
strcpy strcat strcmp
cpy cat cmp 让我们来一步一步解刨。这三个都有c这个字母开头,我真聪明,哈哈。copy(复制) ,catenate(连接),compose(比较)。从这三个单词,我们就知道这三个函数分别是什么意思了,这说明了什么,说明了学好一手英语在边编程里还是有点用的。
先让我们看看这些函数的原型:
strcpy
char* strcpy(char * destination, const char * source );
返回值是char类型的指针,参数有两个,一个是目标字符指针,一个是源字符指针。
我得冷静的分析一会,嗯,与copy(复制)有目标,有源头。!!!!,我知道了,它是将源头的字符传到目标字符里去。
接下来是我们要注意的:
char *my_strcpy(char *dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while((*dest++ = *src++))
{
;
}
return ret;
}
strcat
char *strcat(char *dest, const char *src);
老样子,也是一样的。只不过dest不是空的了,是有字符数据的了。strcat是将src的字符追加到dest后面了,它是将src首字符代替dest里的“\0”,用src里的“\0”完成新的字符诞生。
接下来也是我们需要注意的:
源字符串必须以‘\0’结束
目标空间必须有足够的大,能容纳下源字符串的内容
目标空间必须可修改
下面有一个问题:字符串自己给自己追加,如何?
这是不对的:会导致缓冲区溢出,因为如果 s1 是一个固定大小的字符数组,将其自身拼接会超出数组原本的空间,引发未定义行为。
学会模拟它:
char *my_strcat(char *dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while(*dest)
{
dest++;
}
while((*dest++ = *src++))
{
;
}
return ret;
}
strcmp
int strcmp(const char *s1, const char *s2);
作为比较的函数:
1.它返回的是int 类型
2. 一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数
'\0'
学会模拟它:
int my_strcmp (const char * src, const char * dst)
{
int ret = 0 ;
assert(src != NULL);
assert(dest != NULL);
while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst)
++src, ++dst;
if ( ret < 0 )
{
ret = -1 ;
}
else if ( ret > 0 )
{
ret = 1 ;
}
return( ret );
}
strncpy strncat strncmp
这些字符函数都与前面的功能相似,只不过函数新加了一个参数num 与数量相关
strncpy
char * strncpy ( char * destination, const char * source, size_t num );
char* strncpy(char* dest, const char* src, size_t num)
{
char* ret = dest;
assert(dest != NULL);
assert(src != NULL);
for (int i = 0;i <num;i++)
{
if ((*src) != '\0')
{
*dest++ = *src++;
}
else
{
*dest++ = '\0';
}
}
*dest++ = '\0';
return ret;
}
strncat
char * strncat ( char * destination, const char * source, size_t num );
1.将源字符串的num个字符追加到目标字符串后。
2.与之前的strcat的注意事项一致。
模拟他:
char* strncat(char* dest, const char* src, size_t num)
{
char* ret = dest;
assert(dest != NULL);
assert(src != NULL);
while (*dest)
{
dest++;
}
for(int i =0;i<num && *src;i++)
{
*dest++ = *src++;
}
*dest++ = '\0';
return ret;
}
strncmp
int strncmp (const char * str1, const char * str2, size_t num);
1.将str2字符串与str1字符的num个字符进行比较。
2.有种情况,是num个字符比str1或str2多,那么对比到str1或str2的\0时停止,剩余num未比较的位置视为更小(因\0 ASCII 是 0,比字母 与 数字小)。
strstr
char * strstr ( const char *str1, const char * str2);
在str1字符串里查找str2字符串 其返回值是str2字符串出现在str1字符串的首个位置的地址。
如果没找到就会返回:NULL
学会模拟它:
char *my_strstr(const char *str1, const char *str2)
{
assert(str1);
assert(str2);
char *cp = (char *)str1;
char *substr = (char *)str2;
char *s1 = NULL;
if (*str2 == '\0')
return NULL;
while (*cp)
{
s1 = cp;
substr = (char *)str2;
while (*s1 && *substr && (*s1 == *substr))
{
s1++;
substr++;
}
if (*substr == '\0')
return cp;
cp++;
}
return NULL;
}
strtok
char * strtok ( char * str, const char * sep );
是一个将以sep里的字符标识如:,- 空格等等。在str里找到后其分隔符位置替换为 \0,直接修改原始字符串 str并返回的是原始字符串中被拆分出的子串的起始地址
1.首次调用传 str,后续传 NULL 可 “接着上次的位置继续分割”,直到返回 NULL 表示拆分完
token = strtok(str, delim);
while (token != NULL) {
// 输出分割出的子串
printf("%s\n", token);
// 后续调用:传入 NULL、分隔符、上下文(复用第一次的 context)
token = strtok(NULL, delim);
}
strerror
char *strerror(int errnum);
将错误码(如 errno 里的值 )转换成对应的错误描述字符串,方便开发者理解程序哪里出错。
字符函数
字符分类函数
| 函数 | 如果他的参数符合下列条件就返回真 |
|---|---|
| iscntrl | 任何控制字符 |
| isspace | 空白字符:空格 ' ',换页 '\f',换行 '\n',回车 '\r',制表符 '\t' 或者垂直制表符 '\v' |
| isdigit | 十进制数字 0~9 |
| isxdigit | 十六进制数字,包括所有十进制数字,小写字母 a~f,大写字母 A~F |
| islower | 小写字母 a~z |
| isupper | 大写字母 A~Z |
| isalpha | 字母 a~z 或 A~Z |
| isalnum | 字母或者数字,a~z,A~Z,0~9 |
| ispunct | 标点符号,任何不属于数字或者字母的图形字符(可打印) |
| isgraph | 任何图形字符 |
| isprint | 任何可打印字符,包括图形字符和空白字符 |
字符转换
|
int tolower(int c) |
将大写字母转换为小写 |
| int toupper(int c) | 将小写字母转换为大写 |
内存函数
mencpy menmove mencmp
其实这些函数与之前的有相似之处
mencpy
void *memcpy(void *dest, const void *src, size_t n);
1.dest:指向目标内存区域的指针,用于接收拷贝的数据。
2.src:指向源内存区域的指针,待拷贝的数据从这里获取。
3.n:要拷贝的字节数,决定复制数据的长度
4.从src 指向地址开始,拷贝n 字节数据到dest 指向地址;若源和目标内存重叠,行为未定义(可能出错 ),适合无重叠场景快速拷贝。
学会模拟它:
void * memcpy ( void * dst, const void * src, size_t count)
{
void * ret = dst;
assert(dst);
assert(src);
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
return(ret);
}
menmove
void *memmove(void *dest, const void *src, size_t n);
1.dest:指向目标内存区域的指针,用于接收拷贝的数据。
2.src:指向源内存区域的指针,待拷贝的数据从这里获取。
3.n:要拷贝的字节数,决定复制数据的长度
4.同样拷贝 n 字节数据从 src 到 dest,但支持源和目标内存重叠,内部会合理处理拷贝顺序(如从低地址到高地址,或反向拷贝 ),保证结果正确,是更安全的内存拷贝函数。
学会模拟它:
void * memmove ( void * dst, const void * src, size_t count)
{
void * ret = dst;
if (dst <= src || (char *)dst >= ((char *)src + count)) {
/*
* Non-Overlapping Buffers
* copy from lower addresses to higher addresses
*/
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
}
else {
/*
* Overlapping Buffers
* copy from higher addresses to lower addresses
*/
dst = (char *)dst + count - 1;
src = (char *)src + count - 1;
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst - 1;
src = (char *)src - 1;
}
}
return(ret);
}
mencmp
int memcmp(const void *s1, const void *s2, size_t n);
1.s1,s2:指向两个待比较的内存区域的指
2.n:要比较的字节数,即从两个内存起始地址开始,对比前 n 个字节。
3.按字节逐个比较 s1 和 s2 指向内存的前 n 字节:
- 若
s1与s2内容相等,返回0; - 若
s1对应字节序列 “大于”s2,返回正数(通常是差值,如s1某字节是0x41,s2是0x40,返回1); - 若
s1对应字节序列 “小于”s2,返回负数 ,常用于二进制数据(如结构体、非字符串数据 )的比较。
更多推荐



所有评论(0)