前言
C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在 常量字符串 中或者 字符数组 中。下面介绍C语言中的一些常用的字符串函数和内存函数的功能以及实现原理。
字符串函数(包含在头文件string.h)
strlen
函数声明
size_t strlen ( const char * str );
注:size_t类型其实就是unsigned long long类型
函数的功能
返回str指向的字符串长度,从str指向的字符算起,一直到字符串末尾的\0结束,如果没有\0,函数将继续增加str长度知道遇到\0为止。
使用案例
/* strlen example */#include <stdio.h>#include <string.h>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;}
输出结果为:
Enter sentence: just testing
The sentence entered is 12 characters long.
strcpy
函数声明
char* strcpy(char * destination, const char * source );
函数功能
把字符串source拷贝到字符串destination中,从source指向的位置开始拷贝直到遇到\0,并把\0拷贝到destination字符串中。使用此函数要注意为字符串destination分配的空间要大于为source字符串分配的空间,否则会越界访问报错。
注意事项
源字符串必须以 ‘\0’ 结束。
会将源字符串中的 ‘\0’ 拷贝到目标空间。
目标空间必须足够大,以确保能存放源字符串。
目标空间必须可变。
使用案例
/* strcpy example */#include <stdio.h>#include <string.h>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;}
输出结果为:
str1: Sample string
str2: Sample string
str3: copy successful
strcat
函数声明
char * strcat ( char * destination, const char * source );
函数功能
把source字符串接到destination字符串后,destination字符串末尾的\0会被覆盖,source字符串包括其末尾的\0都会被拷贝到destination字符串末尾。
注意事项
源字符串必须以 ‘\0’ 结束。
目标空间必须有足够的大,能容纳下源字符串的内容。
目标空间必须可修改。
使用案例
/* strcat example */#include <stdio.h>#include <string.h>int main (){char str[80];strcpy (str,"these ");strcat (str,"strings ");strcat (str,"are ");strcat (str,"concatenated.");puts (str);return 0;}
输出结果为:
these strings are concatenated.
strcmp
函数声明
int strcmp ( const char * str1, const char * str2 );
函数功能
函数从两字符串开头开始,按照每个字符的ASCII码大小向后一一比较每个字符,直到遇到不相等的字符或是遇到\0结束。如果遇到不相等的字符时str1中的字符ASCII码大于str2中的,函数返回一个正整数,如果小于,函数返回一个负整数。如果两字符串中的每个字符都相同,函数返回0。
使用案例
//猜水果游戏#include <stdio.h>#include <string.h>int main (){char key[] = "apple";char buffer[80];do {printf ("Guess my favorite fruit? ");scanf ("%79s",buffer);} while (strcmp (key,buffer) != 0);//猜错了就继续猜,猜对了才退出循环puts ("Correct answer!");return 0;}
strncpy
函数声明
char * strncpy ( char * destination, const char * source, size_t num );
函数功能
把source指向的字符串的前num个字节复制给destination指向的num个字节,如果source字符串本身并没有num那么长,那么复制的时候会把不够的地方用0补上,0是\0的ASCII码值,也就是说不够的地方复制的是\0。destination和source参数所指向的数组的大小至少应该是num字节,并且不应该重叠(对于重叠的内存块,memmove是一个更安全的方法)。
使用案例
/* strncpy example */#include <stdio.h>#include <string.h>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;}
Output:
To be or not to be
To be or not to be
To be
strncat
函数声明
char * strncat ( char * destination, const char * source, size_t num );
函数功能
把source字符串的前num个字节接到destination字符串后,destination字符串末尾的\0会被覆盖,source字符串接好后在最末尾加上\0,如果source中的C字符串长度小于num,则只复制到空字符结束之前的内容。
使用案例
/* strncat example */#include <stdio.h>#include <string.h>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;}
Output:
To be or not
strncmp
函数声明
int strncmp ( const char * str1, const char * str2, size_t num );
函数功能
比较C字符串stri和C字符串str2的num字符。这个函数开始比较每个字符串的第一个字符。如果它们相等,则继续执行下面的一对字符,直到字符不同,直到达到一个结束的空字符,或直到两个字符串中的num字符匹配,以先发生的为准。如果比较过程中str1的字符大于对应的str2的字符则返回大于0的整数,如果全部相同返回0,如果比较过程中str1的字符小于于对应的str2的字符则返回小于0的整数,这里比较原则是根据字符的ASCII码大小比较的。
使用案例
/* strncmp example */#include <stdio.h>#include <string.h>int main (){char str[][5] = {"R2D2" , "C3PO" , "R2A6" };int n;puts ("Looking for R2 astromech droids...");for (n=0 ; n<3 ; n++)//str[n]是二维数组第n+1行的地址或是理解为第n+1行字符串名或者地址if (strncmp (str[n],"R2xx",2) == 0){printf ("found %s\n",str[n]);}return 0;}
Output:
Looking for R2 astromech droids…
found R2D2
found R2A6
strstr
函数声明
char * strstr ( const char *str1, const char * str2);
函数功能
在str1字符串中匹配查找字符串str2,如果str2是str1的子串则函数返回str2第一次出现在str1中的地址,如果str2不是str1的子串则返回NULL。在匹配过程中不包括字符串的结束标志\0。
使用案例
/* strstr example */#include <stdio.h>#include <string.h>int main (){char str[] ="This is a simple string";char * pch;pch = strstr (str,"simple");//pch指向str中simple的sif (pch != NULL)strncpy (pch,"sample",6);puts (str);return 0;}
Output:
This is a sample string
strtok
注:此函数比较难用,要想掌握此函数,必须多次使用而且掌握函数的实现原理。
函数声明
char * strtok ( char * str, const char * sep );
函数功能
扫描一个字符串str,若在str中遇到字符串sep的任意子串,则把str中对应的子串
的第一位替换为\0,并返回扫描开始位置的地址。
函数实现原理
这个函数中有一个静态全局变量(假设为p),p是保存扫描的开始地址,如果str为字符串首地址,那么p就会被赋值为str,如果str为空,p的值为上次调用完成后p保存的值。从p位置开始向后扫描,如果遇到sep的子串,函数会把str中对应的子串的第一位置为\0,p保存这个str中这个子串的后一位地址,并返回扫描开始的地址,如果一直扫描到|\0也没遇到sep的子串,那返回值为NULL。stroke函数会改变str字符串的内容,若不想改变,则需新建立一个内容相同的字符串。
使用案例
/* strtok example */#include <stdio.h>#include <string.h>int main (){char str[] ="- This, a sample string.";char * pch;printf ("Splitting string \"%s\" into tokens:\n",str);pch = strtok (str," ,.-");while (pch != NULL){printf ("%s\n",pch);pch = strtok (NULL, " ,.-");}return 0;}
Output:
Splitting string “- This, a sample string.” into tokens:
This
a
sample
string
字符分类函数(包含在头文件ctype.h)
这些函数的声明的格式都是:int 函数名(int c)
参数c可以是要判断的字符c,也可以是c的ASCII码。
内存函数(包含在头文件string.h)
memcpy
函数声明
void * memcpy ( void * destination, const void * source, size_t num );
函数功能
将num字节的值从source指向的位置直接复制到destination指向的内存块。source指针和destination指针所指向的对象的基础类型与此函数无关;结果是数据的二进制拷贝。该函数不检查源文件中是否有任何终止空字符——它总是精确地复制num字节。为了避免溢出,destination和source参数所指向的数组的大小至少应该是num字节,并且不应该重叠(对于重叠的内存块,memmove是一个更安全的方法)。函数返回的是void*类型的destination保存的值,若需要使用此函数的返回值则需要进行相应的类型转换。
使用案例
/* 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;}
Output:
person_copy: Pierre de Fermat, 46
memmove
函数声明
void * memmove ( void * destination, const void * source, size_t num );
函数功能
移动内存块将num字节的值从source所指向的位置复制到destination所指向的内存块。复制就像使用了中间缓冲区一样,允许destination和source重叠。source指针和destination指针所指向的对象的基础类型与此函数无关;结果是数据的二进制拷贝。该函数不检查源文件中是否有任何终止空字符——它总是精确地复制num字节。为了避免溢出,目标参数和源参数所指向的数组的大小应至少为num字节。
使用案例
/* memmove example */#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;}
Output:
memmove can be very very useful.
注:到这里认真的你应该会发现,memmove、strncpy和memcpy三个函数在对字符串的使用中具有差不多的作用
memcmp
函数声明
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
函数功能
比较两个内存块将ptri指向的内存块的第一个num个字节与ptr2指向的第一个num个字节进行比较;如果在两个内存块中不匹配的第一个字节在ptr1中的值比在ptr2中的值低(如果计算为unsigned char值)返回一个负整数,如果两个内存块的内容是相等的返回0,如果在两个内存块中不匹配的第一个字节在ptr1中的值大于ptr2中的值(如果计算为unsigned char值)返回一个正整数。注意,与strcmp不同,该函数在找到空字符后不会停止比较。
使用案例
/* 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;}
Output:
‘DWgaOtP12df0’ is greater than ‘DWGAOTP12DF0’.