C双引号代表字符串;字符串存储在char类型的数组中。
C语言中,字符串用空字符(null character)\0标识字符串结束;故数组容量 = 字符串字符数 + 1;
#1 字符串数组
定义:
char 字符串名[常量] =
用%s打印。
#2 字符串和字符
“x”和‘x’是不同的:
- “x”是派生类型(char数组);‘x’是基本类型(char)
- “x”由‘x’和\0组成
#3 字符串相关函数
#3.1 strlen()函数
所需头文件string.h;返回值是size_t类型(无符号整型),用%zd(C99 C11)或%u %lu打印返回值;从字符串第一个字符开始数,数到\0结束,\0不算计数范围内。
对于size_t类型有些需要注意的点:
int main()
{
if(strlen("abc")-strlen("abcdef") > 0)
{
printf(">\n");
}
else
{
printf("<=\n");
}
return 0;
}
最终打印出来的结果是>,因为strlen返回值是无符号整型,3-6得到一个很大的正数,因此是>;
可以进行如下修改:
if(strlen("abc") > strlen("abcdef"))
if(int(strlen("abc")-strlen("abcdef")) > 0)
#3.2 strcmp()函数
所需头文件string.h;用于比较两个字符串;若字符串相同,则返回0;
模拟实现一个strcmp:
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while(*str1==*str2)
{
if(*str1 == '\0')
return 0;
str1++;
str2++;
}
return (*str1 - *str2);
}
注:两个字符串比较不能用==
int main()
{
char arr1[20] = "zhangsan";
char arr2[] = "zhansan";
if(arr1 == arr2) // 比较的是arr1和arr2的地址(数组名)
printf("==\n");
else
printf("!=\n"); // 永远只打印这个
return 0;
}
#3.3 strcpy()函数
所需头文件string.h;用于将第二个字符串复制到第一个字符串当中(\0也会被复制过去);
要保证源数据有'\0';目标空间必须能够容纳源数据;目标空间必须是可修改的;当目标空间不够时,代码仍然能运行,因此其不是很安全;
int main()
{
char* p = "abcdef";
char arr[] = "bit";
strcpy(p, arr); // error,目标空间不可修改
return 0;
}
模拟实现strcpy():
char* my_strcpy(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
while(*dest++ = *src++); // 当src='\0'时,执行完*dest++ = *src++后得到'\0',也即0或者说逻辑假,跳出while循环
return ret;
}
使用示例:
int main()
{
char arr1[20] = "abcdef";
char arr2[] = "hello bit";
strncpy(arr1, arr2, 5);
printf("%s\n", arr1); // hellof
return 0;
}
#3.4 strncpy()函数
函数申明:
char* strncpy(char* desination, const char* source, size_t num); // num表示要拷贝的字符数
#3.5 strcat()函数
字符串追加,将源字符串添加到目标空间;目标空间必须能够容纳源数据;目标空间必须是可修改的;不能自己给自己追加;
模拟实现strcat():
char* my_strcat(char* destination, const char* source)
{
assert(destination && source);
char* ret = destination;
# 找到目标字符串的'\0'
while(*destination != '\0')
{
destination++;
}
# 赋值源字符串到目标字符串
while(*destination++ = *source++);
return ret;
}
#3.6 strncat()函数
比strcat()函数多了一个参数num,表示追加num个字符;在目标空间的第一个'\0'处开始追加(覆盖掉这个'\0');会自动在追加完成的字符串后面添加'\0';
#3.7 strstr()函数
使用示例:
int main()
{
char email[] = "zpw@bitejiuyeke.com";
char substr[] = "bitejiuyeke";
char* ret = strstr(email, substr);
if(ret == NULL)
printf("字串不存在");
else
printf("%s\n", ret);
return 0;
}
模拟实现(效率不够高):
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
const char* s1 = str1;
const char* s2 = str2;
const char* p = str1;
while(*p)
{
s1 = p;
s2 =str2;
while(*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
{
s1++;
s2++;
}
if(*s2 == '\0')
return (char*)p;
p++;
}
return NULL;
}
#3.8 strtok()函数
用于切割字符串;找到标记位后将其替换为'\0';strtok函数会改变源字符串,如果不想改变源字符串,需要先做好备份;
函数声明:
char* strtok(char* str, const char* sep);
strtok函数第一个参数不为NULL时,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置,并将该标记替换为'\0';
strtok函数第一个参数为NULL('\0')时,函数将在同一个字符串中被保存的位置开始,查找下一个标记;
如果字符串不存在更多的标记,则返回NULL指针;
使用示例:
int main()
{
const char* sep = "@.";
char email[] = "zhangpengwei@bitejiuyeke.com";
char copy[30] = {0};
strcpy(cp, email);
char* ret = strtok(cp, sep);
printf("%s\n", ret); // zhangpengwei
char* ret = strtok(NULL, sep);
printf("%s\n", ret); // bitejiuyeke
char* ret = strtok(NULL, sep);
printf("%s\n", ret); // com
char* ret = strtok(NULL, sep);
printf("%s\n", ret); // (null)
return 0;
}
当我们事先不知道字符串需要调用几次strtok才能全部切割完时,我们就需要进行判断,如下:
int main()
{
const char* sep = "@.";
char email[] = "zhangpengwei@bitejiuyeke.com";
char copy[30] = {0};
strcpy(cp, email);
char* ret = NULL;
for(ret=strtok(cp, sep); ret!=NULL; ret = strtok(NULL, sep))
{
printf("%s\n", ret);
}
return 0;
}
#3.9 strerror()函数
返回错误码(C语言的库函数在执行失败时,都会设置错误码)所对应的错误信息;
errno是C语言设置的一个全局的错误码存放的变量(需要包含<errno.h>);
使用示例:
#include <errno.h>
#include <string.h>
#include <stdio.h>
int main()
{
//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));
FILE* pf = fopen("test.txt", "r");
if(pf == NULL)
{
printf("%s\n", strerror(errno);
return 1;
}
else
{
}
return 0;
}
#3.10 memcpy()函数
从内存中拷贝,可以拷贝任意类型的数据;只能拷贝两块独立空间中的数据(要处理重叠的内存间数据拷贝的情况,使用memmove函数);
函数声明:
void* memcpy(void* destination, const void* source, size_t num); //num是要传递的字节总数
模拟实现memcpy():
void* my_memcpy(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
while(num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest+1; // dest如果没有强制转为char*,就不能加1,void*类型的指针不能+-
src = (char*)src+1;
}
return ret;
}
#3.11 memmove()函数
void* my_memmove(void* dest, const void* src, size_t num)
{
// 以下两种情况是重叠时的,不重叠的话怎么考都可以
// 目标地址dest比源地址src大(高位)时,从后向前实现比较容易
// 目标地址dest比源地址src小(低位)时,从前向后实现比较容易
assert(dest && src);
void* ret = dest;
if(dest<src) // 第二种情况
{
// 从前向后
while(num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest+1;
src = (char*)src+1;
}
}
else
{
// 从后向前
while(num--)
{
*((char*)dest+num) = *((char*)src+num);
}
}
return dest;
}
#3.13 memcmp()函数
int memcmp(const void* ptr1, const void* ptr2, size_t num); // 一个字节一个字节地比较
#3.14 memset()函数
void* memset(void* ptr, int value, size_t num); // 一个字节一个字节设置
#4 字符判断和处理函数
ctype.h头文件中,包含处理字符的函数。
#5 总结
1. C语言字符串存储在char类型数组中,由“字符串内容+\0“”组成,因此数组长度至少要比字符串内容多1;
2. 打印字符串用%s
3. 字符串用双引号,字符用单引号
4. 计算字符串长度的函数:strlen();比较两个字符串是否相同的函数:strcmp();复制一个字符串到另一个字符串中的函数:strcpy();这三个函数都需要string.h头文件
5. ctype.h头文件中有处理字符的函数