读经典之TCPL练习五



1.实现库函数 strncpy、strncat 和 strncmp,它们最多对参数字符串中的前 n 个字符进行操作。例如,函数 strncpy(s, t, n)将 t 中最多前 n 个字符复制到 s中。


strncpy函数


我自己写的:

	void strncpy(char *s,const char *t,int n) {
		char *tmp;
		tmp=t;

		for(;t<(tmp+n);s++,t++)
			*s=*t;
	}

网上看到的:
char *my_strncpy(char *dest, const char *src, int count)  
{  
    char *tmp = dest;  
    while(count) {  
        if ((*tmp = *src) != 0)  
            src++;  
        tmp++;  
        count--;  
    }  
    return dest;  
}  

书上看到的答案:

char *liw_strncpy(char *s, const char *ct, size_t n) {
char *p;
p = s;
for (; n > 0 && *ct != '\0'; --n)
*p++ = *ct++;
for (; n > 0; --n)
*p++ = '\0';
return s;
}

网上看到的和书上的答案思路是相同的只是实现不同,顿时感觉我的程序难以入眼。

man strncpy 查看一下原型(这个应该和平台有关--Ubuntu)

char *strncpy(char *dest, const char *src, size_t n)
           {
               size_t i;

               for (i = 0; i < n && src[i] != '\0'; i++)
                   dest[i] = src[i];
               for ( ; i < n; i++)
                   dest[i] = '\0';

               return dest;
           }
感觉这样的程序算不上特别好,或许真的跟平台有关。

dest的长度必须足够容纳src,否则出现意想不到的结果
测试1:

dest:1234567

src:abcdef

n:3

结果:abc4567

测试2:

dest:12345

src:abcdefg

n:7

结果:abcdefg


strncat


自己写的:

	void strncat(char *s,const char *t,int n) {
		 char *tmp;
		tmp=t;
		while(*s)
			s++;
		for(;t<(tmp+n)&&*t;t++,s++)
			*s=*t;
	}

书上的答案:

<span style="font-size:18px;">char *liw_strncat(char *s, const char *ct, size_t n) {
char *p;
p = s;
while (*p != '\0')
++p;
for (; n > 0 && *ct != '\0'; --n)
*p = '\0';
return s;
}</span>


man 3 strncat得到的

char *strncat(char *dest, const char *src, size_t n)
           {
               size_t dest_len = strlen(dest);
               size_t i;

               for (i = 0 ; i < n && src[i] != '\0' ; i++)
                   dest[dest_len + i] = src[i];
               dest[dest_len + i] = '\0';

               return dest;
           }


测试:

dest:12345

src:abcd

n:3

结果:12345abc


strncmp

首先说明一下这个函数的作用是将s,t字符串的前n个字符左比较,而不是将t的前n个字符和s字符串比较(我以为是后一种)

int strncmp(const char *s,const char *t,int n) {
		while(n>0 && *s==*t && *s!='\0') {
			s++;
			t++;
			n--;
		}
//n==0的情况可能类似:abcef 1234567 5; *s==*t的情况则类似于:abcef 12345 5 
		if(n==0|| *s==*t)
			return 0;
		if( *(unsigned char *)s < *(unsigned char *)t )
			return -1;
		return 1;
	}

man 3 strncmp没有看到有源码

网上找到一个还不错

int strncmp ( char * s1, char * s2, size_t n)
{
  while (--n && *s1 && *s1 == *s2)
  {
  <span style="white-space:pre">	</span>s1++;
  <span style="white-space:pre">	</span>s2++;
  }
  return (*s1-*s2);
}
这其实跟上面的差不多,至于后面的处理可以把上面的拿下来。


测试:

s1:123456

s2:1234567

n;6

结果:0


s1:123456

s2:1234567

n:7

结果:-1


总结:主要是简单的指针使用,三个函数的实现都采用相同的思路。自己的程序不足主要体现在程序的"附加值“不够,应该在程序的后面返回一个指针方便程序的使用和扩展。




        

2. 采用指针而非数组索引方式改写前面章节和练习中的某些程序,例如getline(第 1、4 章),atoi、itoa 以及它们的变体形式(第 2、3、4 章),reverse(第3 章),strindex、getop(第 4 章)等等。


注意c中也有getline函数

  getline()第一版本--函数返回的是整行的长度

<span style="font-size:18px;">int getline(char s[],int lim) {
		int c,i;

		for(i = 0;i < lim -1 && (c = getchar()) != EOF && c != '\n';++i)
			s[i] = c;
		if(c == '\n') {
			s[i] = c;
			++i;
		}
		s[i] = '\0'; /*'\0'以标志字符串的结束。c规定该类型的字符串常量将以字符数组的形式存储*/
		return i;
	}</span>

