C语言技巧巧总结免费.docx
- 文档编号:10801615
- 上传时间:2023-02-23
- 格式:DOCX
- 页数:60
- 大小:47.95KB
C语言技巧巧总结免费.docx
《C语言技巧巧总结免费.docx》由会员分享,可在线阅读,更多相关《C语言技巧巧总结免费.docx(60页珍藏版)》请在冰豆网上搜索。
C语言技巧巧总结免费
C语言技巧总结
自增大总结:
i=(i++)+(++i)i=?
-
源代码:
#include
intmain()
{
inti=1,j=1,k;
i=(i++)+(++i);
printf("i=%d\n",i);
k=(j++)+(++j);
printf("j=%d,k=%d\n",j,k);
}
执行结果:
i=5
j=3,k=4
关键的汇编码片段:
movl$0x1,0xfffffff0(%ebp)
movl$0x1,0xfffffff4(%ebp)
addl$0x1,0xfffffff0(%ebp)
mov0xfffffff0(%ebp),%eax
add%eax,0xfffffff0(%ebp)
addl$0x1,0xfffffff0(%ebp)
mov0xfffffff0(%ebp),%eax
mov%eax,0x4(%esp)
movl$0x8048500,(%esp)
call80482f8
addl$0x1,0xfffffff4(%ebp)
mov0xfffffff4(%ebp),%eax
add0xfffffff4(%ebp),%eax
mov%eax,0xfffffff8(%ebp)
addl$0x1,0xfffffff4(%ebp)
mov0xfffffff8(%ebp),%eax
mov%eax,0x8(%esp)
mov0xfffffff4(%ebp),%eax
mov%eax,0x4(%esp)
movl$0x8048507,(%esp)
call80482f8
发现,先执行++i,最后执行i++。
i=(i++)+(i++),i是不是等于2(0+0,接着i两次自加)
j=(i++)+(i++),j是不是等于0(i没自加前把值赋给了j)
int,float,double之间的关联
-
其实学习过编程的同学,都对这三个东西再熟悉不过了。
int,又称作整型,在.net中特指的是Int32,为32位长度的有符号整型变量。
float,单精度浮点数,32位长度,1位符号位,8位指数位与23位数据位,在.net中又称为Single。
double,64位长度的双精度浮点数,1位符号位,11位指数位,52位数据位。
它们互相的关系就是:
int可以稳式转换成float和double,float只能强制转换成int,但是可以隐式转换成double,double只能强制转换成float和int。
在说明问题之前,还很有必要温习一下计算机组成原理时学习到的一些知识,就是二进制补码表示以及浮点数表示。
我想把一个十进制转化为二进制的方法已经不用多费唇舌,只不过为了计算方便以及消除正零与负零的问题,现代计算机技术,内存里存的都是二进制的补码形式,当然这个也没什么特别的,只不过有某些离散和点,需要特殊定义而已,比如-(2^31),这个数在int的补码里表示成1000…(31个零),这个生套补码计算公式并不能得到结果(其实不考虑进位的话还真是这个结果,但是总让人感觉很怪)。
再者,浮点数,其实就是把任何二进制数化成以0.1....开头的科学计数法表示而已。
废话说完,这就出现了几个问题,而且是比较有意思的问题。
1inti=Int32.MaxValue;
2floatf=i;
3intj=(int)f;
4boolb=i==j;
这里的b,是false。
刚才这个操作,如果我们把float换成long,第一次进行隐式转换,第二次进行强制转换,结果将会是true。
乍一看,float.MaxValue是比int.MaxValue大了不知道多少倍的,然而这个隐式转换中,却造成了数据丢失。
int.MaxValue,这个值等于2^31-1,写成二进制补码形式就是01111…(31个1),这个数,在表示成float计数的科学计数法的时候,将会写成+0.1111…(23个1)*2^31,对于那31个1,里面的最后8个,被float无情的抛弃了,因此,再将这个float强制转换回int的时候,对应的int的二进制补码表示已经变成了0111…(23个1)00000000,这个数与最初的那个int相差了255,所以造成了不相等。
那么提出另一个问题,什么样的int变成float再变回来,和从前的值相等呢?
这个问题其实完全出在那23位float的数据位上了。
对于一个int,把它写成二进制形式之后,成为了个一32个长度的0、1的排列,对于这个排列,只要第一个1与最后一个1之前的间距,不超过23,那么它转换成float再转换回来,两个值就会相等。
这个问题是与大小无关的,而且这个集合在int这个全集下并不连续。
1doubled=0.6;
2floatf=(float)d;
3doubled2=f;
4boolb=d==d2;
这里的b,也是false。
刚才这个操作,如果开始另d等于0.5,结果就将会是true。
乍一看,0.6这个数这么短,double和float都肯定能够表示,那么转换过去再转换回来,结果理应相等。
其实这是因为我们用十进制思考问题太久了,如果我们0.6化成二进制小数,可以发现得到的结果是0.10011001……(1001循环)。
这是一个无限循环小数。
因此,不管float还是double,它在存储0.6的时候,都无法完全保存它精确的值(计算机不懂分数,呵呵),这样的话由于float保存23位,而double保存52位,就造成了double转化成float的时候,丢失掉了一定的数据,非再转换回去的时候,那些丢掉的值被补成了0,因此这个后来的double和从前的double值已经不再一样了。
这样就又产生了一个问题,什么样的double转换成float再转换回来,两个的值相等呢?
其实这个问题与刚才int的那个问题惊人的相似(废话,都和float打交道,能不相似么),只不过我们还需要考虑double比float多了3位的指数位,太大的数double能表示但float不行。
还有一个算是数学上的问题,什么样的十进制小数,表示成二进制不是无限小数呢?
这个问题可以说完全成为数学范畴内的问题了,但是比较简单,答案也很明显,对于所有的最后一位以5结尾的十进制有限小数,都可以化成二进制的有限小数(虽然这个小数可能长到没谱)。
最后,一个有意思有问题,刚才说过0.6表示成为二进制小数之后,是0.1001并且以1001为循环节的无限循环小数,那么在我们将它存成浮点数的时候,一定会在某个位置将它截断(比如float的23位和double的52位),那么真正存在内存里的这个二进制数,转化回十进制,到底是比原先的十进制数大呢,还是小呢?
答案是Itdepends。
人计算十进制的时候,是四舍五入,计算机再计算二进制小数也挺简单,就是0舍1入。
对于float,要截断成为23位,假如卡在24位上的是1,那么就会造成进位,这样的话,存起来的值就比真正的十进制值大了,如果是0,就舍去,那么存起来的值就比真正的十进制值小了。
因此,这可以合理的解释一个问题,就是0.6d转换成float再转换回double,它的值是0.60000002384185791,这个值是比0.6大的,原因就是0.6的二进制科学计数法表示,第24位是1,造成了进位。
到了这里,仍然有一事不解,就是对于浮点数,硬件虽然给予了计算上的支持,但是它与十进制之间的互相转换,到底是如何做到的呢,又是谁做的呢(汇编器还是编译器)。
这个东西突出体现在存在内存里的数明显实际与0.6不等,但是无论哪种语言,都能够在Debug以及输入的时候,将它正确的显示成0.6提供给用户(程序员),最好的例子就是double和ToString方法,如果我写doubled=0.59999999999999999999999999999,d.ToString()给我的是0.6。
诚然,对于double来说,我写的那个N长的数与0.6在内存里存的东西是一样的,但是计算机,又如果实现了将一个实际与0.6不相等的数变回0.6并显示给我的呢?
关于这个问题,欢迎大家讨论并请高手指教一二。
从“交换两个变量而不用临时变量”谈起
-
问题:
写一个函数,实现交换两个变量,但不能用临时变量?
分析:
交换两个变量的这一功能,我们用的比较多,也可以很容易的写出一个出来,但是题目要求的是不能用临时变量。
并且题目也没有说明变量是什么类型,它是int,char,double,还是自定义的结构体,或者是类类型,虽然可以用模板从某种程度上解决这一些问题,但对于自定义的类型,还是不是很好处理。
在这里先从最简单的说起,假设这里的变量是int型的。
一般如果我们要写一个交换整形变量的代码是
1:
inlinevoidSwap(int*a,int*b)
2:
{
3:
inttemp;
4:
temp=*a;
5:
*a=*b;
6:
*b=*a;
7:
}
我们用到了临时变量temp。
思考一个问题我们为什么要用到临时变量呢?
我们平常交换东西时,是否用到了临时的媒介呢?
比方说我手上有一个苹果,你手上有一个桔子,我想和你交换,是如何进行的,用到媒介没有呢?
我们好像是直接交换的,这其中似乎没有用到什么媒介,但是仔细想想过程是用到了媒介的。
交换可以归纳为两种方法,1:
将物品直接从一个人的左手放到一个人的右手2:
借助其它的容器,如果盘,先将物品放在果盘中,再去拿自己要的。
其实这两个也只是一个方法而已,第一个借助的“容器”是我们的身上的另一只空闲的手,很容易忽略掉这个问题,以为我们没有用到其它媒介,要是想想我们只有一只手如何交换呢。
结论1:
物质是有载体的,不管这物质是实体的,还是虚体。
一个载体在同一时间上只能承载一种物质,于是在交换两个载体承载的物质时,是不可能同时进行的,估都要用到其它的媒介载体。
回到计算机的问题上,存放变量也有载体的,它是内存,而变量可以说是一种虚体物质。
这从直观上解释一般为什么我们写交换变量函数时要用到临时变量,也直接反映了编程不是离生活很远的,它不是另一个世界的思维。
即然如此,是不是没有办法了?
方法只给那些勤于思考的人的。
是一定要用到载体媒介吗?
不用到就不行吗?
结合程序设计特点,可以有如下的代码
1:
inlinevoidSwap(int*a,int*b)
2:
{
3:
*a=*a+*b;
4:
*b=*a-*b;
5:
*a=*a-*b;
6:
}
是不是很有意思,真的没有用到其它的临时变量啊!
那我们上面的结论1是不是错了呢?
当然不是,为什么可以直接就可以交换了呢?
还是用上面的例子来说,两个人交换苹果和桔子但是每人只有一个手,也不借助其它的容器,那如何进行哟?
有办法,就只是用一个手拿两样东西,也就是说先将苹果(桔子)给另一个人用一只手拿着,再从他手上拿桔子(苹果)。
基本也就是代码的意思了。
结论2:
看似不可能,但却可以的,要做的是仔细的去挖掘,多思考。
那上面的代码是不是就没有问题了?
有。
要考虑溢出的问题,程序中有一个加法,很可能使结果超出范围,也就是说一只手要是拿不了一个苹果和桔子怎样办?
要相信有问题是可以解决的,可以用assert等等方面处理一下,但不是最好,继续有如下的代码
1:
inlinevoidSwap(int*a,int*b)
2:
{
3:
*a^=*b;
4:
*b^=*a;
5:
*a^=*b;
6:
}
这是什么?
一眼看不出是什么是吗?
不慌,我们可以慢慢的一步步的分析。
对于位运算与、或、非,我们在处理数据的时候是经常要用到的。
我们经常要用到的东西也是在基础的。
设uBitTest为测试数,值为2^n,也就是第n位为1。
uDate为需要处理的数据。
1.将uDate的第n位置1
uDate=uDate|uBitTest
2.将uDate的第n位置0
uDate=uDate&~uBitTest
3.将uDate的第n位取反
uDate=uDate^uBitTest
掌握熟悉了这些在看上面的代码是不是要轻松多了,不是要溢出吗?
不是一只手拿不下苹果和桔子吗?
不要紧,我们可以一部分的交换啊,把苹果切开,桔子剥开来去交换,这样总可以拿下来吧。
结论3:
载体承载物质是必需的,我们不能以改变,却可以改变载体承载的性质。
载体的容量大小不能改变,但可以去改变承载物体的大小,照样可以达到想要的目的。
真的是山穷水复疑无路,柳暗花明又一村。
到这里为止,对于简单的整形变量的交换的情况,已经基本上说清楚了,但并不是说可以去支持任何类型,在这里只是从最简单的例子开始,只是起个引子的作用,是给一些思路的问题。
对于这个问题,还可以从内联汇编的方面去处理,可以看看汇编的代码是如何,从中得到一些启发;对于各种不同类型处理也有其各自的问题需要去处理,如对于类对象怎样去处理拷贝。
对于好的库也可以去看看他们是怎样去写swap这个函数的。
如stl,boost等,从中应该可以学到不少的。
巧用C的Time函数进行时间转换
-
我们经常要遇到时间处理的问题,比如要开发一个schedule的功能,或根据修改时间来过滤文件等。
windowsAPI提供了Get*Time()系列函数用于获取当前时间,但是没有提供进行时间转换的,比如我们要得到距离当前时间2年4个月5天的时间,我们就得自己去计算了。
但是这里有个问题,如果被减的天数大于当前月份的天数,那么天数就会变成负值。
为了解决这个问题,我们就根据不同月份的天数来计算偏移,同时做月和年的变化。
不过这种方法很麻烦,因为每个月天数是不同的还需要考虑闰年和平年的问题。
其实C的Time系列函数可以很好的解决这个问题,
1.首先用TM结构进行需要的时间偏移
2.然后利用mktime这个函数将TM结构转换到从1900.1.1开始的秒数值
3.再利用localtime把秒数转换成TM结构
示例代码如下:
代码
#include"stdafx.h"
#include
#include
#include
usingnamespacestd;
voidOffsetDateTime(conststructtm*inST,structtm*outST,
intdYears,intdMonths,intdDays,
intdHours,intdMinutes,intdSeconds)
{
if(inST!
=NULL&&outST!
=NULL)
{
//偏移当前时间
outST->tm_year=inST->tm_year-dYears;
outST->tm_mon =inST->tm_mon -dMonths;
outST->tm_mday=inST->tm_mday-dDays;
outST->tm_hour=inST->tm_hour-dHours;
outST->tm_min =inST->tm_min -dMinutes;
outST->tm_sec =inST->tm_sec -dSeconds;
//转换到从1900.1.1开始的总秒数
time_tnewRawTime=mktime(outST);
//将秒数转换成时间结构体
outST=localtime(&newRawTime);
}
}
int_tmain(intargc,_TCHAR*argv[])
{
time_trawtime;
structtm*st;
//获取本地当前时间
time(&rawtime);
st=localtime(&rawtime);
cout< //计算时间偏移 structtmoutst; OffsetDateTime(st,&outst,2,3,20,0,0,0); time_tnewTime=mktime(&outst); cout< cout<<"rawTime: "< "< return0; } C语言中标准输入流、标准输出流、标准错误输出流 - 在Linux中,所有对设备和文件的操作都使用文件描述符来进行。 Linux中一个进程启动时,都会打开3个文件: 标准输入、标准输出和标准出错处理。 这三个文件分别对应文件描述符0、1、2。 在C语言中,在程序开始运行时,系统自动打开3个标准文件: 标准输入、标准输出、标准出错输出。 通常这3个文件都与终端相联系。 因此,以前我们所用到的从终端输入或输出都不需要打开终端文件。 系统自定义了3个文件指针stdin、stdout、stderr,分别指向终端输入、终端输出和标准出错输出(也从终端输出)。 标准输入流: stdin 标准输出流: stdout 标准错误输出流: stderr stdin object Standardinputstream Thestandardinputstreamisthedefaultsourceofdataforapplications.Itisusuallydirectedtotheinputdeviceofthestandardconsole(generally,akeyboard). stdincanbeusedasanargumentforanyfunctionthatexpectsaninputstreamasoneofitsparameters,likefgetsorfscanf. Althoughitisgenerallysafetoassumethatthesourceofdataforstdinisgoingtobeakeyboard,bearinmindthatthismaynotbethecaseeveninregularconsolesystems,sincestdincanberedirectedattheoperatingsystemlevel.Forexample,manysystems,amongthemDOS/WindowsandmostUNIXshells,supportthefollowingcommandsyntax: myapplication tousethecontentofthefileexample.txtastheprimarysourceofdataformyapplicationinsteadoftheconsolekeyboard. Itisalsopossibletoredirectstdintosomeothersourceofdatafromwithinaprogramusingthefreopenfunction. stdout object Standardoutputstream Thestandardoutputstreamisthedefaultdestinationofregularoutputforapplications.Itisusuallydirectedtotheoutputdeviceofthestandardconsole(generally,thescreen). stdoutcanbeusedasanargumentforanyfunctionthatexpectsanoutputstreamasoneofitsparameters,likefputsorfprintf. Althoughitisgenerallysafetoassumethatthedefaultdestinationforstdoutisgoingtobethescreen,bearinmindthatthismaynotbethecaseeveninregularconsolesystems,sincestdoutcanberedirectedattheoperatingsystemlevel.Forexample,manysystems,amongthemDOS/WindowsandmostUNIXshells,supportthefollowingcommandsyntax: myapplication>example.txt toredirecttheoutputofmyapplicationtothefileexample.txtinsteadofthescreen. Itisalsopossibletoredirectstdouttosomeothersourceofdatafromwithinaprogramusingthefreopenfunction. stderr object Standarderrorstream Thestandarderrorstreamisthedefaultdestinationforerrormessagesandotherdiagnosticwarnings.Likestdout
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言 技巧 总结 免费