c语言算法怎样统计一个算法执行的CPU时间

算法时间复杂度与程序执行时间计算
已有 6430 次阅读
|个人分类:|系统分类:|关键词:算法时间复杂度 算法指令增速
算法时间复杂度与程序执行时间计算姜咏江 & & & 简介:算法时间复杂度的研究,以所谓的多项式时间做为最低复杂度,由此认定只有多项式类型的算法程序执行最快,实在有些不靠谱。实际上计算机程序执行时间可以用其编译之后的机器语言程序来计算。因为每一机器指令(汇编指令)的指令周期都是确定的,故计算程序执行的时间,可先计算出每条机器指令重复执行次数,然后与指令周期相乘求和,最终获得准确时间。本文给出了的算法程序执行中指令重复执行次数的二元函数基本计算公式,依据该计算公式可以实际计算出程序运行的时间消耗,又提出了程序设计方法转换为一元函数之后,如何判定在同样的循环重复数之下,判定不同算法指令重复执行次数增加的快慢,进而判定各种算法程序执行时间的变化。1.背景为了研究算法程序完成任务运行时间长短,人们引入了算法时间复杂度的概念。在维基百科中,算法时间复杂度被定义为:在中,的时间复杂度是一个,它定量描述了该算法的运行时间。这是一个关于代表算法输入值的的长度的函数。时间复杂度常用表述,不包括这个函数的低阶项和首项系数。在百度文库中有计算方法解释:一般情况下,的基本操作重复执行的次数是模块n的某一个函数f(n),因此,算法的记做:T(n)=O(f(n))。若 T(n)/f(n) 求极限可得到一常数c,则T(n) = O(f(n))。不论维基百科还是百度的解释,都称算法时间复杂度是程序运行时间的定量描述。实际上,我们见到的表述只能算定性描述,根本谈不上定量。为了能够确实对算法程序运行时间进行定量分析,本文从单处理器计算机指令设计与程序执行的原理出发,依据程序设计的基本结构,提出了一种比较切合实际的程序执行时间计算方法。结果会见到有限时间的程序运行计算,都可以表达成一种二元的多项式形式。2.指令重复执行次数我们知道,不论任何形式的计算机设计语言程序都要转化成机器语言程序才能够执行。因此,用与机器语言一一对应的汇编语言来讨论程序执行时间才应该是最准确的。不过由于各种语言程序在执行时间分析上具有一定的一致性,故而作定性分析时,也可以替代汇编语言。不论何种计算机程序设计语言,就程序设计的基本结构来说都是一致的。程序的基本结构分为:顺序、分支、循环和子程序调用。任何计算机程序的执行都可以说是这几种程序结构的重复,所以计算程序执行过程所消耗的时间,都离不开对程序基本结构的分析。程序执行的过程是由每个指令执行的过程组成的,因而程序执行的时间就是指令执行时间的累加。在汇编程序语言中,每条指令执行的时间消耗是确定的。特别是那些指令周期相等的计算机系统设计,用单一指令执行的时间乘以全部指令重复执行的次数,就能够计算出程序执行所需要的时间。如果计算机的指令周期不相同,则可以用每条指令的指令周期做为系数,进行所谓的加权求和来计算程序执行的时间。因此,算法程序执行时间的消耗,最关键的是计算出每条指令重复执行的次数。3.指令重复执行次数公式我们将汇编程序不在执行状态的每条指令叫“写出的指令”,计算指令重复执行次数,就与这种写出的指令有关。指令重复执行次数是由程序的基本结构所决定的。在顺序程序结构中写出的指令重复执行次数是1。而在循环结构中,写出的指令重复执行次数与循环的次数一致。程序执行中消耗时间最长的就是循环程序结构。直观地说,循环的次数越多,处在循环体内的写出指令重复执行次数越多,因而累计耗时就越长。在此种观点之下,整个程序就可以看成是多层循环结构,可以按照多层循环结构来计算程序执行时间。当然,子程序可以单独计算子程序执行的时间。由于分支程序结构的程序分支选择总是惟一的,故时间计算基本上等同于顺序结构。我们将顺序与分支都认定为0层循环,那么程序运行的耗时计算,可以建立在多层循环结构的耗时计算上,也就是循环结构的写出指令重复执行次数计算上。如果用函数来描述,那么一个程序的指令重复执行次数T应为层内循环次数n与循环层数k的二元函数,即T=f(n,k)。为说明问题简单通俗,我们通过c程序的例子加以解释。例1,假设k=n,那么幂nn结构的n重循环用c语言描述如下。for( i1=1; i0&=n; i1++){for( i2=1; i1&=n; i2++){ &......for( in=1; in&=n; in++){no1 += 1; no2 += no1; }}...}for语句条件表达式的初值语句在每层只执行一次,而定界和步长语句都要执行n次。显然最内层每个语句重复循环次数是nn,向外各层依次为nn-1、nn-2、…、n2、n。那么总的语句重复执行次数为1+3n+3n2+3n3+…+3nn-2 +3nn-1+4nn。对于一个程序,在顺序结构中,指令重复执行次数是1;在单层n次循环结构中,指令重复执行的次数是n;在k层n次循环当中写出的指令,其重复执行的次数是nk。于是各层指令重复执行的次数可用公式f(n,k) = a0+a1n+a2n2+...+ak-1nk-1+aknk & & & & & & & & & & & & & & & & & & & & & &(1)来计算,其中ai是各循环层的写出指令数,i=0,1,2,...,k。如果将公式(1)的k认为是常量,那么(1)式不就是一个多项式吗?我们能说这种形式的算法复杂度程序执行最快吗?4.计算类型实例公式(1)中,若幂指数最高固定为常数k,则得到k次多项式;若幂底数固定为常数k,则得到指数多项式;若1层循环次数从1开始,逐层循环次数加1递增,则得到阶乘多项式。如此变化循环层数或循环次数,则可以得到各种重复执行计算的实际公式。为了说明这方面的演变,我们再举两个实例。例2,将(1)式的幂底数(循环次数)设定为常数2,则能够得到指数型指令重复执行的多项式表达式a0+a12+a222+...+an-12n-1+an2n & & & & & & & & & & & & &。例3,设定幂底数是常数k,下面的循环结构可以实现nlogkn型的指令重复执行次数。for( i=0; i&n; i++){for( j=k; j&=n; j*=k){no1 += 1;no2 += no1;}}由j*=k知j的值循环变化序列为k,k2,k3,..., kt=n,于是内层循环次数t=logkn。由于这个程序的外层循环次数是n,内层循环次数是logkn,因而程序指令重复执行次数总共是1+2n+n+4n logkn=1+3n+4n logkn。5.程序执行时间比较关于算法程序时间复杂度,一种流行的观点认为多项式类型的算法复杂度较低,而且认为多项式类型算法程序运行耗时最少。其实在大多数情况下这是一种错觉。从公式(1)我们知道,决定算法程序执行时间长短的有两个因素,一个是多项式的幂指数k,另一个是多项式的幂底数n。当幂指数k较大的时候,虽然它是一个常数,我们也不能够认为多项式时间类型算法程序运行时间消耗会最少。特别在n&k时,从(1)式立即知道,nk多项式型算法程序时间的消耗会大于幂nn型算法程序的耗时。这说明在较大的循环层数的范围内,不能够就认为多项式时间具有较低的复杂度,当然也不能够认为这种情况的算法程序耗时最短。我们不妨仅就一项来进行比较。例如设n=5,k=7,那么nk=57=78125;而nn=55=3125。由此可见用多项式时间复杂度来说明算法程序耗时长短是不太靠谱的事情。有人也许会说,n趋于无穷才对。想象一下,n趋于无穷大对程序执行的时间计算的意义有多大?6.算法指令增速在前面我们已经指出,程序执行时间要用指令重复执行次数为计算单位。并给出了二元多项式型计算公式。这自然会引出什么样的算法程序耗时较多,什么样的算法程序耗时较少的问题。由于同样的问题或任务可以采用不同形式的算法解决,故而研究不同算法程序执行耗时的多少具有现实意义。6.1 指令增速将(1)这个公式进行一元函数变换,即或者固定比较次数,或者固定循环层数,可以得到各种与实际问题有关的一元函数。例如我们用c表示常数,n表示变量,(1)式可以演化出nc、cn、nn、nlogcn等多种类型程序指令重复执行次数的计算公式。不同的类型公式直接与程序执行的耗时有关,因而我们有兴趣去研究,那些能够判定出采用何种类型算法更能够耗时较少的方法。为了统一且不失一般性,我们让循环各层参与执行的指令都尽可能的少些,比如各层执行的指令只有一个。那么由(1)式可以得到下面一些类型的指令重复次数的一元函数表达式。这个函数我们用F(n)表示。F(n)=1c+2c+3c+...+nc &
& & & & & &
& & & & & & & & & & (2)F(n)=20+21+22+...+2n-1+2n & & & & & & & & & & & & & & & & & & (3)F(n)=1!+2!+3!+...+(n-1)!+n! & & & & & & & & & & & & & & &(4)F(n)= lg1+2lg2+3lg3+...+(n-1)lg(n-1)+nlgn & & & & & & & &(5)F(n)=11+22+33+...+(n-1)n-1+nn & & & & & & & & & & & & & & (6)这些表达式中的变量n或者表示循环中指令重复执行次数,或者表示程序循环结构的层数,也可以具有两者同步的属性。为了研究各种算法程序执行过程中F(n)的增长快慢,我们引入F(n)的指令增长速度概念。定义1:F(n)中变量n增加一个单位的指令重复次数增长量,称为指令增速。由定义可知指令增速是与n有关的函数。将定义1用表达式写出,并用υ(n)来记,则有:υ(n)=F(n) - F(n-1)于是,表达式(2)的指令n点增速υ(n)= nc;表达式(3)的指令增速υ(n)=2n;表达式(4)的指令增速υ(n)=n!;表达式(5)的指令增速υ(n)= nlgn;表达式(6)的指令增速υ(n)=nn。例,n=10,求上列各算法指令增速。表达式(2)的指令增速是υ(n)=nc=10 c.表达式(3)的指令增速是υ(n)=2n=210=1024.表达式(4)的指令增速是υ(n)=n!=10!=3628800.表达式(5)的指令增速是υ(n)= nlgn=10 lg10=10.表达式(6)的指令增速是υ(n)=nn=1010=.指令增速不是表示算法程序执行时间缩短的指标,而是在算法中指令重复执行的次数增加的快慢指标。如果指令增速较大,则说明在某种程序设计之下指令重复执行的次数增加很快。例如在n=9到n=10的变化中,上面各种算法程序指令增速最快的是幂nn型,指令增速最慢的是nlgn型。所谓的多项式型由指数常量确定,c=1时指令增速最慢,若是c的值超过10,那么在这个地方,会成为指令增速最快的算法形式。因而我们说所谓的O(nk)表示的多项式时间程序执行最快是不靠谱的。公式(1)的每一种确定的变化形式都代表一种算法。指令增速无疑能够表示在确定的循环次数或循环层数处指令重复执行次数的增长快慢。这自然可以大致描述n值附近的程序执行耗时的变化。例如n=10,我们立即可以计算出由n=9变到n=10的指令增速,从而判定出那种算法在此处耗时增加快慢。6.2 指令相对变化率依据定义1我们能够计算出指令增速表达式,但是由于在n的值变动的情况下,我们仍然不能直接比较出各种形式的表达式指令增速的快慢,为了能够在统一的范围内进行比较,我们引入单位指令重复执行次数下的指令增速,即指令相对变化率的概念。定义2:指令增速与指令重复执行次数的比,称为指令相对变化率。用μ表示指令相对变化率,即有μ=υ(n)/ F(n)。由指令相对变化率的大小就可以知道,不同类型算法的指令增速对程序执行时间长短的影响程度。这样,表达式(2)的指令相对变化率μ=υ(n)/ F(n)= nc /(1c+2c+3c+...+nc);表达式(3)的指令相对变化率μ=υ(n)/ F(n)= 2n /(20+21+22+...+2n-1+2n);表达式(4)的指令相对变化率μ=υ(n)/ F(n)= n! /(1!+2!+3!+...+(n-1)!+n!);表达式(5)的指令相对变化率μ=υ(n)/ F(n)= nlgn /( lg1+2lg2+3lg3+...+(n-1)lg(n-1)+nlgn);表达式(6)的指令相对变化率μ=υ(n)/ F(n)= nn /(11+22+33+...+(n-1)n-1+nn)。在这些公式中只要确定n值,就可以求出指令相对变化率。6.3 算法指令增速做为一种算法,一般都要研究公式(1)中k或n的全局变化情况。这种变化会随着k与n的值超大变化带来诸多的未知状况,为此对k或n向着无穷大方向的变化,就体现了与算法相关的指令执行重复数的整体变化规律。对于一元函数的情况,我们引入算法指令增速的概念。定义3:指令相对变化率的极限称为算法指令增速。我们希望在n较大的时候,通过算法一元函数全局的变化,来寻找算法类型对指令执行重复数的增长规律。如此我们对上面的几种类型指令相对变化率求极限。表达式(2)的算法指令增速为nc /(1c+2c+3c+...+(n-1)c+ nc)=0;表达式(3)的算法指令增速为 lim 2n /(20+21+22+...+2n-1+2n) =1/(2-n+2-n+1+...+2-1+1)=1/2;这是一个等比序列,如果幂底数是常数c,那么结果将是(c-1)/c。表达式(4)的算法指令增速为 lim&n! /(1!+2!+3!+...+(n-1)!+n!)=1表达式(5)的算法指令增速为 lim nlgn /( lg1+2lg2+3lg3+...+(n-1)lg(n-1)+nlgn) & & & &这个极限是多少?我没能求出,依指令增速判断应该是0。请有兴趣的读者帮我补充一下。表达式(6)的算法指令增速为 lim nn /(11+22+33+...+(n-1)n-1+nn)=1(上面各极限n-&∞)
& &由指令增速与指令重复执行次数的定义可知,μ=υ(n)/ F(n)是一个0与1之间的数。因而0是最小的算法指令增速,1是最大的算法指令增速。由此知从算法全局来看,多项式型算法的指令增速最小,阶乘型或幂指型增速最快,而指数型介于它们之间。需要说明,算法指令增速是对算法全局的一种属性描述,它并不能够代表算法程序执行的实际时间。这是因为不论何种算法,只要n趋向无穷,那么算法程序执行就不会有完结的时候,因而也就无所谓程序执行时间快慢问题了。算法指令增速很像算法时间复杂度,但算法指令增速是真实依据时间计算获得的概念,就此一点就说明比没有确切定义的算法时间复杂度更加实用。7.结论计算程序执行时间,要通过程序写出的指令重复执行次数进行,指令重复执行次数的多少,取决于程序循环结构的循环次数和循环层数这两个变量。通俗地讲,循环层数和循环次数越大,程序运行得出结果所消耗的时间越长。任何在计算机上完成任务的程序运行,都需要在有限时间内解决问题,不然我们编写的程序就失去了意义。因而研究程序运行时间的有限性,远比研究程序执行时间的无限性更为重要。但是为了定性地比较算法的效率,我们引入了算法指令增速的概念,作者相信,依据算法指令增速的概念可以推进算法的深入研究,也或许对P与NP的讨论会有所帮助。Email &&&&&&
转载本文请联系原作者获取授权,同时请注明本文来自姜咏江科学网博客。链接地址:
上一篇:下一篇:
当前推荐数:4
评论 ( 个评论)
扫一扫,分享此博文
作者的精选博文
作者的其他最新博文
热门博文导读
Powered by
Copyright &如何正确地统计函数执行时间 - 推酷
如何正确地统计函数执行时间
1923年,美国福特公司一台大型电机发生故障,来“会诊”的专家一连数月一筹莫展。后来,移居美国的德国科学家斯坦敏茨经过反复摸索比对在电机旁用粉笔画了一道线,并指导工程师们打开电机,将画线的地方的线圈减少16匝后,电机果然恢复正常运转。斯坦敏茨因此开口向福特公司要1万美金的酬劳。有人说:“画一条线值一万美金,这是敲竹杆”。斯坦敏茨听后说道:“粉笔画一条线,1美元。知道在哪里画线,9999美元。”
最近在做性能优化方面的工作,而这其中最重的就是找出性能的瓶颈在哪里——知道了在哪画粉笔线,才能对症下药。
TraceView、Systrace当然是非常好的分析工具,但是profile 的引入必然就导致函数真实执行时间的不准确,我们只能借助这些profile工具分析可能的瓶颈在哪;而且很多时候,工具也是有误差的,甚至误差会超出可以容忍的范围——那就是错误了。想象一下,你用一把最小刻度是1m的尺子去测量1cm长的物体,这样的结果还可靠吗?因此,使用时钟来计量函数的执行时间,这种最原始的方法往往也是最有效的办法;今天要聊的就是我们常用的那几个时钟函数。
在Android里面我们有如下几个时钟函数可以使用:
前面三个函数我就不说了,
说的很清楚,无非就是算不算休眠时间的问题;建议好好看一下。
当我看到第四个函数
() 的文档的时候,我不由得心里一惊:
Returns milliseconds running in the current thread.
返回当前线程运行的时间?
我们知道,Android里面的线程调度跟一般的操作系统一样,使用的是时间片轮转的方式。线程之间通过抢占CPU时间片来轮流执行达到并发执行的效果。那么问题来了:如果一个线程在执行的时候,CPU被别的线程抢去了,被抢去的这段时间算这个线程的执行时间么?如果我们需要分析的函数里面有IO操作,这种情况几乎是必然的——现代CPU才不会傻傻等待IO完成了,它肯定跑去执行别的CPU任务了。
那么,上面几个计算时间的函数对这种情况分别有什么不同的表现呢?写个demo测一下:
new Thread(new Runnable() {
public void run() {
long currentTime1 = System.currentTimeMillis();
long currentThreadTime1 = SystemClock.currentThreadTimeMillis();
SystemClock.sleep(1000);
Log.d(TAG, &cost:& + (System.currentTimeMillis() - currentTime1));
Log.d(TAG, &cost2:& + (SystemClock.currentThreadTimeMillis() - currentThreadTime1));
}).start();
下面是输出结果:
MainActivity: cost:1000
正如文档所述,currentThreadTimeMillis()这个函数返回的是在 当前线程处于running状态的时间。
因此,如果你想统计一个计算密集型函数的执行时间,很有可能被一个IO操作给坑了哦;更一般的情况是,在多线程环境(数百个)下,如果你要统计一个函数的执行时间,很有可能它丫的时间片被抢走了,你统计了线程runnable的时间!!给一张Java线程状态的图:
我估计很多人不知道有这么一个函数,因此在你统计执行时间的时候,你选择了对的API吗?
OK,文章说到这里应该就结束了;但是本着刨根问底的态度,这个native的函数到底实现是啥样的呢?
我们找到C++的
;代码在/framework/base/jni/android_os_SystemClock.cpp :
* native public static long currentThreadTimeMillis();
static jlong android_os_SystemClock_currentThreadTimeMillis(JNIEnv* env,
jobject clazz)
#if defined(HAVE_POSIX_CLOCKS)
struct timespec tm;
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm);
return tm.tv_sec * 1000LL + tm.tv_nsec / 1000000;
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000LL + tv.tv_usec / 1000;
可以看到,这里是用了Linux的API,我们看看这个API的文档:
CLOCK_THREAD_CPUTIME_ID Thread-specific CPU-time clock.
所以,你弄清楚了应该使用哪个API测试函数执行时间了吗 ^_^
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致君,已阅读到文档的结尾了呢~~
C语言如何计算程序运行时间,c语言计算运行时间,c语言程序运行时间,c语言计算器程序代码,c语言程序设计计算器,c语言计算器程序,c语言计算器程序报告,c语言计算时间差,c语言简单计算器程序,c语言计算时间
扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
C语言如何计算程序运行时间
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='/DocinViewer--144.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口完成一个程序,作用是统计一个文件夹下面所有文件的代码行数。输入是一个文件夹的绝对路径,输出是代码行数。所以此程序的新特点有两个:
统计某一文件夹下的所有文件;
可以任意指定本机硬盘上任何位置的某一个文件夹。
在中熟悉了文件的基本操作。但仍然有改进的余地:统计特定文件时,还是需要手动输入文件名。如果文件数量很多怎么办?可不可以直接统计某个文件夹下面的所有文件的代码行数?今天解决的就是这个问题。
我们已经解决了一个问题:写一个带有文件操作的C语言程序,输入文件名,输出此文件下的换行数。
考虑了一下现有的成果以及新增的目标,有需要更改的地方应该是输入,由文件名换成一个文件夹,然后通过程序扫描一遍这个文件夹下面所有的文件。然后读取所有的文件名,存放到数组里。再把字符串由fopen一个个调用,统计行数。细节差不多是这样,但总归要先扫描文件夹吧!
怎么扫描文件夹下面的文件?
搜索引擎搜索:
有找到几个链接,比较有用,可以参考:
比较可惜的是,上述网站提供的参考方法有使用函数opendir,查找了一下,发现这是一个在Linux系统下的函数。本人没有用过Linux。想了想,并没有直接开始搜索“安装Linux教程”,我心想:总有不用这个函数的方法吧!于是依旧在搜索引擎上不断地寻找。
找了接近一小时也无果,我打算换个问法。于是我在搜索引擎打入了:
立刻映入我眼帘的就有一个,我惊呆了。
我立刻在桌面按照步骤进行实验。不得不说,看到结果,我非常满意。这不就我要的文件夹下面的所有文件名的字符串组合嘛!
立刻通过编程实现这一功能,效果拔群。
#include&stdio.h&
int main()
fp = fopen(&TEXT.bat&,&w&);
fputs(&DIR *.* /B& LIST.TXT&,fp);
fclose(fp);
这里面有一行神秘的操作码,
DIR *.* /B& LIST.TXT
问题来了,这一行代码到底是什么呢?还是
大概知道了:
dir 显示目录中的文件和子目录列表;
/B 使用空格式(没有标题信息或摘要);
* . * (两个星号中间一个句点)就是说显示所有文件,文件名和扩展名没有限制。举例说明:如果是*.txt,则是显示所有扩展名为txt的文件;
最后面是字符串,很容易就知道是重定向输出的文件名。
好了。那么进行下一步的操作。我们考虑到一个文件夹只统计C语言代码的行数,所以这里我考虑只对.c后缀的文件进行这种操作。
#include&stdio.h&
int main()
fp = fopen(&Text.bat&,&w&);
fputs(&DIR *.c /B& list.txt&,fp);
fclose(fp);
fp=fopen(&Text.bat&,&r&);
fclose(fp);
这儿遇到了第一个问题,运行可执行文件main.exe之后,文件夹内的文件情况如下图:
没有产生预期想要获得的文本文件list.txt。看来问题出在:
fp = fopen(&Text1.bat&,&r&);
这行语句上。因为,已经正常生成.bat文件,并且双击运行的话还是能生成文本文件,说明.bat文件没有问题,应该是打开文件的方式有问题。
立刻搜索:。得到:
在程序中使用system() 函数。
假设bat文件的名称叫a.bat 即:
system(&a.bat&);
通过搜素来的方法,轻松解决这个问题。
#include&stdio.h&
int main()
fp = fopen(&Text.bat&,&w&);
fputs(&DIR *.c /B& list.txt&,fp);
fclose(fp);
system(&Text.bat&);
编译后运行结果:
得到了我们想要的list.txt。也就是说.bat文件被正确地执行了。生成LIST的工作已经完成。现在已经有了存储文件名字符串的文本文件,下面只需要从这些文件中读取文件名,再调用上一篇随笔中实现的功能,即可实现这次需要添加的新功能了。
实现的简单的从list.txt中读取文件名。打开文件进行行数统计:
#include&stdio.h&
#include&Windows.h&
#include&string.h&
int main()
fp = fopen(&Text.bat&,&w&);
fputs(&DIR *.c /B&list.txt&,fp);
fclose(fp);
system(&Text.bat&);
/*~~~the end of creating file name list~~~*/
/*~~~the beginning of get .c file name from list~~~*/
static int count = 0;
FILE *fp1, *fp2;
fp1 = fopen(&list.txt&,&r&);
char s[100], singleline[1000];
while(fgets(s, 100, fp1))//get the name(one line) of a file from the list
int len=strlen(s);
if(s[len-1]=='\n') s[len-1]='\0';
what is this?
printf(&%s: &,s);
fp2 = fopen(s,&r&);//open the correct file, according to the file name
while(fgets(singleline, 1000, fp2))
printf(&%d\n&, count);
fclose(fp2);
printf(&\n&);
fclose(fp1);
system(&pause&);
我们先不要管带有注释 what is this? 的那一行代码。
编译后执行,这次我在可执行文件.exe的目录下放了一些.c文件进行测试,看看运行结果如何:
我觉得代码意思都很明了了,然而执行结果还是失败的。
仔细观察结果,发现几个奇怪的点。
.c文件是没问题的,统计代码行数的几行代码是由上一个版本引进的所以也应该是没问题的,但是结果却是0;
输出中有多余的空行。
找到问题所在了!由于fgets函数碰到'\n'是会停止输入的,\n留在了缓冲区,下一次fgets时会导致文件读取失败(个人猜想,可能有误)所以我们要设法处理掉这个'\n'。
思考一下,数组s是存储文件名的。举&C++.txt&为例,假设造成读入失败的原因是字符串后面跟了一个\n
'\n'的位置是s[strlen(s)-1]为此我们通过如下代码进行debug
int len=strlen(s);
if(s[len-1]=='\n')
s[len-1]='\0';
再次编译运行,完成功能。此时距离成功已经很近了!
如何指定任意的文件夹路径?绝对路径!
绝对路径是什么?请看下面的代码:
fp = fopen(&test.txt&, &r&);
我们知道fopen函数的第一个参数是一个字符串,代表文件名,这里的&test.txt&就不是绝对路径,而是当前文件夹下(也可以理解为.exe可执行程序的文件夹)的文件。如果要使用绝对路径名,就把绝对路径输入进第一个参数即可。怎么看文件夹or文件的绝对路径呢?
很简单,鼠标右键,属性,就可以看了。例如上图test的文件夹的绝对路径是E:\test
这里有一个注意点,在代码中操作字符串时,要想代表输入一个反斜杠,需要输入两个反斜杠。大家还记得吗?还有就是注意输入时要把中文输入法关掉了。
贴上比较粗略的代码,附赠注释,以及执行效果图一张:
#include&stdio.h&
#include&Windows.h&
#include&string.h&
int main()
char filepath[1000], batpath[1010]; //the absolute path of a file folder and a .bat file.
gets(filepath);//input absolute path of a file folder
strcpy(batpath, filepath);
strcat(batpath, &\\Text1.bat&);
fp = fopen(batpath, &w&);
fputs(&DIR *.c /B&list.txt&, fp);
fclose(fp);
system(batpath);
/*~~~the end of creating file name list~~~*/
/*~~~the beginning of get .c file name from list~~~*/
static int count = 0;
FILE *fp1, *fp2;
fp1 = fopen(&list.txt&, &r&);
char s[100];
char singleline[1000];
while(fgets(s, 100, fp1))//get one line from list, each line refers to a .c file name
int len = strlen(s);
if(s[len-1] == '\n') s[len-1] = '\0';
printf(&%s: &, s);
fp2 = fopen(s, &r&);
/*~~~the beginning of counting lines of code~~~*/
while(fgets(singleline, 1000, fp2))//open the correct file, according to the file name
printf(&%d\n&, count);
fclose(fp2);
printf(&\n&);
fclose(fp1);
system(&pause&);
由图片可以看出,我们想要的功能:输入一个文件夹的路径,统计文件夹下面的.c文件的代码行数,已经实现了。
主要的工作实现原理都在上面这段代码里,最后要做的工作就是
实现可循环输入;
错误信息提示;
考虑改改变量名、增加一点宏定义什么的,让代码更具有可读性;
还可以考虑添加的功能,例如可以分别显示每个文件各自的代码行数,最后显示一个总行数。
稍后我会把完成版代码推上我的github。
想了想还是要感谢下栋哥的启发式教学,让我完成了一个小小的东西,虽然没啥技术含量吧,但是也是自己独立完成的,成就感还是有的。
这个问题我想了一早上,到了中午卡在绝对路径那儿,结果头晕晕的就去睡午觉了,醒来之后才解决的。233
最后,当然就是有什么错误,或者是有疑惑的地方都可以跟我说哦!
阅读(...) 评论()

我要回帖

更多关于 c语言全排列递归算法 的文章

 

随机推荐