第二版本
<span style="font-size:18px;">int getline(char s[], int lim)
{
    int c, i;
    i = 0;
    while (--lim > 0 && (c=getchar()) != EOF && c != '\n')
        s[i++] = c;
    if (c == '\n')
         s[i++] = c;
    s[i] = '\0';
    return i;
}</span>

指针版本:


<span style="font-size:18px;">int getline(char *s,int lim) {
		char *tmp;
		int c;

		 tmp=s;

		while(--lim>0 && (c=getchar())!=EOF && c!='\n')
			*tmp++=c;
		if(c=='\n')
			*tmp++=c;
		*tmp='\0';

		return (int)(tmp-s);
	}
</span>


atoi函数 将字符串转换为对应数值的函数 atoi,它可以处理可选的前导空白符以及一个可选的加 (+)或减(-)号.


<span style="font-size:18px;">int atoi(char s[])
	{
		int i,n,sign;
		
		for(i=0;isspace(s[i]);i++)
			;
		sign=(s[i]=='-') ? -1:1;
		
		if(s[i]=='+' || s[i]=='-')
			i++;
		
		for(n=0;isdigit(s[i]);i++)
			n=n*10 + (s[i]-'0');

		return sign*n;
	}</span>

我写的指针版本(思路跟上面的没有区别):
<span style="font-size:18px;">	int atoi(char *s) {
		int n,sign;

		for(;isspace(*s);s++)
			;
		sign=(*s=='-')?-1:1;
		if(*s=='+' || *s=='-')
			s++;

		for(n=0;isdigit(*s);s++)
			n=n*10 + (*s-'0');

		return sign*n;
	}</span>

答案版本:
<span style="font-size:18px;">int atoi(char *s)
{
    int n, sign;
    while (isspace(*s))
       s++;
    sign = (*s == '+' || *s == '-') ? ((*s++ == '+') ? 1 : -1) : 1; //这一句比较有意思,三木运算符的重叠
    for (n = 0; isdigit(*s); s++)
        n = (n * 10) + (*s - '0'); 

    return sign * n;
}</span>

  itoa



resverse()  将字符串的顺序翻转。

<span style="font-size:18px;">	void resverse(char s[]) {
		int i,j;
		char tmp;

		for(i=0,(j=strlen(s)-1);i<j;i++,j--) {
			tmp=s[i];
			s[i]=s[j];
			s[j]=tmp;
		}
	}</span>

我写的:
<span style="font-size:18px;">	void resverse(char *s) {
		char *p;
		char tmp;
		for(p=(s+strlen(s)-1);s<p;s++,p--) {
			tmp=*s;
			*s=*p;
			*p=tmp;
		}
	}</span>

答案的版本感觉不好就不贴了。



strindex()  该函数返回字符串 t 在字符串 s 中出现的起始位置或索引。当 s 不包含 t 时,返回值为-1。

版本1:

<span style="font-size:18px;">int strindex(char source[],char pattern[]) {
		int i,j,k;
                //遍历source每个字符与pattern中的每个字符比较
		for(i=0;source[i] != '\0';i++) {
			for(j=i,k=0;pattern[k] != '\0' && pattern[k] == source[j];k++,j++) 
				;
			if(k>0 && pattern[k] == '\0')  //当条件满足时说明出现了连续匹配的情况
				return i;
		}

		return -1;
	}</span>


仿照上面修改的指针版本:

<span style="font-size:18px;">int strindex(char *s,char *p) {
		char *tmp;
		while(*s) {
			s++;
			for(tmp=s;*p !='\0' && *p==*tmp;p++,tmp++)
				;
			if(*p=='\0')
				return (int)(tmp-s);
		}
		return -1;
	}</span>

答案版本(这里将查找有可能匹配的位置的功能独立出来,中英文注释都是我写的^_^)

<span style="font-size:18px;">//find out a char match the fist char of pattern and then return the location
	static char *strchar(char *s,int c) {
		char ch=c;
		
		for(;ch!=*s;s++)
			if(*s=='\0')
				return NULL;
		return s;
	}
	
	int strindex(char *s,char *p) {
		char *u,*v,*w;
		//when the pattern string is NULL
		if(*s=='\0') 
			return 0;
		//一旦有在s中出现和p中的第一个字符相同的就进入继续匹配
		for(u=s;(u=strchar(u,*p))!=NULL;u++) {
			for(v=u,w=p; ;)
				//但到最后一个字符时即匹配成功
				if(*++w=='\0')
					return (int)(u-s);
				//当发现有下一个字符不匹配就跳出循环
				else if(*++v !=*w)
					break;
		}
		//when the source don't contain the pattern string
		return -1;
	}</span>


getop中也可以相应的修改用数组可指针表示元素,和指针的自增即可就不贴代码了。


总结:指针很强大。




