C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在 常量字符串 中或者 字符数组 中。下面介绍C语言中的一些常用的字符串函数和内存函数的功能以及实现原理。
size_t strlen ( const char * str );
注:size_t类型其实就是unsigned long long类型
返回str指向的字符串长度,从str指向的字符算起,一直到字符串末尾的\0结束,如果没有\0,函数将继续增加str长度知道遇到\0为止。
/* strlen example */
#include
#include
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.
char* strcpy(char * destination, const char * source );
把字符串source拷贝到字符串destination中,从source指向的位置开始拷贝直到遇到\0,并把\0拷贝到destination字符串中。使用此函数要注意为字符串destination分配的空间要大于为source字符串分配的空间,否则会越界访问报错。
源字符串必须以 ‘\0’ 结束。
会将源字符串中的 ‘\0’ 拷贝到目标空间。
目标空间必须足够大,以确保能存放源字符串。
目标空间必须可变。
/* strcpy example */
#include
#include
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
char * strcat ( char * destination, const char * source );
把source字符串接到destination字符串后,destination字符串末尾的\0会被覆盖,source字符串包括其末尾的\0都会被拷贝到destination字符串末尾。
源字符串必须以 ‘\0’ 结束。
目标空间必须有足够的大,能容纳下源字符串的内容。
目标空间必须可修改。
/* strcat example */
#include
#include
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.
int strcmp ( const char * str1, const char * str2 );
函数从两字符串开头开始,按照每个字符的ASCII码大小向后一一比较每个字符,直到遇到不相等的字符或是遇到\0结束。如果遇到不相等的字符时str1中的字符ASCII码大于str2中的,函数返回一个正整数,如果小于,函数返回一个负整数。如果两字符串中的每个字符都相同,函数返回0。
//猜水果游戏
#include
#include
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;
}
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
#include
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
char * strncat ( char * destination, const char * source, size_t num );
把source字符串的前num个字节接到destination字符串后,destination字符串末尾的\0会被覆盖,source字符串接好后在最末尾加上\0,如果source中的C字符串长度小于num,则只复制到空字符结束之前的内容。
/* strncat example */
#include
#include
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
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
#include
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
char * strstr ( const char *str1, const char * str2);
在str1字符串中匹配查找字符串str2,如果str2是str1的子串则函数返回str2第一次出现在str1中的地址,如果str2不是str1的子串则返回NULL。在匹配过程中不包括字符串的结束标志\0。
/* strstr example */
#include
#include
int main ()
{
char str[] ="This is a simple string";
char * pch;
pch = strstr (str,"simple");
//pch指向str中simple的s
if (pch != NULL)
strncpy (pch,"sample",6);
puts (str);
return 0;
}
Output:
This is a sample string
注:此函数比较难用,要想掌握此函数,必须多次使用而且掌握函数的实现原理。
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
#include
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
这些函数的声明的格式都是:int 函数名(int c)
参数c可以是要判断的字符c,也可以是c的ASCII码。
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
#include
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
void * memmove ( void * destination, const void * source, size_t num );
移动内存块将num字节的值从source所指向的位置复制到destination所指向的内存块。复制就像使用了中间缓冲区一样,允许destination和source重叠。source指针和destination指针所指向的对象的基础类型与此函数无关;结果是数据的二进制拷贝。该函数不检查源文件中是否有任何终止空字符——它总是精确地复制num字节。为了避免溢出,目标参数和源参数所指向的数组的大小应至少为num字节。
/* memmove example */
#include
#include
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三个函数在对字符串的使用中具有差不多的作用
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
#include
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’.