C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。
字符串常量适用于那些对它不做修改的字符串函数.
size_t strlen ( const char * str );
1.字符串以 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中’\0’ 前面出现的字符个数(不包含 ‘\0’ )。
2.参数指向的字符串必须要以 ‘\0’ 结束。
3.注意函数的返回值为size_t,是无符号的整数。
例:
#include
#include
int main()
{
const char*str1 = "abcdef"; //常量字符串后面默认带了个'\0'
const char*str2 = "bbb";
if(strlen(str2)-strlen(str1)>0) // 6-3
{
printf("str2>str1\n");
}
else
{
printf("srt1>str2\n");
}
return 0;
}
size_t strlen ( const char * str );
#include
size_t my_strlen(const char* str)
{
const char* start = str;
const char* end = str;
while (*end != '\0')
{
end++;
}
return end - start; //指针相减等于两指针间的元素个数
}
int main()
{
char arr[] = "abcdef";
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
char* strcpy(char * destination, const char * source );
注:前面的指针指向目标字符串,后面指针指向源字符串
1.源字符串必须以 ‘\0’ 结束。
2.将源指向的字符串复制到目标所指向的数组中,包括 ‘\0’ 字符(并在该点停止)。
3.目标空间必须足够大,以确保能存放源字符串。
4.目标空间必须可变。(常量字符串不能修改,目标空间不能是常量字符串)
#include
#include
int main()
{
char arr[20] = "abcdef";
char* m = "fff";
strcpy(arr, m);
printf("%s", arr);
return 0;
}
输出:fff
char* strcpy(char * destination, const char * source );
函数返回值为目标字符串起始地址
char* my_strcpy(char* dest, const char* src)
{
char* ret = dest;
while (*dest++ = *src++); //后置加加,先解引用,再赋值,再加加,直到*dest=*src='\0',退出循环
return ret;
}
int main()
{
char arr1[20] = "abc";
char arr2[] = "hello hello";
printf("%s\n", my_strcpy(arr1, arr2));
return 0;
}
输出:hello hello
char * strncpy ( char * destination, const char * source, size_t num );
1.拷贝num个字符从源字符串到目标空间。
2.如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
我们可以看到,在拷贝完源字符串后又追加了3个0
模拟实现:
char* m_strncpy(char* destination, const char* source, size_t num)
{
char* p = destination;
int i = 1;
while (*destination = *source)
{
destination++;
source++;
i++;
}
destination++;
num -= i;
while (num--)
{
*destination++ = '\0';
}
return p;
}
int main()
{
char arr[20] = "abcdef";
char* m = "abc";
printf("%s", strncpy(arr, m, 6));
return 0;
}
char * strcat ( char * destination, const char * source );
1.将源字符串的内容追加到目标字符串(包括 ‘\0’ ),目标中的 ‘\0’ 字符被源的第一个字符覆盖。
2.源字符串必须以 ‘\0’ 结束。
3.目标空间必须有足够的大,能容纳下源字符串的内容。
4.目标空间必须可修改。
例:
#include
#include
int main()
{
char arr[20] = "abcdef";
char* m = "abc";
printf("%s", strcat(arr, m)); //函数返回值为目标字符串起始地址
return 0;
}
输出:abcdefabc
char * strcat ( char * destination, const char * source );
#include
char* m_strcat(char* destination, const char* source)
{
char* p = destination;
while (*destination++);
destination--; //destination指向了\0后面的元素,所以--
while (*destination++ = *source++);
return p;
}
int main()
{
char arr[20] = "abcdef";
char* m = "abc";
printf("%s", m_strcat(arr, m));
return 0;
}
char * strncat ( char * destination, const char * source, size_t num );
1.将源的前 num 个字符追加到目标,外加一个终止空字符。
2.如果源中 C 字符串的长度小于 num,则仅复制到终止空字符之前的内容。
类似于前面的 strncpy 这里就不展开讲了。
int strcmp ( const char * str1, const char * str2 );
1.第一个字符串大于第二个字符串,则返回大于0的数字
2.第一个字符串等于第二个字符串,则返回0
3.第一个字符串小于第二个字符串,则返回小于0的数字
如何判断两个字符串呢?
假设有如下两个字符串:
char* m = "abcdef";
char* n = "abce";
strcmp(m, n);
首先是两个字符串的第一个字符比较,很明显相等,再比较下一个字符,直到字符 ‘d’ 与 ‘e’ 比较,字符 ‘d’ 是小于 ‘e’ 的,所以m指向的字符串小于n指向的字符串。
#include
#include
int main()
{
char* m = "abcdef";
char* n = "abce";
printf("%d", strcmp(m, n));
return 0;
}
输出:-1
#include
int m_strcmp(const char* str1, const char* str2)
{
while (*str1 == *str2)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
if (*str1 > *str2)
return 1;
if (*str1 < *str2)
return -1;
}
int main()
{
char* m = "abcdef";
char* n = "abca";
printf("%d", m_strcmp(m, n));
return 0;
}
输出:1
int strncmp ( const char * str1, const char * str2, size_t num );
比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。
有兴趣可以自己实现一下。
char * strstr ( const char *str1, const char * str2);
1.返回指向 str1 中第一次出现的 str2 的指针,如果 str2 不是 str1 的一部分,则返回空指针。
2.匹配过程不包括终止空字符,但它在那里停止。
int main()
{
char* m = "abcdef";
char* n = "cde";
printf("%s", strstr(m, n));
return 0;
}
输出:cdef
char * strstr ( const char *str1, const char * str2);
char* m_strstr(const char* str1, const char* str2)
{
const char* s1 = str1; //见下图,s1 标记目标字符串每次开始查找的首元素
const char* s2 = str2; // s2 标记源字符串每次开始查找的首元素
const char* p = str1; // p 标记每次从 s1 开始遍历的元素
if (*str2 == '\0')
{
return str1;
}
while (*p)
{
s1 = p;
s2 = str2; //每次循环开始将 s1 移到后一个元素处,s2 移到首元素处
while (*s1 != '\0' && *s2 != '\0' && (*s1 == *s2))
{
s1++;
s2++;
}
if (*s2 == '\0') //当源字符串遍历完了后即找到了
{
return (char*)p;
}
p++;
}
return NULL; //找不到子串
}
int main()
{
char* m = "abcdef";
char* n = "cde";
printf("%s", m_strstr(m, n));
return 0;
}
当第一次没找到后,第二次循环 s1 、p指向如图:
若 p 指向 ‘\0’ ,则表示找不到该字符串,退出循环。
void * memcpy ( void * destination, const void * source, size_t num );
1.函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
2.这个函数在遇到 ‘\0’ 的时候并不会停下来。
3.如果source和destination有任何的重叠,复制的结果都是未定义的。
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
memcpy(arr2, arr, 20);
return 0;
}
我们来看看内存分配情况:
由图可以看出memcpy函数改变了20字节的内容,也就是5个整型,改变了5个整型数,那么它是什么方式改变这20字节的呢?
从左到右一个字节一个字节的依次复制,直到20个字节。
void* m_memcpy(void* destination, const void* source, size_t num)
{
void* p = destination;
while (num--) //循环num次
{
*(char*)destination = *(char*)source; //char型访问一个字节,方便逐个字节打印
destination = (char*)destination + 1;
source = (char*)source + 1;
}
return p;
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
m_memcpy(arr2, arr, 20);
return 0;
}
不知道大家有没有想过,如果是自己复制自己,那memcpy为什么不能用呢?什么函数可以实现呢?
memcpy函数会改变目标数据,如果目标数据与源数据有重叠,可能导致
如下情况:
假设我要把1 2 3 4 复制到3 4 5 6 的位置上,那么从前往后就是如图,我们可以看到原来的3 4 变为了1 2 ,然后1 2 再复制到5 6 处,原来的3 4 5 6 就变为了1 2 1 2,这与我们要的结果不同,所以不能用该函数了,那么我们应该如何才能正确打印呢?
我们发现如果从后往前复制就可以很好的避免这种情况发生,即从两数据重叠处开始复制。而memmove函数就很好的解决了这点。
我们来看看memmove函数:
void * memmove ( void * destination, const void * source, size_t num );
1.和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
2.如果源空间和目标空间出现重叠,就得使用memmove函数处理
#include
#include
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr1 + 2, arr1, 20);
return 0;
}
void* m_memmove(void* destination, const void* source, size_t num)
{
void* p = destination;
if (destination < source)
{
while (num--)
{ //从前往后复制
*(char*)destination = *(char*)source;
destination = (char*)destination + 1;
source = (char*)source + 1;
}
}
else
{
while (num--)
{ //从后往前复制
*((char*)destination+num) = *((char*)source+num);
}
}
return p;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
m_memmove(arr1 + 2, arr1, 20);
return 0;
}
char * strtok ( char * str, const char * sep );
1.sep参数是个字符串,定义了用作分隔符的字符集合。
2.第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
3.strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
4.strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
5.strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。(该函数具有记忆功能)
6.如果字符串中不存在更多的标记,则返回 NULL 指针。
我们来具体看看:
#include
#include
int main()
{
char arr[] = "zhangsan@baidu.com";
char buf[200] = { 0 };
strcpy(buf, arr); //将arr的内容复制到buf中
const char* p = "@.";
char* str = strtok(buf, p); //找到第第一个标记字符'@',并将其改为'\0',返回指向这个字符串的指针
printf("%s\n", str); //打印: zhangsan
str = strtok(NULL, p); //从上次标记的'\0'开始查找下一个标记符号,重复以上操作
printf("%s\n", str); //打印: baidu
str = strtok(NULL, p);
printf("%s\n", str); //打印: com
return 0;
}
简化一下:
int main()
{
char arr[] = "zhangsan@baidu.com";
char buf[200] = { 0 };
strcpy(buf, arr);
const char* p = "@.";
char* str = NULL;
for (str=strtok(buf, p); str!=NULL; str=strtok(NULL, p))
{
printf("%s\n", str);
}
return 0;
}
char * strerror ( int errnum );
返回错误码,所对应的错误信息。
#include
#include
#include
int main()
{ //errno - C语言提供的全局的错误变量
printf("%s\n", strerror(0));
//printf("%s\n", strerror(1));
//printf("%s\n", strerror(2));
//printf("%s\n", strerror(3));
//printf("%s\n", strerror(4));
return 0;
}
输出:
其他的错误变量对应的错误码可以自己输出试试。
返回真就是返回大于0的数,可用于条件判断。
int tolower ( int c ); //大写转小写
int toupper ( int c ); //小写转大写
我们来看下面一段小写转大写的代码:
#include
#include
int main()
{
char arr[] = "Are you ok?";
char* p = arr;
while (*p)
{
if (islower(*p)) //判断是否是小写
{
*p = toupper(*p); //小写转大写
}
p++;
}
printf("%s\n", arr);
return 0;
}
输出:ARE YOU OK?
本希望本期内容对大家能有所帮助,下期见了~