《C和指针》第九章讲到了:字符串、字符和字节。学习之后,现把这一章的一些知识点提炼、总结。
===========================================
回顾一下字符串基础知识:字符串就是由零个或多个字符构成,结尾以’\0’作为终止标志的一种数据类型。
但是在我们的C语言中,没有像int、float这样,有一个显式的数据类型。因为字符串以一个字符串常量形式表示,或者存储在一个字符数组中。
存储方式除了字符串常量是放在文字常量区(.rodata)外,其他的可存在字符数组或是动态分配的内存中。
8.12日补充:
关于内存重叠的学习:http://blog.csdn.net/feitianxuxue/article/details/7195158
如下两句代码:
char *str = "hello,wrold"; /*"hello,wrold"内容不可修改因为在文字常量区*/
char arr[] = "nihao,xiexie";/*"nihao,xiexie"的内容可以修改因为是在栈中分配了空间*/
例(摘自《C和指针》):
/*
**用动态分配内存制作一个字符串的一份拷贝。
*/
char *strdup(char const *string)
{
char *new_string;
/*请求足够长度的内存,用于存储字符串和'\0'*/
new_string = malloc(strlen(string) + 1);
/*如果得到内存,就复制字符串*/
if(new_string != NULL)
strcpy(new_string, string);
return new_string;
}
一、字符串长度
字符串的长度就是它包含的字符个数。
库函数strlen原形如下:
size_t strlen(const char *string);
在Linux(3.0内核)中string.c中实现如下:
size_t strlen(const char *s)
{
const char *sc;
for (sc = s; *sc != '\0'; ++sc)
/* nothing */;
return sc - s;
}
值得注意的是,它返回的是size_t类型,size_t是一个与机器相关的unsigned类型,其大小足以保证存储内存中对象的大小。
二、字符串函数
1.复制字符串strcpy:
用于复制字符串的函数是strcpy,原型:
char *strcpy(char *dest, const char *src)
在Linux(3.0内核)中string.c中实现如下:
char *strcpy(char *dest, const char *src)
{
char *tmp = dest;
while ((*dest++ = *src++) != '\0')
/* nothing */;
return tmp;
}
(1)功能是把参数src字符串复制到dest参数。dest里的内容将会被新串覆盖并且丢失。
(2)这个会有可能src和dest在内存中出现重叠。
(3)dest是要被修改的,所以它必须是个字符数组或者是一个指向动态分配内存的数组指针,不能是字符串常量。
(4)返回值是第一个参数的一份拷贝,是指向目标字符数组的指针。这个函数就可以嵌套的调用,也就是说可以作为别的函数的参数来使用。
2.复制字符串strncpy:
strncpy原型:
char *strncpy(char *dest, const char *src, size_t count)
在Linux(3.0内核)中string.c中实现如下:
char *strncpy(char *dest, const char *src, size_t count)
{
char *tmp = dest;
while (count) {
if ((*tmp = *src) != 0)
src++;
tmp++;
count--;
}
return dest;
}
较之strcpy,这个函数接受一个显示的参数count,用于限定复制的字符数,这是更安全的机制。
(1)同样是把src字符串复制到dest,但是它规定了向dest写入count个字符。
(2)如果源src的字符个数小于给定的count,那么在dest里会用额外的’\0’填充到count。
(3)如果源src的值大于count,那么只会有count个字符被复制到dest中。
它的结果将不会以’\0’结尾。
3.连接字符串strcat:
原型:
char *strcat(char *dest, const char *src)
在Linux(3.0内核)中string.c中实现如下:
char *strcat(char *dest, const char *src)
{
char *tmp = dest;
while (*dest)
dest++;
while ((*dest++ = *src++) != '\0')
;
return tmp;
}
(1)功能是把一个字符串连接到另一个字符串的后面。
(2)要注意dest的剩余空间要足够保存整个的src字符串。
4.连接字符串strncat:
原型:
char *strncat(char *dest, const char *src, size_t count)
在Linux(3.0内核)中string.c中实现如下:
char *strncat(char *dest, const char *src, size_t count)
{
char *tmp = dest;
if (count) {
while (*dest)
dest++;
while ((*dest++ = *src++) != 0) {
if (--count == 0) {
*dest = '\0';
break;
}
}
}
return tmp;
}
(1)从src中最多复制count个字符到dest后面,并且总在新生成的字符串后面添加一个’\0’。
5.字符串查找strstr
原型:
char *strstr(const char *s1, const char *s2)
在Linux(3.0内核)中string.c中实现如下:
char *strstr(const char *s1, const char *s2)
{
size_t l1, l2;
l2 = strlen(s2);
if (!l2)
return (char *)s1;
l1 = strlen(s1);
while (l1 >= l2) {
l1--;
if (!memcmp(s1, s2, l2))
return (char *)s1;
s1++;
}
return NULL;
}
(1)在某一个字符串中查找一个字串,该函数在s1查找整个s2第一次出现的位置,返回一个指向该位置的指针。
(2)若s2没有完整出现在s1,则返回一个NULL指针。
(3)若第二个参数是空字符串,函数就返回s1。
strstr的另一种实现方法:
char *my_strstr(const char *find, const char *need)
{
assert(find !=NULL && need !=NULL);
while( *find !='\0')
{
const char *p = find;
const char *q = need;
const char *res;
if(*p == *q)
{
res= p;
while(*p++ == *q++)
;
if(*q == '\0')
return (char *)res;
}
find ++;
}
return NULL;
}
二、内存操作函数
内存操作函数可以处理任意的字节序列。
memcpy:
void *memcpy(void *dest, const void *src, size_t count)
{
char *tmp = dest;
const char *s = src;
while (count--)
*tmp++ = *s++;
return dest;
}
该函数的功能是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。
可以看到参数类型是void* 指针,而任何类型的指针都可以转换为void* 类型。所以前两个参数在使用时不需要强制类型转换。
memmove:
void *memmove(void *dest, const void *src, size_t count)
{
char *tmp;
const char *s;
if (dest <= src) {
tmp = dest;
s = src;
while (count--)
*tmp++ = *s++;
} else {
tmp = dest;
tmp += count;
s = src;
s += count;
while (count--)
*--tmp = *--s;
}
return dest;
}
memmove的行为和memcpy差不多,但是它的src和dest可以重叠。
具体区别可以查看这一篇文章:http://www.linuxidc.com/Linux/2013-06/85344.htm
memset:
void *memset(void *s, int c, size_t count)
{
char *xs = s;
while (count--)
*xs++ = c;
return s;
}
memset是把从s开始的count个字节都设置成c。
memset(buffer, 0, SIZE)
把buffer的前SIZE个字节初始化为0。