@[toc]
一、字符串操作函数
(1)strlen()
求字符个数,不计算'\0'。
函数作用:
1、当不完全初始化的数组求字符个数时,后面未初始化的默认为0,ascill码为0的是\0,所以这种情况区别于完全初始化是可以求出字符个数的。
2、strlen的返回类型是无符号整数,当两个strlen相减时,可能会出现负数,这个时候它是以无符号形式表现出来,所以它会是一个很大的正数。
函数原型:
size_t strlen ( const char * str );
头文件:
#include <string.h>
模拟实现:
//1、计数器的方法
#include<stdio.h>
#include<assert.h>
size_t my_strlen(const char *str)
{
int i = 0;
assert(str != NULL);
while (*str != '\0')
{
str++;
i++;
}
return i;
}
//2.指针减去指针
/*
int my_strlen(char* s)
{
char* p = s;
while (*p != '\0')
p++;
return p - s;
}*/
//3.递归,不创建临时变量计数器
/*int my_strlen(const char * str)
{
if(*str == '\0')
return 0;
else
return 1+my_strlen(str+1);
}
*/
int main()
{
char arr[] = "abcdef";
printf("%u", my_strlen(arr));
return 0;
}
(2)strcpy()
函数作用:
字符串复制到对应的空间
- 源字符串必须以'\0'结束。
- 会将源字符串中的‘\0’拷贝到目标空间。
- 空间必须足够大,以确保能存放源字符串。否则程序奔溃,栈空间被破坏。
- 目标空间必须可变。比如字符指针的字符是常量,不可以被修改,这样就不能用这个函数。
- 返回值的类型可以是void但是他之所以设置为cahr*是为了实现链式访问。
函数原型:
char* strcpy(char * destination, const char * source);
头文件:
#include <string.h>
模拟实现:
#include <stdio.h>
#include <assert.h>
char* my_strcpy(char *dest,const char *src)
{
assert(dest != NULL && src != NULL);//assert(dest && src );
//while (*src != '\0')
//{
// *dest = *src;
// src++;
// dest++;
//}
//*dest = '\0';
char* ret = dest;
while (*dest++ = *src++)
{
}
return ret;
}
int main()
{
char arr[] = "xxxxxxxxxxx";
char *arr1 = "heelo bit";
printf("%s", my_strcpy(arr, arr1));
return 0;
}
(3)strcat()
函数作用:
在dest指向的字符串追加一串src指向的字符串,并返回dest的地址,追加失败应该没有返回值(以后再看看,不太清楚)
- 源字符串必须以'\0'结束
- 目标空间必须足够大,能容纳下字符串的内容
- 目标空间必须可以修改
- 字符串自己给自己追加,如何?下面代码是不能自己给自己追加,会死循环,但是库函数里的strcat可以自己追加。要看编译器的代码是否支持。
函数原型:
char * strcat ( char * destination, const char * source );
头文件:
#include <string.h>
模拟实现:
#include <assert.h>
#include <stdio.h>
#include <string.h>
char* my_strcat(char* dest,const char* src)
{
assert(dest && src);
char* ret = dest;
while (*dest!=0)
{
dest++;
}
while (*dest++ = *src++)
{
}
return ret;
}
#include <stdio.h>
#include <assert.h>
int main()
{
char arr1[20] = "abcdef";
char arr2[10] = "ghijk";
printf("%s", my_strcat(arr1, arr2));
return 0;
}
//abcdefghjk
(4)strcmp()
函数作用:
判断两个字符串的大小,根据ascill值,相同往后推送直到判断出来
- 第一个字符串大于第二个字符串,则返回大于0的数字
- 第一个字符串等于第二个字符串,则返回0
- 第一个字符串小于第二个字符串,则返回小于0的数字
函数原型:
int strcmp ( const char * str1, const char * str2 );
头文件:
#include <string.h>
模拟实现:
#include <assert.h>
#include <stdio.h>
#include <string.h>
int my_strcmp(char* dest, char* src)
{
assert(src && dest);
while (*dest == *src)
{
if (*dest == 0)
return 0;
dest++;
src++;
}
return *dest - *src;
}
int main()
{
char arr1[] = "abcd";
char arr2[] = "abcf";
printf("%c",my_strcmp(arr1, arr2));
return 0;
}
(5)长度受限制的字符串函数
strncpy n是位数。n大于数组位数复制过去就是补0
strncmp 前几个和前几个比较 “abcd”>"afhkld"这里是比较大小 char* p = "abcd" ,其中“abcd”时a的地址。
strncat 这里位数不够时不会补
按字节追加
char *strncat( char *strDest, const char *strSource, size_t count );
模拟实现:
#include <assert.h>
#include <stdio.h>
#include <string.h>
char* my_strncat(char* dest, const char* src,int n)
{
assert(dest && src);
char* ret = dest;
while (*dest != 0)
{
dest++;
}
while (n--)
{
*dest++ = *src++;
}
*dest = '\0';
return ret;
}
//abcdefghjk
int main()
{
char arr1[20] = { 'a','b','c','\0' };
char arr2[20] = "ghjklmn";
my_strncat(arr1, arr2, 6);
printf("%s", arr1);
return 0;
}
详情看看课件。
(6)strstr()
函数作用:
在一个字符串中(str2)找是否存在另外字符子串(str1),找到返回子串在这个字符串中的地址,找不到返回NULL。
函数原型:
char * strstr ( const char *str1, const char * str2);
头文件:
<string.h>
使用例子:
情景1:
用于单次匹配,返回的是匹配成功的字符串以及后面的字符串
#include <stdio.h>
#include <string.h>
main()
{
char *s="GoldenGlobalView";
char *l="lob";
char *p;
p=strstr(s,l);
if(p)
printf("%s",p);
else
printf("NotFound!");
return 0;
}
//lobalView
情景2:
用于单次匹配,返回的是子串在母串的位置
#include <stdio.h>
#include <string.h>
main()
{
char *s="GoldenGlobalView";
char *l="lob";
char *p;
p=strstr(s,l);
if(p)
printf("%d",p-s+1);
else
printf("NotFound!");
return 0;
}
//8
//lob在GoldenGlobalView的第8位上。
情景3:
用于多次匹配知道母串结束,记录子串在母串中出现的次数
#include<stdio.h>
#include<string.h>
int main()
{
int i,n,j,k=0;
char a1[1001],a2[1001];
scanf("%s %s",a1,a2);
char *p;
p=a1;
while( ( p=strstr(p,a2) ) != NULL)//p为子串与母串匹配成功
{ //时,子串第一个符号在母串
k++; //中出现的位置地址
p++; //p++后才能匹配下一个,否则无法退出循环
}
printf("%d",k);
}
//abababababa
//aba
//5
//aba出现了5次
模拟实现:
#include <assert.h>
#include <stdio.h>
#include <string.h>
char* my_strstr(const char* dest,const char* src)
{
assert(dest && src);
const char* s1 = dest;//两个源地址在后面字符匹配不成功时需要复原,用个假身替代它改变
const char* s2 = src;
const char* cur = dest;//被查字符串(dest)的起始地址
while (*cur)//当查字符串(dest)起始地址不为0时
{
s1 = cur;
s2 = src;
while (*s1 && *s2 && (*s1 == *s2))//三者均不能为0
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return (char*)cur;//定义的是const char*不能改,函数定义是char*,所以要强转
}
cur++;
}
return NULL;
}
int main()
{
const char arr1[20] = "abcdefghj";
const char arr2[20] = "efghj";
if (my_strstr(arr1, arr2) == NULL)
printf("找不到");
else
printf("%s",my_strstr(arr1, arr2));
return 0;
}
(7)strtok()
charstrtok(charstr,constcharsep);
- sep参数是个字符串,定义用作分隔符的字符集合。
- 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
- strtok函数找到str中的下一个标记,并将其用\0结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
- strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
如果字符串中不存在更多的标记,则返回NULL指针。
使用例子:
(8)strerror()
char * strerror ( int errnum );
返回错误码对应的信息。
常见错误(可以背背)
没有错误
No error
不允许操作
Operation not permitted
没有这样的文件或目录
No such file or directory
没有这样的过程
No such process
中断函数调用
Interrupted function call
输入/输出错误
Input/output error
没有这样的设备或地址
No such device or address
参数列表太长
Arg list too long
执行文件格式错误
Exec format error
错误的文件描述符
Bad file descriptor
没有子进程
No child processes
资源暂时不可用
Resource temporarily unavailable
空间不够
Not enough space
没有权限
Permission denied
错误的地址
Bad address
未知的错误
Unknown error
资源设备
Resource device
文件是否存在
File exists
不当的链接
Improper link
没有这样的装置
No such device
不是目录
Not a directory
是一个目录
Is a directory
无效的参数
Invalid argument
系统中打开的文件太多
Too many open files in system
打开的文件太多
Too many open files
I/O控制操作不当
Inappropriate I/O control operation
未知的错误
Unknown error
文件太大
File too large
设备上没有剩余空间
No space left on device
无效的寻求
Invalid seek
只读文件系统
Read-only file system
链接太多
Too many links
破碎的管
Broken pipe
域的错误
Domain error
结果太大
Result too large
未知的错误
Unknown error
避免资源死锁
Resource deadlock avoided
未知的错误
Unknown error
文件名太长
Filename too long
没有可用的锁
No locks available
二、字符分类函数(分类/转换):
返回的是随机值,非0就是真。以后要用到,可以背背用用
三、内存操作函数
字符操作函数只能操作字符,比如strcpy复制,strcmp对比,针对的对象都是字符,而内存操作函数的对象是内存,可以操作的对象更多。
(1)memcpy()
void * memcpy ( void * destination, const void * source, size_t num );
- 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
- 这个函数在遇到 '\0' 的时候并不会停下来。
- 如果source和destination有任何的空间重叠,复制的结果都是未定义的,应该是两片空间,也就是不能在同一块内存,特别是还有重叠的,这时候memmove这个函数比较好。
使用方法:
/* memcpy example */
#include <stdio.h>
#include <string.h>
struct {
char name[40];
int age;
} person, person_copy;
int main ()
{
char myname[] = "Pierre de Fermat";
/* using memcpy to copy string: */
memcpy ( person.name, myname, strlen(myname)+1 );
person.age = 46;
/* using memcpy to copy structure: */
memcpy ( &person_copy, &person, sizeof(person) );
printf ("person_copy: %s, %d \n", person_copy.name, person_copy.age );
return 0;
}
模拟实现:
//方法一
void * memcpy ( void * dst, const void * src, size_t count)
{
void * ret = dst;
assert(dst&&src);
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
return(ret);
}
(2)memmove()
void * memmove ( void * destination, const void * source, size_t num );
- 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
- 如果源空间和目标空间出现重叠,就得使用memmove函数处理。
使用例子
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] = "memmove can be very useful......";
memmove (str+20,str+15,11);
puts (str);
return 0;
}
模拟实现:
两种情况:
一种是dest在src前面、dest在src后面但是不重合,这个时候只需要首地址对首地址逐个更换即可。
一种是dest在src后面且重合,这个时候就是要尾地址对尾地址才可以,否则就会乱。
//方法一
#include <memory.h>
#include <stdio.h>
#include <assert.h>
void* my_memmove(void* dest, const void* src, size_t count)
{
assert(src && dest);
void* ret = dest;
if (src > dest)
{
while (count--)
{
*(char*)dest = *(char*)src;
((char*)dest)++; //dst = (char *)dst + 1;
((char*)src)++; // src = (char *)src + 1;
}
}
else
{
dest = (char*)dest + count - 1;
src = (char*)src + count + 1;
while (count--)
{
*(char*)dest = *(char*)src;
((char*)dest)--;
((char*)src)--;
}
return ret;
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9 };
memmove(arr, arr+2, 20);
int i = 0;
for (i = 0; i < 9; i++)
{
printf("%d", arr[i]);
}
return 0;
}
//方法二,与法一类似
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);
}
memcpy先出来,后来才有memmov,但是由之前程序员用mempy,不能随便删了,否则就编译不了了,但是现在编译器一般功能一样。
(3)memcmp()
int memcmp ( const void * ptr1,
const void * ptr2,
size_t num );
- 比较从ptr1和ptr2指针开始的num个字节,它不是以int型比较,是按字节比较
返回值如下:
/* memcmp example */
#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;
}
(4)memset()
void *memset( void *dest, int c, size_t count );
以字节为单位设置数字。
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9 };
memset(arr, 6, 20);
int i = 0;
for (i = 0; i < 9; i++)
{
printf("%d", arr[i]);
}
return 0;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。