一、串的介绍
串型结构就是由若干个类型相同的元素组成的数据结构,末尾有一个结束标志,字符串就是由字符元素组成的串型结构,'\0'是它的结束标志,使用它可以存储单词、文章、汉字等文字信息。
随着计算机和编程语言的发展,字符串在程序中使用的越来越多,字符串就简称串,对它的操作都是对串里面所有字符整体操作,以'\0'为结束标志,如果字符串的末尾没有'\0',可能会产生乱码、段错误、脏数据等问题。
字符串结构应该具备的功能、算法有:
创建:定义字符串
销毁:释放字符串
清空:删除所有的字符
复制:就是strcpy函数
连接:就是strcat函数
比较:就是strcmp函数
长度:就是strlen函数
查询字串:就是strstr函数
字符串的表示与实现
字符串一般有两实现方式,这两种方式都是使用顺序表,只是内存不同而已:
1、用栈内存存储字符,定长方式,字符的数量一旦超出表的范围,为了防止内存越界要对字符串进行截取,了解一下即可。
2、使用堆内存破碎字符,在操作字符串时,自动扩展堆内存。
#include <stdio.h> #include <stdbool.h> #include <stdlib.h> #include <string.h> typedef struct String { char* ch; size_t size; }String; // 创建字符串 String* create_string(void) { String* str = malloc(sizeof(String)); str->ch = NULL; str->size = 0; return str; } // 计算长度 size_t len_string(String* str) { return strlen(str->ch); } // 复制 void copy_string(String* str,const char* ch) { size_t size = strlen(ch)+1; if(size > str->size) { str->ch = realloc(str->ch,size); str->size = size; } strcpy(str->ch,ch); } // 构建字符串 String* assign_string(const char* ch) { String* str = create_string(); copy_string(str,ch); return str; } // 判断是否是空串 bool empty_string(String* str) { return NULL == str->ch; } // 赋值 void sav_string(String* str1,String* str2) { if(str1->size < str2->size) { str1->ch = realloc(str1->ch,str2->size); str1->size = str2->size; } strcpy(str1->ch,str2->ch); } // 比较 int cmp_string(String* str1,String* str2) { return strcmp(str1->ch,str2->ch); } // 连接 void cat_string(String* str1,String* str2) { size_t size = strlen(str1->ch)+strlen(str2->ch)+1; if(size >= str1->size) { str1->ch = realloc(str1->ch,size); str1->size = size; } strcat(str1->ch,str2->ch); } // 清空字符串 void clear_string(String* str) { free(str->ch); str->size = 0; } // 销毁字符串 void destroy_string(String* str) { free(str->ch); free(str); } int main(int argc,const char* argv[]) { /* String* str1 = assign_string("hehe"); String* str2 = create_string(); // 浅拷贝,两个对象的ch指向了同一个字符串,当其它对象被销毁,另一个会受影响 *str2 = *str1; // str2->ch = str1->ch; str2->len = str1->len; destroy_string(str1); puts(str2->ch); // 深拷贝,如果结构体中有成员是指针类型,且指向了堆内,这种结构变量不能直接赋值(浅拷贝),为了不出问题,我们需要实现深拷贝 sav_string(str2,str1); destroy_string(str1); puts(str2->ch); */ String* str1 = assign_string("hehe"); String* str2 = assign_string("xixi12rfaspoikjrqw;elifkj;lasejkrf;oawlikeujf;olaeirjtfasldkjf;qlwiejfa;sldkjf;qwlsekjfa;sldkfj;l"); cat_string(str1,str2); puts(str1->ch); return 0; }
封装字符串的意义
1、字符串被封装成数据结构后,使用者不需要关心字符串的空间问题,但这种封装在C语言下没有太大意义,因为C语言语法的原因会使用字符串操作更麻烦。
2、我们在C++语言中重写该数据结构,因为C++的语法会让该数据结构使用起来更方便。
子串查询的算法
假定有两个字符串str1,str2,查询子串就是在字符串str1中查询是否存在str2,如果存在则返回str2首次出现的位置,这个操作叫子串查询。
char *str_str(const char *str1, const char *str2) { assert(NULL != str1 && NULL != str2); for (int i = 0, j; '\0' != str1[i]; i++) { for (j = 0; '\0' != str2[j] && str2[j] == str1[i + j]; j++) ; if ('\0' == str2[j]) return (char *)str1 + i; } return NULL; } char *str_str(const char *str1, const char *str2) { assert(NULL != str1 && NULL != str2); int i = 0, j = 0; while ('\0' != str1[i] && '\0' != str2[j]) { if (str1[i] == str2[j]) { i++; j++; } else { i = i - j + 1; j = 0; } } return '\0' == str2[j] ? (char *)str1 + i - j : NULL; } char* str_str(const char* str1,const char* str2) { assert(NULL != str1 && NULL != str2); int sum1 = 0 , sum2 = 0 , len = 0; while('\0'!=str2[len]&&'\0'!=str1[len]) { sum1 += str1[len]; sum2 += str2[len++]; } for(int i=len; '\0' != str1[i]; i++) { printf("%d %d\n",sum1,sum2); if(sum1 == sum2 && 0 == strncmp(str1+i-len,str2,len)) { return (char*)str1+i-len; } else { sum1 -= str1[i-len]; sum1 += str1[i]; } } return NULL; }