函数部分的解析来源于( C++资源网络)
写代码时对字符和字符串的处理很频繁,但是C语言中本身没有字符串类型,字符串通常存储在常量字符串中或者字符数组中。常量字符串适用于那些对它不做修改的字符串函数。
Get string length获取字符串长度
返回C语言中字符串的长度。
size_t strlen ( const char * str );
字符串的长度由终止字符确定。字符串的长度与字符串开头和终止空字符之间的字符数一样长(不包括终止空字符本身)。
注: 1. 字符串以’\0’作为结束标志。 2. 函数的返回值为size_t,是无符号的。 size_t==unsigned int
int main ()
{
char szInput[256];
printf ("Enter a sentence: ");
gets (szInput);
printf ("The sentence entered is %u characters long.\n",(unsigned)strlen(szInput));
return 0;
}
Copy block of memory复制字符串
将源所指向的 C 字符串复制到**目标**所指向的数组中,包括终止空字符(并在该点停止)。
char * strcpy ( char * destination, const char * source );
为避免溢出,目标所指向的数组的大小应足够长,以包含与源相同的 C 字符串(包括终止空字符),并且不应在内存中与 source 重叠。
注:1. 源字符串必须以’\0’结束。2. 结束标志’\0’也会被拷贝。 3. 目标空间必须足够大。 4. 目标空间必须可变。
int main ()
{
char str1[]="Sample string";
char str2[40];
char str3[40];
strcpy (str2,str1);
strcpy (str3,"copy successful");
printf ("str1: %s\nstr2: %s\nstr3: %s\n",str1,str2,str3);
return 0;
}
Concatenate strings连接字符串
将源字符串的副本追加到目标字符串。目标中的终止空字符被源的第一个字符覆盖,并且在目标中由两者串联形成的新字符串的末尾包含一个空字符。
char * strcat ( char * destination, const char * source );
目的地和来源不得重叠。自己不能给自己追加。
注:1. 源数组必须以’\0’结束。2. 目标空间必须足够大,能容纳下源字符串的内容。 3. 目标空间必须是可修改的。**重点:**strcat不能自己给自己追加。
int main ()
{
char str[80];
strcpy (str,"these ");
strcat (str,"strings ");
strcat (str,"are ");
strcat (str,"concatenated.");
puts (str);
return 0;
}
Compare two strings比较两个字符串
将 C 字符串str1与 C 字符串str2进行比较。
返回值 | 表明 |
---|---|
<0 | 第一个不匹配的字符在ptr1中的值低于ptr2中的值 |
0 | 两个字符中的内容相等 |
>0 | 第一个不匹配的字符在ptr1中的值低于ptr2中的值 |
int strcmp ( const char * str1, const char * str2 );
此函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续使用以下对,直到字符不同或达到终止空字符。
int main ()
{
char key[] = "apple";
char buffer[80];
do {
printf ("Guess my favorite fruit? ");
fflush (stdout);
scanf ("%79s",buffer);
} while (strcmp (key,buffer) != 0);
puts ("Correct answer!");
return 0;
}
Copy characters from string从字符串中复制字符
将源的前num字符复制到目标。如果在复制num个字符之前找到源C 字符串(由空字符指示)的末尾,则目标将填充零,直到向其写入总共num个字符。
char * strncpy ( char * destination, const char * source, size_t num );
如果源的长度超过 num,则不会在目标末尾隐式追加空字符。
因此,在这种情况下,目标不应被视为以空值结尾的 C 字符串(这样读取它会溢出)。
目的地和来源不得重叠(重叠时,请参阅 memmove 以了解更安全的替代方案)。
int main ()
{
char str1[]= "To be or not to be";
char str2[40];
char str3[40];
/* copy to sized buffer (overflow safe): */
strncpy ( str2, str1, sizeof(str2) );
/* partial copy (only 5 chars): */
strncpy ( str3, str2, 5 );
str3[5] = '\0'; /* null character manually added */
puts (str1);
puts (str2);
puts (str3);
return 0;
}
Append characters from string
将源的前num个字符追加到目标,加上一个终止空字符。
char * strncat ( char * destination, const char * source, size_t num );
如果源中 C 字符串的长度小于num,则仅复制直到终止空字符的内容。
如果source拷贝进去之后desination还有原本存在的字符会主动追加一个’\0’。
如果追加长度num大于strlen(source),则只追加source原本的字符串和一个’\0’
int main ()
{
char str1[20];
char str2[20];
strcpy (str1,"To be ");
strcpy (str2,"or not to be");
strncat (str1, str2, 6);
puts (str1);
return 0;
}
Compare characters of two strings 比较两个字符串的字符
将 C 字符串str1的最多 num个字符与 C 字符串str2的字符数进行比较。
int strncmp ( const char * str1, const char * str2, size_t num );
此函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续向后比较,直到字符不同,直到达到终止空字符,或者直到两个字符串中的num字符匹配,以先发生的情况为准。
int main ()
{
char str[][5] = { "R2D2" , "C3PO" , "R2A6" };
int n;
puts ("Looking for R2 astromech droids...");
for (n=0 ; n<3 ; n++)
if (strncmp (str[n],"R2xx",2) == 0)
{
printf ("found %s\n",str[n]);
}
return 0;
}
**Locate substring ** 定位子字符串
返回一个指向str1中第一次出现的str2的指针,如果str2 不是 str1的一部分,则返回空指针。
const char * strstr ( const char * str1, const char * str2 );
char * strstr ( char * str1, const char * str2 );
匹配过程不包括终止空字符,但碰到它就此停止。
int main ()
{
char str[] ="This is a simple string";
char * pch;
pch = strstr (str,"simple");
if (pch != NULL)
strncpy (pch,"sample",6);
puts (str);
return 0;
}
Split string into tokens 将字符串拆分为标记
对该函数的一系列调用将str拆分为标记,这些标记是由分隔符中的任何字符分隔的连续字符序列。
char * strtok ( char * str, const char * delimiters );
在第一次调用时,函数需要一个C字符串作为str的参数,str的第一个字符用作扫描标记的起始位置。在随后的调用中,函数需要一个空指针,并使用最后一个标记结束后的位置作为扫描的新起始位置。
要确定标记的开头和结尾,函数首先从起始位置扫描分隔符中未包含的第一个字符(即标记的开头)。然后从标记的这个开头开始扫描分隔符中包含的第一个字符,它将成为标记的结尾。如果找到终止的空字符,扫描也会停止。
令牌的这一端将自动替换为空字符,函数将返回令牌的开头。
在对strtok的调用中找到str的终止null字符后,所有后续对该函数的调用(以null指针作为第一个参数)都会返回null指针。
第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
strtok函数找到str中的下一个标记,并将其用’\0’结尾,返回一个指向这个标记的指针。
(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改)<提前备份>
strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存在字符串中的位置。
strtok函数的第一个参数为NULL,函数将在同一个字符串(我的理解是上次传递的字符串)中被保存的位置开始,查找下一个标记。
如果字符串中不存在更多的标记,则返回NULL指针。
int main ()
{
char str[] ="- This, a sample string.";
char * pch;
printf ("Splitting string \"%s\" into tokens:\n",str);
pch = strtok (str," ,.-");
//因为每次只切割出一部分所以要使用while循环。
while (pch != NULL)
{
printf ("%s\n",pch);
pch = strtok (NULL, " ,.-");
}
return 0;
}
Get pointer to error message string获取指向错误消息字符串的指针
错误码是程序员知道的,但是要转化为用户能看懂的错误信息,就需要使用函数将其转化为错误信息。
解释errnum的值,生成一个字符串,其中包含一条描述错误条件的消息,就好像被库的函数设置为errno一样。
char * strerror ( int errnum );
字符串。对此函数的进一步调用可能会覆盖其内容(不需要特定的库实现来避免数据争用)。strerror 生成的错误字符串可能特定于每个系统和库实现。
#include
#include
#include
int main ()
{
FILE * pFile;
//打开文件
pFile = fopen ("unexist.ent","r");
if (pFile == NULL)
printf ("Error opening file unexist.ent: %s\n",strerror(errno));
else{
printf("Win opening file!")
}
return 0;
}
//errno在头文件中
//errno是一个全局的错误码变量
//当C语言的库函数在执行过程中,发生错误,就会把对应的错误码,赋值到errno中
函数使用需要的头文件
#include
函数 | 参数符合以下条件返回真值 |
---|---|
iscntrl | 任何控制字符 |
isspace | 空白字符:空格’ ‘、换页’\f’、换行’\n’、回车’\r’、制表符或者垂直制表符’\v’ |
isdigit | 十进制0-9 |
isxdigit | 十六进制数字,包括十进制数字,小写a-f,大写A-F |
islower | 小写字母a-z |
isupper | 大写字母A-Z |
isalpha | 所有字母 |
isalnum | 字母或数字 |
ispunct | 标点符号,任何不属于数字或者字母的图形字符(可打印) |
isgraph | 任何图形字符 |
isprint | 任何可打印字符,包括图形字符和空白字符 |
一定是字符转换,不是字符串转化。
tolower(c);//转小写字母
toupper(c);//转大写字母
使用案例
int main(){
char str[] = "I Am A Good Student";
int i = 0;
while(str[i]){
if(isupper(str[i])){
str[i] = tolower(str[i]);
}
i++;
}
printf("%s\n",str);
return 0;
}
字符串函数主要功能是拷贝字符串,而拷贝数字的时候可能会出现提前结束的情况(1在内存中01 00 00 00,在拷贝的时候00等价于字符串结束的标志)。那么我们想要拷贝除了字符串,像数字这种会随时出现字符串终止标志的应该怎么处理呢?我们就出现了基于内存的操作。
Copy block of memory 复制内存块
将num字节的值从源指向的位置直接复制到目标所指向的内存块。源指针和目标指针所指向的对象的基础类型与此函数无关。
void * memcpy ( void * destination, const void * source, size_t num );
结果是数据的二进制副本。该函数不检查源中是否有任何终止空字符 - 它始终精确地复制数字字节。
为避免溢出,目标参数和源参数所指向的数组的大小应至少为 num 字节,并且不应重叠(对于重叠的内存块,memmove 是一种更安全的方法)。
简单理解
int main(){
int arr[] = {1,2,3,4,5};
int arr2[5] = {0};
memcpy(arr2,arr1,sizeof(arr1));
return 0;
}
进阶理解
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;
}
Move block of memory 移动内存块
将num字节的值从源所指向的位置复制到目标所指向的内存块。
注:处理重叠拷贝的情况
void * memmove ( void * destination, const void * source, size_t num );
复制就像使用中间缓冲区一样进行,允许目标和源重叠。
源指针和目标指针所指向的对象的基础类型与此函数无关;结果是数据的二进制副本。该函数不检查源中是否有任何终止空字符 - 它始终精确地复制数字字节。
为避免溢出,目标参数和源参数所指向的数组的大小应至少为 num 字节。
int main ()
{
char str[] = "memmove can be very useful......";
memmove (str+20,str+15,11);
puts (str);
return 0;
}
C语言标准规定
memcpy 只要处理,不重叠的内存拷贝就可以
memmove处理重叠内存的拷贝。但是现在好多的编译器memcpy都可以进行重叠拷贝
比较两个字符串
将ptr1指向的内存块的第一个字节数与ptr2指向的第一个数字字节进行比较
返回值 | 表明 |
---|---|
<0 | 在两个内存块中不匹配的第一个字节在ptr1中的值低于ptr2中的值(如果计算为无符号字符值) |
0 | 两个内存块的内容相等 |
>0 | 两个内存块中不匹配的第一个字节在ptr1中的值大于ptr2中的值(如果计算为无符号 char值) |
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
将ptr1指向的内存块的第一个字节数与ptr2指向的第一个数字字节进行比较,如果它们都匹配,则返回零,或者一个值不同于零,表示如果它们不匹配,则表示哪个值更大。请注意,与strcmp不同,该函数在找到空字符后不会停止比较。
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;
}
Fill block of memory填充内存块
void * memset ( void * ptr, int value, size_t num );
将ptr指向的内存块 的前num字节设置为指定值(解释为unsigned char
)。
ptr指向要填充的内存块的指针。num指的是字节数
#include
#include
int main ()
{
char str[] = "almost every programmer should know memset!";
memset (str,'-',6);
puts (str);
return 0;
}
//计数器方式
int my_strlen(const char *str) {
int count = 0;
while (*str) {
count++;
str++;
}
return count;
}
//不能创建临时变量计数器 --递归方式实现
int my_strlen(const char *str) {
if (*str == '\0')
return 0;
else
return 1 + my_strlen(str + 1);
}
//指针的方式实现
int my_strlen(char *s) {
char *p = s;
while (*p != '\0')
p++;
return p - s;
}
//1.参数顺序
//2.函数的功能,停止条件
//3.assert
//4.const修饰指针
//5.函数返回值
char *my_strcpy(char *dest, const char *src) {
//保留原来的地址。
char *ret = dest;
//保证输入的是有效的指针。
assert(dest != NULL);
assert(src != NULL);
//赋值过程
while ((*dest++ = *src++)) { ;
}
return ret;
}
char *my_strcat(char *dest, const char *src) {
//备份初始位置
char *ret = dest;
//保证指针的有效性
assert(dest != NULL);
assert(src != NULL);
//将dest的指向位置改为,当前字符串的'\0'位置,即拼接开始的地方
while (*dest) {
dest++;
}
//开始拼接操作 要将最后'\0'也拼接上
while ((*dest++ = *src++)) ;
return ret;
}
char *strstr(const char *str1, const char *str2) {
char *cp = (char *) str1;
char *s1, *s2;
if (!*str2)
return ((char *) str1);
while (*cp) {
s1 = cp;
s2 = (char *) str2;
while (*s1 && *s2 && !(*s1 - *s2))
s1++, s2++;
if (!*s2)
return (cp);
cp++;
}
return (NULL);
}
//只返回0,1,-1型
int my_strcmp(const char *word1,const char *word2) {
//保证指针的有效性
assert(word1!=NULL);
assert(word2!=NULL);
//相等的情况
while (*word1==*word2){
if(*word2=='\0'){
return 0;
}
word1++;
word2++;
}
//不相等的情况
if (*word1 > *word2){
return 1 ;
} else{
return -1;
}
}
//返回动态值的方法
int my_strcmp(const char *word1,const char *word2){
assert(word1!=NULL);
assert(word2!=NULL);
while (*word1==*word2){
if(*word2=='\0'){
return 0;
}
word1++;
word2++;
}
return (*word1-*word2);
}
char *my_strncpy(char *dest, const char *src, unsigned int count) {
//保证指针的有效性
assert(dest != NULL);
assert(src != NULL);
//备份返回值
char *res = dest;
//复制字符串
while (count && (*src)) {
*dest = *src;
src++;
dest++;
count--;
}
//超出的部分补位'\0'
if (count) {
while (count) {
count--;
*dest= '\0';
dest++ ;
}
}
return res;
}
char* my_strncat(char* front,const char * back,unsigned int count){
//保证指针的有效性
assert(front != NULL);
assert(back != NULL);
//备份返回值
char * res = front;
//找到被拼接的末尾位置的结束标志
while(*front++);
front--;//多计算了一次
//拷贝
while(count--){
if (!(*front++ = * *back++)){
return r;
}
}
*front = '\0';
return (res);
}
char *my_strstr(const char *str1, const char *str2) {
assert(str1 != NULL);
assert(str2 != NULL);
//拷贝
char *cp = (char *) str1;
//运算指针
char *s1 = NULL;
char *s2 = NULL;
//如果是空指针的话直接返回
if (!*str2)
return ((char *) str1);
//查找子集的过程
while (*cp) {
s1 = cp;//str1的运算指针
s2 = (char *) str2;//str2的运算指针
while ((*str1 != '\0') && (*str2 != '\0') && (*str1 == *str2)) {
s1++;
s2++;
}
// 找到子串
if (!*s2)
return cp;
cp++;
}
// 找不到子串
return (NULL);
}
注:NULL是空指针的意思,Null是’\0’。
不能实现重叠拷贝
//使用无类型指针接受参数
void *memcpy(void *dst, const void *src, size_t count) {
void *res = dst;
assert(dst);
assert(src);
//每次拷贝一个字节
while (count--) {
*(char *) dst = *(char *) src;
dst = (char *) dst + 1;//地址加一,还可以写成++(char*)dst
src = (char *) src + 1;//地址加一,还可以写成++(char*)src
}
return res;
}
可以实现重叠拷贝。
void *my_memmove(void *dest, const void *src, size_t count) {
//count是字节的个数
//有效性
assert(dest != NULL);
assert(src != NULL);
//备份返回值,因为不知道用于何处返回的是void类型的指针。
void *res = dest;
//判断dest和src的位置关系并决定拷贝方向
//从前往后 重叠时dest在src的左边,和不重叠的情况
if (dest < src || (char *) dest >= ((char *) src + count)) {
while (count--) {
*(char *) dest = *(char *) src;//一个字节一个字节的拷贝
dest = (char *) dest + 1;//这里不能使用内置加加的方法
src = (char *) src + 1;
}
}
// 从后往前
else {
dest = (char *) dest + count - 1;//更改开始位置
src = (char *) src + count - 1;//更改开始位置
while (count--) {
*(char *) dest = *(char *) src;
dest = (char *) dest - 1;
src = (char *) src - 1;
}
}
return res;
}
注:void*是无具体类型的指针。这样的函数是需要返回值的。