首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

程序员编程艺术:第四章、现场编写类似strstr/strcpy/strpbrk的函数

  • 25-03-02 15:42
  • 4463
  • 11909
blog.csdn.net

               第四章、现场编写类似strstr/strcpy/strpbrk的函数   

 



前奏

    有网友向我反应,之前三章(http://t.cn/hgVPmH)的面试题目,是否有点太难了。诚如他所说,绝大部分公司的面试题不会像微软等公司的面试题目出的那么变态,或复杂。

    面试考察的是你对基础知识的掌握程度,及编程能力是否过硬的一种检测,所以,扎实基础知识,提高编程能力,比去看什么所谓的面经,或去背面试题目的答案强多了。

    很多中、小型公司自己的创造能力,包括人力,物力资源都有限,所以,他们的面试题目除了copy一些大公司的题库之外(当然,考察你对基础知识的掌握情况,是肯定不会放过的),还有一个途径就是让你在限定时间内(如十分钟),当场实现一些类似strcpy/strcat/strpbrk等库函数,这个主要看你对细节的把握,以及编程能力是否之扎实了。

    同时,本章里出现的代码(除了第4节的c标准库部分源码)都是个人限定在短时间内(正好,突出现场感)编写的,很多问题,难免有所考虑不周。所以,如果你发现本章任何一段代码有任何问题,恳请不吝指正。


第一节、字符串查找
1.1题目描述:
给定一个字符串A,要求在A中查找一个子串B。
如A="ABCDF",要你在A中查找子串B=“CD”。

分析:比较简单,相当于实现strstr库函数,主体代码如下:

  1. //在字符串中查找指定字符串的第一次出现,不能找到则返回-1
  2. int strstr(char *string, char *substring)
  3. {
  4. if (string == NULL || substring == NULL)
  5. return -1;
  6. int lenstr = strlen(string);
  7. int lensub = strlen(substring);
  8. if (lenstr < lensub)
  9. return -1;
  10. int len = lenstr - lensub;
  11. int i,j;
  12. for (i = 0; i <= len; i++) //复杂度为O(m*n)
  13. {
  14. for (j = 0; j < lensub; j++)
  15. {
  16. if (string[i+j] != substring[j])
  17. break;
  18. }
  19. if (j == lensub)
  20. return i + 1;
  21. }
  22. return -1;
  23. }

    读者反馈@xiaohui5319:楼主啊,对于你那个strstr的函数,我觉得有点小问题。我查了一下C标准库的源码,它给的声明是这样的,两个参数都有const。

char *

STRSTR (const char *haystack_start, const char *needle_start)

    而且标准库中没有调用strlen函数,因为假如你是标准库的设计者,strlen()函数还没设计出来,你怎么去计算两个字符串的长度?是不是只能通过指针移动来实现,我觉得这些都是微软要考察的地方。

    此外:还有int lenstr=strlen(string);这是不安全的?
    strlen函数的返回类型是size_t型,也就是无符号整型,假如我的数组长度很长(假如是用堆分配的,可以很大很大),长过2的31次方减1的话,会发生一处,你这lenstr就会变成负值了
    用size_t类型最保险。

    以后,本编程艺术系列中有任何问题,暂未来得及及时修正,请读者多加思考,多加辨明。

    上述程序已经实现了在字符串中查找第一个子串的功能,时间复杂度为O(n*m),也可以用KMP算法,复杂度为O(m+n)。为人打通思路,提高他人创造力,我想,这是狂想曲与其它的面试解答所不同的地方,也是我们写狂想曲系列文章的意义与价值之所在。

1.2、题目描述

在一个字符串中找到第一个只出现一次的字符。如输入abaccdeff,则输出b。 

代码则可以如下编写:

  1. //查找第一个只出现一次的字符,
  2. //copyright@ yansha
  3. //July、updated,2011.04.24.
  4. char FirstNotRepeatChar(char* pString)
  5. {
  6. if(!pString)
  7. return '\0';
  8. const int tableSize = 256;
  9. //有点要提醒各位注意,一般常数的空间消耗,如这里的256,我们也认为此空间复杂度为O(1)。
  10. int hashTable[tableSize] = {0}; //存入数组,并初始化为0
  11. char* pHashKey = pString;
  12. while(*(pHashKey) != '\0')
  13. hashTable[*(pHashKey++)]++;
  14. while(*pString != '\0')
  15. {
  16. if(hashTable[*pString] == 1)
  17. return *pString;
  18. pString++;
  19. }
  20. return '\0'; //没有找到满足条件的字符,退出
  21. }

代码二,bitmap:

  1. # include
  2. # include
  3. const int N = 26;
  4. int bit_map[N];
  5. void findNoRepeat(char *src)
  6. {
  7. int pos;
  8. char *str = src;
  9. int i ,len = strlen(src);
  10. //统计
  11. for(i = 0 ; i < len ;i ++)
  12. bit_map[str[i]-'a'] ++;
  13. //从字符串开始遍历 其bit_map==1 那么就是结果
  14. for(i = 0 ; i < len ; i ++)
  15. {
  16. if(bit_map[str[i]-'a'] == 1)
  17. {
  18. printf("%c",str[i]);
  19. return ;
  20. }
  21. }
  22. }
  23. int main()
  24. {
  25. char *src = "abaccdeff";
  26. findNoRepeat(src);
  27. printf("\n");
  28. return 0;
  29. }

第二节、字符串拷贝
题目描述:
要求实现库函数strcpy,
原型声明:extern char *strcpy(char *dest,char *src);
功能:把src所指由NULL结束的字符串复制到dest所指的数组中。  
说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。  
返回指向dest的指针。

    分析:如果编写一个标准strcpy函数的总分值为10,下面给出几个不同得分的答案:

  1. //得2分
  2. void strcpy( char *strDest, char *strSrc )
  3. {
  4. while( (*strDest++ = * strSrc++) != '\0' );
  5. }
  6. //得4分
  7. void strcpy( char *strDest, const char *strSrc )
  8. {
  9. //将源字符串加const,表明其为输入参数,加2分
  10. while( (*strDest++ = * strSrc++) != '\0' );
  11. }
  12. //得7分
  13. void strcpy(char *strDest, const char *strSrc)
  14. {
  15. //对源地址和目的地址加非0断言,加3分
  16. assert( (strDest != NULL) && (strSrc != NULL) );
  17. while( (*strDest++ = * strSrc++) != '\0' );
  18. }
  19. //得9分
  20. //为了实现链式操作,将目的地址返回,加2分!
  21. char * strcpy( char *strDest, const char *strSrc )
  22. {
  23. assert( (strDest != NULL) && (strSrc != NULL) );
  24. char *address = strDest;
  25. while( (*strDest++ = * strSrc++) != '\0' );
  26. return address;
  27. }
  28. //得10分,基本上所有的情况,都考虑到了
  29. //如果有考虑到源目所指区域有重叠的情况,加1分!
  30. char * strcpy( char *strDest, const char *strSrc )
  31. {
  32. if(strDest == strSrc) { return strDest; }
  33. assert( (strDest != NULL) && (strSrc != NULL) );
  34. char *address = strDest;
  35. while( (*strDest++ = * strSrc++) != '\0' );
  36. return address;
  37. }

第三节、小部分库函数的实现
    考察此类编写同库函数一样功能的函数经常见于大大小小的IT公司的面试题目中,以下是常见的字符串库函数的实现,希望,对你有所帮助,有任何问题,欢迎不吝指正:

  1. //@yansha:字串末尾要加结束符'\0',不然输出错位结果
  2. char *strncpy(char *strDes, const char *strSrc, unsigned int count)
  3. {
  4. assert(strDes != NULL && strSrc != NULL);
  5. char *address = strDes;
  6. while (count-- && *strSrc != '\0')
  7. *strDes++ = *strSrc++;
  8. *strDes = '\0';
  9. return address;
  10. }
  11. //查找字符串s中首次出现字符c的位置
  12. char *strchr(const char *str, int c)
  13. {
  14. assert(str != NULL);
  15. for (; *str != (char)c; ++ str)
  16. if (*str == '\0')
  17. return NULL;
  18. return str;
  19. }
  20. int strcmp(const char *s, const char *t)
  21. {
  22. assert(s != NULL && t != NULL);
  23. while (*s && *t && *s == *t)
  24. {
  25. ++ s;
  26. ++ t;
  27. }
  28. return (*s - *t);
  29. }
  30. char *strcat(char *strDes, const char *strSrc)
  31. {
  32. assert((strDes != NULL) && (strSrc != NULL));
  33. char *address = strDes;
  34. while (*strDes != '\0')
  35. ++ strDes;
  36. while ((*strDes ++ = *strSrc ++) != '\0')
  37. NULL;
  38. return address;
  39. }
  40. int strlen(const char *str)
  41. {
  42. assert(str != NULL);
  43. int len = 0;
  44. while (*str ++ != '\0')
  45. ++ len;
  46. return len;
  47. }
  48. //此函数,梦修改如下
  49. char *strdup_(char *strSrc)
  50. //将字符串拷贝到新的位置
  51. {
  52. if(strSrc!=NULL)
  53. {
  54. char *start=strSrc;
  55. int len=0;
  56. while(*strSrc++!='\0')
  57. len++;
  58. char *address=(char *)malloc(len+1);
  59. assert(address != NULL);
  60. while((*address++=*start++)!='\0');
  61. return address-(len+1);
  62. }
  63. return NULL;
  64. }
  65. //多谢laoyi19861011指正
  66. char *strstr(const char *strSrc, const char *str)
  67. {
  68. assert(strSrc != NULL && str != NULL);
  69. const char *s = strSrc;
  70. const char *t = str;
  71. for (; *strSrc != '\0'; ++ strSrc)
  72. {
  73. for (s = strSrc, t = str; *t != '\0' && *s == *t; ++s, ++t)
  74. NULL;
  75. if (*t == '\0')
  76. return (char *) strSrc;
  77. }
  78. return NULL;
  79. }
  80. char *strncat(char *strDes, const char *strSrc, unsigned int count)
  81. {
  82. assert((strDes != NULL) && (strSrc != NULL));
  83. char *address = strDes;
  84. while (*strDes != '\0')
  85. ++ strDes;
  86. while (count -- && *strSrc != '\0' )
  87. *strDes ++ = *strSrc ++;
  88. *strDes = '\0';
  89. return address;
  90. }
  91. int strncmp(const char *s, const char *t, unsigned int count)
  92. {
  93. assert((s != NULL) && (t != NULL));
  94. while (*s && *t && *s == *t && count --)
  95. {
  96. ++ s;
  97. ++ t;
  98. }
  99. return (*s - *t);
  100. }
  101. char *strpbrk(const char *strSrc, const char *str)
  102. {
  103. assert((strSrc != NULL) && (str != NULL));
  104. const char *s;
  105. while (*strSrc != '\0')
  106. {
  107. s = str;
  108. while (*s != '\0')
  109. {
  110. if (*strSrc == *s)
  111. return (char *) strSrc;
  112. ++ s;
  113. }
  114. ++ strSrc;
  115. }
  116. return NULL;
  117. }
  118. int strcspn(const char *strSrc, const char *str)
  119. {
  120. assert((strSrc != NULL) && (str != NULL));
  121. const char *s;
  122. const char *t = strSrc;
  123. while (*t != '\0')
  124. {
  125. s = str;
  126. while (*s != '\0')
  127. {
  128. if (*t == *s)
  129. return t - strSrc;
  130. ++ s;
  131. }
  132. ++ t;
  133. }
  134. return 0;
  135. }
  136. int strspn(const char *strSrc, const char *str)
  137. {
  138. assert((strSrc != NULL) && (str != NULL));
  139. const char *s;
  140. const char *t = strSrc;
  141. while (*t != '\0')
  142. {
  143. s = str;
  144. while (*s != '\0')
  145. {
  146. if (*t == *s)
  147. break;
  148. ++ s;
  149. }
  150. if (*s == '\0')
  151. return t - strSrc;
  152. ++ t;
  153. }
  154. return 0;
  155. }
  156. char *strrchr(const char *str, int c)
  157. {
  158. assert(str != NULL);
  159. const char *s = str;
  160. while (*s != '\0')
  161. ++ s;
  162. for (-- s; *s != (char) c; -- s)
  163. if (s == str)
  164. return NULL;
  165. return (char *) s;
  166. }
  167. char* strrev(char *str)
  168. {
  169. assert(str != NULL);
  170. char *s = str, *t = str, c;
  171. while (*t != '\0')
  172. ++ t;
  173. for (-- t; s < t; ++ s, -- t)
  174. {
  175. c = *s;
  176. *s = *t;
  177. *t = c;
  178. }
  179. return str;
  180. }
  181. char *strnset(char *str, int c, unsigned int count)
  182. {
  183. assert(str != NULL);
  184. char *s = str;
  185. for (; *s != '\0' && s - str < count; ++ s)
  186. *s = (char) c;
  187. return str;
  188. }
  189. char *strset(char *str, int c)
  190. {
  191. assert(str != NULL);
  192. char *s = str;
  193. for (; *s != '\0'; ++ s)
  194. *s = (char) c;
  195. return str;
  196. }
  197. //@heyaming
  198. //对原 strtok 的修改,根据MSDN,strToken可以为NULL.实际上第一次call strtok给定一字串,
  199. //再call strtok时可以输入NULL代表要接着处理给定字串。
  200. //所以需要用一 static 保存没有处理完的字串。同时也需要处理多个分隔符在一起的情况。
  201. char *strtok(char *strToken, const char *str)
  202. {
  203. assert(str != NULL);
  204. static char *last;
  205. if (strToken == NULL && (strToken = last) == NULL)
  206. return (NULL);
  207. char *s = strToken;
  208. const char *t = str;
  209. while (*s != '\0')
  210. {
  211. t = str;
  212. while (*t != '\0')
  213. {
  214. if (*s == *t)
  215. {
  216. last = s + 1;
  217. if (s - strToken == 0) {
  218. strToken = last;
  219. break;
  220. }
  221. *(strToken + (s - strToken)) = '\0';
  222. return strToken;
  223. }
  224. ++ t;
  225. }
  226. ++ s;
  227. }
  228. return NULL;
  229. }
  230. char *strupr(char *str)
  231. {
  232. assert(str != NULL);
  233. char *s = str;
  234. while (*s != '\0')
  235. {
  236. if (*s >= 'a' && *s <= 'z')
  237. *s -= 0x20;
  238. s ++;
  239. }
  240. return str;
  241. }
  242. char *strlwr(char *str)
  243. {
  244. assert(str != NULL);
  245. char *s = str;
  246. while (*s != '\0')
  247. {
  248. if (*s >= 'A' && *s <= 'Z')
  249. *s += 0x20;
  250. s ++;
  251. }
  252. return str;
  253. }
  254. void *memcpy(void *dest, const void *src, unsigned int count)
  255. {
  256. assert((dest != NULL) && (src != NULL));
  257. void *address = dest;
  258. while (count --)
  259. {
  260. *(char *) dest = *(char *) src;
  261. dest = (char *) dest + 1;
  262. src = (char *) src + 1;
  263. }
  264. return address;
  265. }
  266. void *memccpy(void *dest, const void *src, int c, unsigned int count)
  267. {
  268. assert((dest != NULL) && (src != NULL));
  269. while (count --)
  270. {
  271. *(char *) dest = *(char *) src;
  272. if (* (char *) src == (char) c)
  273. return ((char *)dest + 1);
  274. dest = (char *) dest + 1;
  275. src = (char *) src + 1;
  276. }
  277. return NULL;
  278. }
  279. void *memchr(const void *buf, int c, unsigned int count)
  280. {
  281. assert(buf != NULL);
  282. while (count --)
  283. {
  284. if (*(char *) buf == c)
  285. return (void *) buf;
  286. buf = (char *) buf + 1;
  287. }
  288. return NULL;
  289. }
  290. int memcmp(const void *s, const void *t, unsigned int count)
  291. {
  292. assert((s != NULL) && (t != NULL));
  293. while (*(char *) s && *(char *) t && *(char *) s == *(char *) t && count --)
  294. {
  295. s = (char *) s + 1;
  296. t = (char *) t + 1;
  297. }
  298. return (*(char *) s - *(char *) t);
  299. }
  300. //@big:
  301. //要处理src和dest有重叠的情况,不是从尾巴开始移动就没问题了。
  302. //一种情况是dest小于src有重叠,这个时候要从头开始移动,
  303. //另一种是dest大于src有重叠,这个时候要从尾开始移动。
  304. void *memmove(void *dest, const void *src, unsigned int count)
  305. {
  306. assert(dest != NULL && src != NULL);
  307. char* pdest = (char*) dest;
  308. char* psrc = (char*) src;
  309. //pdest在psrc后面,且两者距离小于count时,从尾部开始移动. 其他情况从头部开始移动
  310. if (pdest > psrc && pdest - psrc < count)
  311. {
  312. while (count--)
  313. {
  314. *(pdest + count) = *(psrc + count);
  315. }
  316. } else
  317. {
  318. while (count--)
  319. {
  320. *pdest++ = *psrc++;
  321. }
  322. }
  323. return dest;
  324. }
  325. void *memset(void *str, int c, unsigned int count)
  326. {
  327. assert(str != NULL);
  328. void *s = str;
  329. while (count --)
  330. {
  331. *(char *) s = (char) c;
  332. s = (char *) s + 1;
  333. }
  334. return str;
  335. }
 
   测试:以上所有的函数,都待进一步测试,有任何问题,欢迎任何人随时不吝指出。

第四节、c标准库部分源代码

    为了给各位一个可靠的参考,以下,我摘取一些c标准框里的源代码,以飨各位:

  1. char * __cdecl strcat (char * dst,const char * src)
  2. {
  3. char * cp = dst;
  4. while( *cp )
  5. cp++; /* find end of dst */
  6. while( *cp++ = *src++ ) ; /* Copy src to end of dst */
  7. return( dst ); /* return dst */
  8. }
  9. int __cdecl strcmp (const char * src,const char * dst)
  10. {
  11. int ret = 0 ;
  12. while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst)
  13. ++src, ++dst;
  14. if ( ret < 0 )
  15. ret = -1 ;
  16. else if ( ret > 0 )
  17. ret = 1 ;
  18. return( ret );
  19. }
  20. size_t __cdecl strlen (const char * str)
  21. {
  22. const char *eos = str;
  23. while( *eos++ ) ;
  24. return( (int)(eos - str - 1) );
  25. }
  26. char * __cdecl strncat (char * front,const char * back,size_t count)
  27. {
  28. char *start = front;
  29. while (*front++)
  30. ;
  31. front--;
  32. while (count--)
  33. if (!(*front++ = *back++))
  34. return(start);
  35. *front = '\0';
  36. return(start);
  37. }
  38. int __cdecl strncmp (const char * first,const char * last,size_t count)
  39. {
  40. if (!count)
  41. return(0);
  42. while (--count && *first && *first == *last)
  43. {
  44. first++;
  45. last++;
  46. }
  47. return( *(unsigned char *)first - *(unsigned char *)last );
  48. }
  49. /* Copy SRC to DEST. */
  50. char *
  51. strcpy (dest, src)
  52. char *dest;
  53. const char *src;
  54. {
  55. reg_char c;
  56. char *__unbounded s = (char *__unbounded) CHECK_BOUNDS_LOW (src);
  57. const ptrdiff_t off = CHECK_BOUNDS_LOW (dest) - s - 1;
  58. size_t n;
  59. do
  60. {
  61. c = *s++;
  62. s[off] = c;
  63. }
  64. while (c != '\0');
  65. n = s - src;
  66. (void) CHECK_BOUNDS_HIGH (src + n);
  67. (void) CHECK_BOUNDS_HIGH (dest + n);
  68. return dest;
  69. }
  70. char * __cdecl strncpy (char * dest,const char * source,size_t count)
  71. {
  72. char *start = dest;
  73. while (count && (*dest++ = *source++)) /* copy string */
  74. count--;
  75. if (count) /* pad out with zeroes */
  76. while (--count)
  77. *dest++ = '\0';
  78. return(start);
  79. }

有关编程艺术的修订

    程序员编程艺术系列的Github地址已于今天建立:https://github.com/nateriver520/The-Art-Of-Programming-By-July,我们急切的想得到读者的反馈,意见,建议,以及更好的思路,算法,和代码优化的建议。所以,如果你发现了本编程艺术系列中的任何一题,任何一章中的错误,问题,与漏洞,欢迎随时fork到本github中,thanks。


版权所有,本人对本blog内所有任何内容享有版权及著作权。实要转载,请以链接形式注明出处。

注:本文转载自blog.csdn.net的v_JULY_v的文章"http://blog.csdn.net/v_JULY_v/archive/2011/05/13/6417600.aspx"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top