目录这里排版出现问题了,改了很长时间也没改好,只能图片代替,望读者见谅
一,前言
C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在 常量字符串中或者字符数组中。字符串常量 适用于那些对它不做修改的字符串函数。
二,字符串和内存函数
1,strlen
获取字符串的长度。
字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0' )。
参数指向的字符串必须要以 '\0' 结束。
注意函数的返回值为size_t,是无符号的( 易错 )
int main()
{
char* p = "hello";
char arr1[] = "hello";
char arr2[] = { 'h', 'e', 'l', 'l', 'o' };
printf("%d\n", strlen(p));
//结果为5,strlen求的是字符串的长度,不包含\0
printf("%d\n", strlen(arr1));
//结果为5,strlen求的是字符串的长度,不包含\0
printf("%d\n", strlen(arr2));
//因为字符型不包含\0,但字符串需要找到\0才可结束,所以在'o'之后继续向后读取直到找到\0,所以是一个随机值
}
#include <stdlib.h>
#include <string.h>
int main()
{
char arr[] = "abcdef";
printf("%d\n", strlen(arr)); // 6
printf("%d\n", strlen(arr + 1)); // 5
printf("%d\n", strlen(&arr + 1)); //类型不匹配
printf("%d\n", strlen(&arr)); // 类型不匹配
printf("%d\n", strlen(&arr[0])); // 6
printf("%d\n", strlen(*&arr)); // 6
printf("%d\n", strlen(&*arr)); // 6
printf("%d\n", strlen(&arr[1] + 1));//4
return 0;
}
#include <stdlib.h>
#include <string.h>
int main()
{
char* p = "abcdef";
printf("%d\n", strlen(p)); // 6
printf("%d\n", strlen(p + 1)); // 5
printf("%d\n", strlen(*p)); // 类型不匹配
printf("%d\n", strlen(p[1])); // 类型不匹配
printf("%d\n", strlen(&p)); // 类型不匹配.
printf("%d\n", strlen(&p[1] + 1)); // 4
printf("%d\n", strlen(*&p)); // 6
printf("%d\n", strlen(&*p)); // 6
return 0;
}
int main()
{
//3-6 - -3
//size_t
if (strlen("abc") - strlen("abcdef") > 0)
{
printf("hehe\n");
}
else
{
printf("haha\n");
}
return 0;
}
//strcpy
//strcat
//strcmp
//长度不受限制,不稳定
2,strcpy
复制一个字符串。
源字符串必须以 '\0' 结束。
会将源字符串中的 '\0' 拷贝到目标空间。
目标空间必须足够大,以确保能存放源字符串。
目标空间必须可变
int main()
{
char arr1[] = "xxx";
//const char* p = "xxxxxxxxxxxxxx";
char arr2[] = "hello\0abc";
//char arr2[] = { 'a', 'b', 'c' };
strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
3,strcat
附加一个字符串
源字符串必须以 '\0' 结束。
目标空间必须有足够的大,能容纳下源字符串的内容。
目标空间必须可修改
int main()
{
//strcpy字符串拷贝\0是停止的标志
//stract字符串连接
char arr1[20] = "abc";//保证数据可以放进去
char arr2[20] = { 'd','e','f' };//无\0程序将会出现问题
char arr3[20] = { 'd','e','f' ,'\0'};//正确书写
strcat(arr1, arr2);
strcat(arr1,"dssrg");
printf("%s\n", arr1);
return 0;
}
#include<string.h>
#include<stdio.h>
#include<assert.h>
my_strcat(char* dest, const char* src)
{
assert(dest && src);
// a b c \0
// d e f \0
//1,找到目标字符串的末尾\0
//2,追加字符串直到\0
//返回类型是char*,stract返回目标空间的起始地址
char* ret = dest;
while (*dest)
{
dest++;
}
//与strcpy追加相等
while (*dest++ = *src++)
{
;
}
return ret;
//返回类型是char*,stract返回目标空间的起始地址
}
int main()
{
//strcpy字符串拷贝\0是停止的标志
//stract字符串连接
char arr1[20] = "abc";//保证数据可以放进去
char arr2[20] = { 'd','e','f' };//无\0程序将会出现问题
char arr3[20] = { 'd','e','f' ,'\0' };//正确书写
my_strcat(arr1, arr3);
printf("%s\n", arr1);
return 0;
}
4,strcmp
比较字符串。
标准规定:
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
int main()
{
char arr1[] = "asihvw";
char arr2[] = "asns";
//字符串一一比较
//返回值有三种可能性
//发现相等\0,停下来,结果为0;
//不同比较的是字符串对应的ascii码值
int ret = strcmp(arr1, arr2);
if (ret == 0)
{
printf("=\n");
}
else if (ret < 0)
{
printf("<\n");
}
else
{
printf(">\n");
}
printf("%d\n", ret);
return 0;
}
#include<string.h>
#include<stdio.h>
#include<assert.h>
//不修改加const
int my_strcmp(const char* s1,const char* s2)
{
assert(s1 && s2);
// a b c d e \0
// a d n \0
//c与n不相等,比较ascii值
// a b c \0
// a b c \0
//相等
while (*s1 == *s2)
{
if (*s1 == '\0')
{
return 0;
}
s1++;
s2++;
}
return *s1 - *s2;//第一个字符串小于第二字符串,返回负数
}
int main()
{
char arr1[] = "asihvw";
char arr2[] = "asns";
//字符串一一比较
//返回值有三种可能性
//发现相等\0,停下来,结果为0;
//不同比较的是字符串对应的assic码值
int ret = my_strcmp(arr1, arr2);
if (ret == 0)
{
printf("=\n");
}
else if (ret < 0)
{
printf("<\n");
}
else
{
printf(">\n");
}
printf("%d\n", ret);
return 0;
}
//strncpy
//strncat
//strncmp
//长度受限制的字符串,稳定
5,strncpy
将一个字符串的字符复制到另一个字符串。
拷贝num个字符从源字符串到目标空间。
如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个
int main()
{
char arr1[20] = "sho\0isfohf";
char arr2[] = "svnv";
//相对来说比较稳定
strncpy(arr1, arr2, 2);
// strncpy(arr1, arr2, 6);不够追加,前面正常拷贝,到后面凑\0,个数一定要注意限制
printf("%s\n", arr1);
return 0;
}
6,strncat
附加字符串的字符。
将源的第一个num字符加上终止的null字符追加到目标。
如果源中C字符串的长度小于num,则仅复制到终止的nullcharacter之前的内容
int main()
{
char arr1[20] = "abc\0sojcss";
char arr2[] = "chcda";
strncat(arr1, arr2, 3);//遇到\0就停止追加
return 0;
}
7,strncmp
比较两个字符串的字符。
比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。
int main()
{
char arr1[] = "schcss";
char arr2[] = "schsrgvv";
int ret = strncmp(arr1, arr2, 3);//前三个相等,返回值为0
printf("%d\n", ret);
return 0;
}
8,strstr
找到一个子串。
返回指向str1中第一个出现的str2的指针,如果str2不是str1的一部分,则返回空指针
strstr字符串查找函数
帮我们在一个字符串查找子字符串是否存在
int main()
{
char arr1[] = "i am student";
char arr2[] = "student";
//查找arr1中arr2第一次出现的位置
char* ret = strstr(arr1, arr2);
if(ret == NULL)
{
printf("找不到\n");
}
else
{
printf("找到了\n");
}
return 0;
}
char* my_strstr(const char* str1, const char* str2)
{
//建议画图分析比较好理解
assert(str1 && str2);
//abcdef
//bcd
//找匹配的可能性,相等后一直比较是否相等,遇到\0后结束匹配
//abbbcdef
//bbc
//当起始位置不相等,跳过不相等的字符,类似于第一个比较,匹配,
//为了方便匹配,我们需要创建指针,记录从哪开始匹配,子串要回归匹配,字串\0就应该表示查找完
//三种情况停止匹配
char* s1;
char* s2;
char* cp;//cp 最开始指向abbcdef中的a,与s1匹配对应的b,跳过a,到第二个相等时,++,匹配不成功,s2回到原点b
if (*str2 == '\0')
{
return str1;
}
while (*cp)
{
s1 = cp;
s2 = str2;//比较
while (*s1 != '\0'&& * s2 != '\0' && *s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return cp;
}
cp++;
}
return NULL;//找不到的情况
}
int main()
{
char arr1[] = "i am student";
char arr2[] = "student";
//查找arr1中arr2第一次出现的位置
char* ret = my_strstr(arr1, arr2);
if (ret == NULL)
{
printf("找不到\n");
}
else
{
printf("找到了\n");
}
return 0;
}
9,strtok
在字符串中查找下一个标记。
sep参数是个字符串,定义了用作分隔符的字符集合
第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改
变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
如果字符串中不存在更多的标记,则返回 NULL 指针
//strtok
//对一个字符串切割
int main()
{
char arr1[] = "cscwocsoe.dj";
char arr2[100] = { 0 }; //临时数据
char sep[] = "s.";
strcpy(arr2, arr1);//分割字符串
char* ret = NULL;
for (ret = strtok(arr2, sep); ret != NULL;ret = strtok(NULL,sep))
{
printf("%s\n", ret);
}
/*strtok(arr2, sep);
strtok(NULL, sep);*/
return 0;
}
10,strerror
获取系统错误消息(strerror)或打印用户提供的错误消息(strerror)
返回错误码,所对应的错误信息
字符分类函数:
函数 | 如果他的参数符合下列条件就返回真 |
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 | 任何可打印字符,包括图形字符和空白字符 |
#include <string.h>
//显示错误代码0至9的错误原因描述
int main()
{
int i;
for (i = 0; i < 10; i++)
{
printf("%d : %s\n", i, strerror(i));
}
return 0;
}
11,memcpy
在缓冲区之间复制字符,内存拷贝。
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
这个函数在遇到 '\0' 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7 };
int arr2[20] = { 0 };
memcpy(arr2, arr1, 40);//拷贝的是整型数据
int i = 0;
for (i = 0; i < 20; i++)
{
printf("%d", arr2[i]);
}
return 0;
}
my_memcpy(void* dest, const void* src, size_t count)
{
void* set = dest;
assert(dest && src);
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return set;
}
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7 };
int arr2[20] = { 0 };
my_memcpy(arr2, arr1, 40);//拷贝的是整型数据
int i = 0;
for (i = 0; i < 20; i++)
{
printf("%d", arr2[i]);
}
return 0;
}
12,memmove
将一个缓冲区移动到另一个缓冲区。
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用memmove函数处理。
#include<stdio.h>
#include<string.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
// 1,2,1,2,3,4,7,8,9,10
//memcpy(arr+2, arr, 16);
//memcpy - 只要完成了不重叠的内存拷贝就完成任务
memmove(arr + 2, arr, 16);
//内存拷贝时,出现内存重叠的现象,应该使用memmove
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
13,memcmp
比较两个缓冲区中的字符
比较从ptr1和ptr2指针开始的num个字节
memcmp函数是用于比较字符串的,比较内存前N个字节;
该函数在头文件<string.h>中,函数定义为:int memcmp (const void *s1, const void *s2, size_t n);
字符串大小的比较是以ASCII 码表上的顺序来决定,次顺序亦为字符的值。memcmp()首先将s1 第一个字符值减去s2 第一个字符的值,若差为0 则再继续比较下个字符,若差值不为0 则将差值返回。例如,字符串"Ab"和"ba"比较则会返回字符'A'(65)和'b'(98)的差值(-33)。
返回值:两个字符串内容完全一样,返回0;若S1大于S2,则大于0,反之则小于0;
#include <string.h>
int main()
{
char* a = "aBcDeF";
char* b = "AbCdEf";
char* c = "aacdef";
char* d = "aBcDeF";
printf("memcmp(a, b):%d\n", memcmp((void*)a, (void*)b, 6));
printf("memcmp(a, c):%d\n", memcmp((void*)a, (void*)c, 6));
printf("memcmp(a, d):%d\n", memcmp((void*)a, (void*)d, 6));
}
#include <stdio.h>
#include <string.h>
int main()
{
char buffer1[] = "DWgaOtP12df0";
char buffer2[] = "DWGAOTP12DF0";
int n;
n = memcmp(buffer1, buffer2, sizeof(buffer1));
if (n > 0)
printf("'%s' is greater than '%s'.\n", buffer1, buffer2);
else if (n < 0)
printf("'%s' is less than '%s'.\n", buffer1, buffer2);
else
printf("'%s' is the same as '%s'.\n", buffer1, buffer2);
return 0;
}
二,库函数的模拟实现
1, assert()函数介绍
assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行。
原型定义:
#include <assert.h>
void assert( int expression );
assert的作用是先计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
int main( void )
{
FILE *fp;
fp = fopen( "test.txt", "w" );//以可写的方式打开一个文件,如果不存在就创建一个同名文件
assert( fp ); //所以这里不会出错
fclose( fp );
fp = fopen( "noexitfile.txt", "r" );//以只读的方式打开一个文件,如果不存在就打开文件失败
assert( fp ); //所以这里出错
fclose( fp ); //程序永远都执行不到这里来
return 0;
}
2,模拟实现strlen
(1)计数器方式
int my_strlen(const char* str)
{
int count = 0;
while (*str)
{
count++;
str++;
}
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* s)
{
char* p = s;
while (*p != '\0')
p++;
return p - s;
}
3,模拟实现strcpy
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char*dest, const char* src)
{
assert(dest && src);
char* ret = dest;
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[] = "xxxxxxxx";
char arr2[] = "abc";
printf("%s\n", my_strcpy(arr1, arr2));
return 0;
}
4,模拟实现strcat
#include<stdio.h>
#include<assert.h>
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;
}
5,模拟实现strstr
#include<stdio.h>
#include<assert.h>
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 = str2;
while (*s1 && *substr && (*s1 == *substr))
{
s1++;
substr++;
}
if (*substr == '\0')
return cp;
cp++;
}
}
6,模拟实现strcmp
#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* src, const char* dst)
{
int ret = 0;
assert(src != NULL);
assert(dst != 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);
}
7,模拟实现memcpy
#include<stdio.h>
#include<assert.h>
void* memcpy(void* dst, const void* src, size_t count)
{
void* ret = dst;
assert(dst);
assert(src);
//copy from lower addresses to higher addresses
while (count--) {
*(char*)dst = *(char*)src;
dst = (char*)dst + 1;
src = (char*)src + 1;
}
return(ret);
}
8,模拟实现memmove
#include<stdio.h>
void* memmove(void* dst, const void* src, size_t count)
{
void* ret = dst;
if (dst <= src || (char*)dst >= ((char*)src + count)) {
while (count--) {
*(char*)dst = *(char*)src;
dst = (char*)dst + 1;
src = (char*)src + 1;
}
}
else {
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);
}