3.在给定可选参数-n 的情况下,该函数将按数 值大小而非字典顺序对输入的字符串行进行排序


/*
	date:9-5-2014
	author:doodlesomething
	version:1.1

	sort -n

排序函数(排序字符串),在给定可选参数-n 的情况下,该函数将按数值大小而非字典顺序对输入行进行排序
排序程序通常包括 3 部分:判断任何两个对象之间次序的比较操作、颠倒对象次序的交
换操作、一个用于比较和交换对象直到所有对象都按正确次序排列的排序算法。由于排序算
法与比较、交换操作无关,因此,通过在排序算法中调用不同的比较和交换函数,便可以实
现按照不同的标准排序
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAXLEN 1000
#define MAXLINES 1000
#define MAXBUFPSIZE 10000

char *lineptr[MAXLINES];


int main(int argc,char *argv[]) {
	int readlines(char *lineptr[],int lim);
	void quicksort( void *lineptr[],int left,int right,int (*comp)(void *,void *) );
	void writelines(char *lineptr[],int nlines);
	int numcmp(const char *s1,const char *s2);
	int nlines;
	int isnum; //flag

	isnum = 0;
	//带有-n参数
	if(argc > 1 && strcmp(argv[1],"-n") == 0 ) {
		isnum=1;
	}
	

	if( (nlines = readlines(lineptr,MAXLINES) ) > 1) {
		/*
		(int (*) (void *,void *))  (isnum?numcmp:strcmp )  表示将 numcmp或是strcmp
		强制类型转换为 指针类型 ,该指针指向一个带有两个void型的指针,且该函数返回int的数值
		即最后其是一个指针
		(void**) lineptr 不明白 ---》望指点
		*/
		quicksort( (void**) lineptr,0,nlines-1, (int (*)(void *,void *) ) (isnum ? numcmp:strcmp ) );
		writelines(lineptr,nlines);
	}
	else {
		printf("Something wrong");
	}

	return 0;
}
	/*
	将单行收集,并用指针数组的方式存储
	*/
	int readlines(char *lineptr[],int lim) {
		int len;
		char line[MAXLEN];
		char *p;
		int nlines;
		int getLine(char *s,int lim);
		char *alloc(int n);

		nlines=0;

		while( (len = getLine(line,MAXLEN) ) >0 ) {
			if( nlines>lim || (p=alloc(len)) == NULL ) {
				return -1;
			}
			else  {
				line[len-1]='\0';
				strcpy(p,line);
				lineptr[nlines++]=p;
			}
		}

		return nlines;
	}

	/*
	排序:快速排序法
	*/
	void quicksort(void *lineptr[],int left,int right, int (*comp) (void *,void *) ) {

		int i,last;

		void swap(void *v[],int i,int j);

		if(left >= right) 
			return;
		swap(lineptr,left,(left+right)/2 );
		
		last = left;
		//找到last的位置
		for(i = left+1;i <= right;i++ ) {
			if( (*comp)(lineptr[i],lineptr[left]) < 0 ) {
				swap(lineptr,++last,i);
			}
		}

		swap(lineptr,left,last);
		quicksort(lineptr,left,last-1,comp);
		quicksort(lineptr,last+1,right,comp);
	}
	
	/*
	这里之所以将函数参数声明为const主要是因为 三目运算符要求参数类型一致,
	strcmp原型为 int strcmp(const char *s1,const char *s2) 当然也可增加安全性
	*/
	int numcmp(const char *s1,const char *s2) {
		double v1,v2;

		v1=atof(s1);
		v2=atof(s2);

		if(v1<v2)
			return -1;
		else if(v1>v2)
			return 1;
		else 
			return 0;
	}
	//交换两元素指针,以减少内存开销和元素的移动
	void swap(void *v[],int i,int j) {
		void *tmp;
		tmp=v[i];
		v[i]=v[j];
		v[j]=tmp;
	}

	//获取单行
	int getLine(char *s,int lim) {
		char *tmp;
		int c;

		tmp=s;

		while(--lim > 0 && (c=getchar()) !=EOF && c != '\n') 
			*tmp++ = c;
		if(c == '\n')
			*tmp++ = c;
		*tmp = '\0';

		return (int) (tmp-s);
	}

	void writelines(char *lineptr[],int nlines) {
		printf("\n");

		while( (nlines--) > 0) {
			printf("%s\n",*lineptr++);
		}
	}


	//缓冲区
	static char buf[MAXBUFPSIZE];
	//栈顶指针
	static char *bufp=buf;

	//ask for stack
	char *alloc(int n) {
		if(n > 0 && (buf+MAXBUFPSIZE-bufp) > n ) {
			bufp=bufp+n;
			return bufp-n;
		}
		else {
			return -1;
		}
	}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值