走进C标准库(8)——"string.h"中函数的实现相关字符串操作函数
我的strcat:
- char *strcat(char *dest,char *src)
- {
- char * reval = dest;
- while(*dest)
- dest++;
- while(*src)
- *dest++ = *src++ ;
- *dest = *src;
- return reval;
- }
MSVC:
- char * __cdecl strcat (
- char * dst,
- const char * src
- )
- {
- char * cp = dst;
- while( *cp )
- cp++; /* find end of dst */
- while( *cp++ = *src++ ) ; /* Copy src to end of dst */
- return( dst ); /* return dst */
- }
在while( *cp++ = *src++ )中,条件的值为赋值语句的返回值即*cp被赋的值,也就是此时*src的值。则,当*src为0时,将其赋给*cp后完成赋值。非常简洁。
该函数的前提条件:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
我的strncat:
- char *strncat(char *dest,char *src,int n)
- {
- char * reval = dest;
- while(*dest)
- dest++;
- while(n-- && (*dest++ = *src++));
- if(n < )
- *dest = ;
- return reval;
- }
MSVC:
- char * __cdecl strncat (
- char * front,
- const char * back,
- size_t count
- )
- {
- char *start = front;
- while (*front++)
- ;
- front--;
- while (count--)
- if (!(*front++ = *back++))
- return(start);
- *front = '\0';
- return(start);
- }
我的strchr:
- char *strchr(const char *s,char c){
- while(*s)
- {
- if(*s == c)
- return s;
- s++;
- }
- return NULL;
- }
MSVC:
- char * __cdecl strchr (
- const char * string,
- int ch
- )
- {
- while (*string && *string != (char)ch)
- string++;
- if (*string == (char)ch)
- return((char *)string);
- return(NULL);
- }
我的strcmp:
- int strcmp(const char *s1,const char * s2){
- while(*s1 == *s2 && *s1)
- {
- ++s1;
- ++s2;
- }
- return *(unsigned char *)s1 - *(unsigned char *)s2;
- }
MSVC:
- int __cdecl strcmp (
- const char * src,
- const char * dst
- )
- {
- int ret = ;
- while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst)
- ++src, ++dst;
- if ( ret < )
- ret = - ;
- else if ( ret > )
- ret = ;
- return( ret );
- }
strcmp返回值不必为1和-1的。使用unsigned char 因为有符号数可能会导致比较大小错误。
我的strcpy:
- char *strcpy(char *dest,const char *src){
- char * reval = dest;
- while(*dest++ = *src++);
- return reval;
- }
MSVC:
- char * __cdecl strcpy(char * dst, const char * src)
- {
- char * cp = dst;
- while( *cp++ = *src++ )
- ; /* Copy src over dst */
- return( dst );
- }
我的strncpy:
- char *strncpy(char *dest, const char *src, int n){
- char * reval = dest;
- while(n--){
- if(*src)
- *dest++ = *src++;
- else
- *dest++ = ;
- }
- return reval;
- }
MSVC:
- char * __cdecl strncpy (
- char * dest,
- const char * source,
- size_t count
- )
- {
- char *start = dest;
- while (count && (*dest++ = *source++)) /* copy string */
- count--;
- if (count) /* pad out with zeroes */
- while (--count)
- *dest++ = '\0';
- return(start);
- }
我的strcspn:
- int strcspn(const char *s1,const char *s2){
- const char *cp;
- int reval = ;
- for(; *s1; s1++){
- for(cp = s2; *cp; cp++){
- if(*s1 == *cp)
- return reval;
- }
- ++reval;
- }
- return reval;
- }
MSVC:
- size_t __cdecl strcspn (
- const char * string,
- const char * control
- )
- {
- const unsigned char *str = string;
- const unsigned char *ctrl = control;
- unsigned char map[];
- int count;
- /* Clear out bit map */
- for (count=; count<; count++)
- map[count] = ;
- /* Set bits in control map */
- while (*ctrl)
- {
- map[*ctrl >> ] |= ( << (*ctrl & ));
- ctrl++;
- }
- /* 1st char in control map stops search */
- count=;
- map[] |= ; /* null chars not considered */
- while (!(map[*str >> ] & ( << (*str & ))))
- {
- count++;
- str++;
- }
- return(count);
- }
函数说明:strcspn()从参数s 字符串的开头计算连续的字符, 而这些字符都完全不在参数reject 所指的字符串中. 简单地说, 若strcspn()返回的数值为n, 则代表字符串s 开头连续有n 个字符都不含字符串reject 内的字符。
返回值:返回字符串s 开头连续不含字符串reject 内的字符数目。
我的实现和《C标准库》书中基本相同,不需要任何的额外存储空间,但是使用两层的循环,花费较多的时间。
该函数的实质其实是判断s1中的每一个字符,是否在s2字符串中,对于这种判断是否在其中的问题,经常会采用映射表的方式来缩减查找时间,典型实现就是布隆过滤器。
此处,MSVC的实现就是采用了映射表的方式。
因为ASCII码有256个,所以需要256bit来作为映射标记。一个字节是8bit,所以需要32个字节。所以在代码中有 unsigned char map[32]的定义。
那么,我们就需要将8bit,分别映射出一个map的下标和bit中的位位置。
map的下表需要使用5bit(32),而bit中的位位置使用剩余的3bit(8)来映射。
通过*ctrl >> 3取到高5位到0-31的映射,通过1 << (*ctrl & 7)取得到1字节中某一位的标记。
完成控制字符的映射表建立,就能用o(1)的时间完成某个字符的查找了。
strerror
功 能: 返回指向错误信息字符串的指针
例如:
- #include <stdio.h>
- #include <errno.h>
- int main(void)
- {
- char *buffer;
- buffer = strerror(errno);
- printf("Error: %s\n", buffer);
- return ;
- }
此段代码strerror指向的内存中的字符串为No Error
我的strlen:
- int strlen(const char *s){
- const char *cp = s;
- while(*cp){
- cp++;
- }
- return (cp - s);
- }
MSVC:
- size_t __cdecl strlen (
- const char * str
- )
- {
- const char *eos = str;
- while( *eos++ ) ;
- return( (int)(eos - str - ) );
- }
返回值应该不需要强制类型转换,因为指针相减返回值是int。当然,加上显式转换则更加明确。
ptrdiff_t
This is the type returned by the subtraction operation between two pointers. This is a signed integral type, and as such can be casted to compatible fundamental data types.
strpbrk和strspn的实现和strcspn相同
我的strrchr:
- char *strrchr(const char *str, const char c){
- const char *cp = str;
- if(*str == )
- return NULL;
- while(*str)
- str++;
- while(*cp++){
- if(*--str == c)
- return str;
- }
- return NULL;
- }
MSVC:
- char * __cdecl strrchr (
- const char * string,
- int ch
- )
- {
- char *start = (char *)string;
- while (*string++) /* find end of string */
- ;
- /* search towards front */
- while (--string != start && *string != (char)ch)
- ;
- if (*string == (char)ch) /* char found ? */
- return( (char *)string );
- return(NULL);
- }
确实只需要初始位置的拷贝,不需要用拷贝来计数。
strtok,没想好如何实现比较合适。
MSVC:
- char * __cdecl strtok (
- char * string,
- const char * control
- )
- {
- unsigned char *str;
- const unsigned char *ctrl = control;
- unsigned char map[];
- int count;
- static char *nextoken;
- /* Clear control map */
- for (count = ; count < ; count++)
- map[count] = ;
- /* Set bits in delimiter table */
- do {
- map[*ctrl >> ] |= ( << (*ctrl & ));
- } while (*ctrl++);
- /* Initialize str. If string is NULL, set str to the saved
- * pointer (i.e., continue breaking tokens out of the string
- * from the last strtok call) */
- if (string)
- str = string;
- else
- str = nextoken;
- /* Find beginning of token (skip over leading delimiters). Note that
- * there is no token iff this loop sets str to point to the terminal
- * null (*str == '\0') */
- while ( (map[*str >> ] & ( << (*str & ))) && *str )
- str++;
- string = str;
- /* Find the end of the token. If it is not the end of the string,
- * put a null there. */
- for ( ; *str ; str++ )
- if ( map[*str >> ] & ( << (*str & )) ) {
- *str++ = '\0';
- break;
- }
- /* Update nextoken (or the corresponding field in the per-thread data
- * structure */
- nextoken = str;
- /* Determine if a token has been found. */
- if ( string == str )
- return NULL;
- else
- return string;
- }
用一个static变量来记录当前分割到的位置,它是线程不安全的,多次调用也会使它失效。
strcoll使用当前的区域设置来比较字符串,strxfrm使用当前的区域设置来转换字符串。当前区域设置由LL_COLLATE宏指定。它们均调用带有区域设置参数的内部版本strcoll_l和strxfrm_l来完成实际的工作。
完
走进C标准库(8)——"string.h"中函数的实现相关字符串操作函数的更多相关文章
- 走进C标准库(6)——"string.h"中函数的实现memchr
我写的memchr: void *memchr(const void *buf, char ch, unsigned count){ unsigned ; while(*(buf++) != ch & ...
- 走进C标准库(7)——"string.h"中函数的实现memcmp,memcpy,memmove,memset
我的memcmp: int memcmp(void *buf1, void *buf2, unsigned int count){ int reval; while(count && ...
- 走进C标准库(3)——"stdio.h"中的getc和ungetc
接前文. 再来看看getc和ungetc的实现.在看这两个函数的实现之前,我们先来想一想这两个函数分别需要做的工作. int getc(FILE *stream) 说明:函数getc从stream指向 ...
- 走进C标准库(2)——"stdio.h"中的fopen函数
其他的库文件看起来没有什么实现层面的知识可以探究的,所以,直接来看stdio.h. 1.茶余饭后的杂谈,有趣的历史 在过去的几十年中,独立于设备的输入输出模型得到了飞速的发展,标准C从这个改善的模型中 ...
- 走进C标准库(5)——"stdio.h"中的其他部分函数
函数介绍来自:http://ganquan.info/standard-c/ 函数名: freopen 功 能: 替换一个流 用 法: FILE *freopen(char *filename, ...
- 走进C标准库(4)——"stdio.h"中的putc
花了点时间把园子弄得好看了点,现在继续. 函数名: putc 功 能: 输出一字符到指定流中 用 法: int putc(int ch, FILE *stream); #define _putc_ ...
- 走进C标准库(1)——assert.h,ctype.h
默默觉得原来的阅读笔记的名字太土了,改了个名字,叫做走进C标准库. 自己就是菜鸟一只,第一次具体看C标准库,文章参杂了对<the standard C library>的阅读和对源码的一些 ...
- C++ 标准库类型-String,Vector and Bitset
<C++ Primer 4th>读书摘要 最重要的标准库类型是 string 和 vector,它们分别定义了大小可变的字符串和集合.这些标准库类型是语言组成部分中更基本的那些数据类型(如 ...
- 谈谈两种标准库类型---string和vector
两种最重要的标准库---string和vector string和vector是两种最重要的标准库类型,string表示可变长的字符序列,vector存放的是某种给定类型对象的可变长序列. 一.标准库 ...
随机推荐
- 关于ECharts Java类库的一个jquery插件
在项目中开发图表功能时用到了Echars和一个关于Echars的java类库(http://git.oschina.net/free/ECharts).这个类库主要目的是方便在Java中构造EChar ...
- css样式规则的简要总结
css与文档关联起来发挥作用. css文件中是各种样式规则,由选择器和声名块构成.声明块由多条声明组成.选择器是声明要作用的对象,声明是对具体规则的描述. 声明由属性和值组成,值或是属性的错误都会使该 ...
- Java环境配置原理
Java环境配置原理详解 1.Jdk安装目录文件说明: 一般jdk安装目录及路径 \Java\jdk1.7.0_79\lib,里面主要包含以下文件夹. bin:主要存放的是java工具中常用命令如:j ...
- pythonj基础之 多线程
多线程类似于同时执行多个不同程序,多线程运行有如下优点: 使用线程可以把占据长时间的程序中的任务放到后台去处理. 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进 ...
- Android 开源控件系列_1
第一部分 个性化控件(View) 主要介绍那些不错个性化的View,包括ListView.ActionBar.Menu.ViewPager.Gallery.GridView.ImageView.Pro ...
- android AsyncTask 的使用(转载)
1 ) AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可 ...
- Looper、Hander、HandlerThread
一.Message .Looper.Handler之间的关系 1.系统发送的Message消息传送给Handler,Handler将Message放入自己的looper队列的底部 然后再从Loop ...
- Linux06--Shell程序设计02 数据流重定向与管道
包含3种数据流: •标准输入(stdin):代码为0,符号为<或<<; •标准输出(stdout):代码为1,符号为>或>>; •标准错误输出(stderr):代码 ...
- 转:JavaScript函数式编程(一)
转:JavaScript函数式编程(一) 一.引言 说到函数式编程,大家可能第一印象都是学院派的那些晦涩难懂的代码,充满了一大堆抽象的不知所云的符号,似乎只有大学里的计算机教授才会使用这些东西.在曾经 ...
- GDB单步调试程序
linux下gdb单步调试 用 GDB 调试程序 GDB 概述———— GDB 是 GNU开源组织发布的一个强大的 UNIX下的程序调试工具.或许,各位比较喜欢那种图形界面方式的,像 VC. BCB等 ...