C语言高手编程技巧(精品资料).doc

上传人:asd****56 文档编号:70344321 上传时间:2023-01-19 格式:DOC 页数:48 大小:216KB
返回 下载 相关 举报
C语言高手编程技巧(精品资料).doc_第1页
第1页 / 共48页
C语言高手编程技巧(精品资料).doc_第2页
第2页 / 共48页
点击查看更多>>
资源描述

《C语言高手编程技巧(精品资料).doc》由会员分享,可在线阅读,更多相关《C语言高手编程技巧(精品资料).doc(48页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

1、C语言技巧总结自增大总结:i=(i+)+(+i)i=?- 源代码:#include int main()int i = 1, j = 1, k;i = (i+) + (+i);printf(i=%d n, i);k = (j+) + (+j);printf(j=%d, k=%dn, j, k);执行结果:i=5j=3, k=4关键的汇编码片段:movl $0x1,0xfffffff0(%ebp)movl $0x1,0xfffffff4(%ebp)addl $0x1,0xfffffff0(%ebp)mov 0xfffffff0(%ebp),%eaxadd %eax,0xfffffff0(%ebp

2、)addl $0x1,0xfffffff0(%ebp) mov 0xfffffff0(%ebp),%eaxmov %eax,0x4(%esp)movl $0x8048500,(%esp)call 80482f8 addl $0x1,0xfffffff4(%ebp)mov 0xfffffff4(%ebp),%eaxadd 0xfffffff4(%ebp),%eaxmov %eax,0xfffffff8(%ebp)addl $0x1,0xfffffff4(%ebp)mov 0xfffffff8(%ebp),%eaxmov %eax,0x8(%esp)mov 0xfffffff4(%ebp),%ea

3、xmov %eax,0x4(%esp)movl $0x8048507,(%esp)call 80482f8 发现,先执行+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位长度的

4、双精度浮点数,1位符号位,11位指数位,52位数据位。它们互相的关系就是:int可以稳式转换成float和double,float只能强制转换成int,但是可以隐式转换成double,double只能强制转换成float和int。在说明问题之前,还很有必要温习一下计算机组成原理时学习到的一些知识,就是二进制补码表示以及浮点数表示。我想把一个十进制转化为二进制的方法已经不用多费唇舌,只不过为了计算方便以及消除正零与负零的问题,现代计算机技术,内存里存的都是二进制的补码形式,当然这个也没什么特别的,只不过有某些离散和点,需要特殊定义而已,比如-(231),这个数在int的补码里表示成1000(31

5、个零),这个生套补码计算公式并不能得到结果(其实不考虑进位的话还真是这个结果,但是总让人感觉很怪)。再者,浮点数,其实就是把任何二进制数化成以0.1.开头的科学计数法表示而已。废话说完,这就出现了几个问题,而且是比较有意思的问题。1 int i = Int32.MaxValue;2 float f = i;3 int j = (int)f;4 bool b = i = j;这里的b,是false。刚才这个操作,如果我们把float换成long,第一次进行隐式转换,第二次进行强制转换,结果将会是true。乍一看,float.MaxValue是比int.MaxValue大了不知道多少倍的,然而这个

6、隐式转换中,却造成了数据丢失。 int.MaxValue,这个值等于231-1,写成二进制补码形式就是01111(31个1),这个数,在表示成float计数的科学计数法的时候,将会写成+0.1111(23个1)*231,对于那31个1,里面的最后8个,被float无情的抛弃了,因此,再将这个float强制转换回 int的时候,对应的int的二进制补码表示已经变成了0111(23个1)00000000,这个数与最初的那个int相差了255,所以造成了不相等。那么提出另一个问题,什么样的int变成float再变回来,和从前的值相等呢?这个问题其实完全出在那23位float的数据位上了。对于一个in

7、t,把它写成二进制形式之后,成为了个一32个长度的0、1的排列,对于这个排列,只要第一个1与最后一个1之前的间距,不超过23,那么它转换成 float再转换回来,两个值就会相等。这个问题是与大小无关的,而且这个集合在int这个全集下并不连续。1 double d = 0.6;2 float f = (float)d;3 double d2 = f;4 bool b = d = d2;这里的b,也是false。刚才这个操作,如果开始另d等于0.5,结果就将会是true。乍一看,0.6这个数这么短,double和 float都肯定能够表示,那么转换过去再转换回来,结果理应相等。其实这是因为我们用十

8、进制思考问题太久了,如果我们0.6化成二进制小数,可以发现得到的结果是0.10011001(1001循环)。这是一个无限循环小数。因此,不管float还是double,它在存储0.6 的时候,都无法完全保存它精确的值(计算机不懂分数,呵呵),这样的话由于float保存23位,而double保存52位,就造成了double转化成 float的时候,丢失掉了一定的数据,非再转换回去的时候,那些丢掉的值被补成了0,因此这个后来的double和从前的double值已经不再一样了。这样就又产生了一个问题,什么样的double转换成float再转换回来,两个的值相等呢?其实这个问题与刚才int的那个问题惊

9、人的相似(废话,都和float打交道,能不相似么),只不过我们还需要考虑double比float多了3位的指数位,太大的数double能表示但float 不行。还有一个算是数学上的问题,什么样的十进制小数,表示成二进制不是无限小数呢?这个问题可以说完全成为数学范畴内的问题了,但是比较简单,答案也很明显,对于所有的最后一位以5结尾的十进制有限小数,都可以化成二进制的有限小数(虽然这个小数可能长到没谱)。最后,一个有意思有问题,刚才说过0.6表示成为二进制小数之后,是0.1001并且以1001为循环节的无限循环小数,那么在我们将它存成浮点数的时候,一定会在某个位置将它截断(比如float的23位和

10、double的52位),那么真正存在内存里的这个二进制数,转化回十进制,到底是比原先的十进制数大呢,还是小呢?答案是It depends。人计算十进制的时候,是四舍五入,计算机再计算二进制小数也挺简单,就是0舍1入。对于float,要截断成为23位,假如卡在24位上的是1,那么就会造成进位,这样的话,存起来的值就比真正的十进制值大了,如果是0,就舍去,那么存起来的值就比真正的十进制值小了。因此,这可以合理的解释一个问题,就是0.6d转换成float再转换回double,它的值是0.60000002384185791,这个值是比0.6大的,原因就是 0.6的二进制科学计数法表示,第24位是1,造

11、成了进位。到了这里,仍然有一事不解,就是对于浮点数,硬件虽然给予了计算上的支持,但是它与十进制之间的互相转换,到底是如何做到的呢,又是谁做的呢(汇编器还是编译器)。这个东西突出体现在存在内存里的数明显实际与0.6不等,但是无论哪种语言,都能够在Debug以及输入的时候,将它正确的显示成 0.6提供给用户(程序员),最好的例子就是double和ToString方法,如果我写double d=0.59999999999999999999999999999,d.ToString()给我的是0.6。诚然,对于double来说,我写的那个N长的数与0.6在内存里存的东西是一样的,但是计算机,又如果实现了

12、将一个实际与0.6不相等的数变回0.6并显示给我的呢?关于这个问题,欢迎大家讨论并请高手指教一二。从“交换两个变量而不用临时变量”谈起-问题:写一个函数,实现交换两个变量,但不能用临时变量? 分析:交换两个变量的这一功能,我们用的比较多,也可以很容易的写出一个出来,但是题目要求的是不能用临时变量。并且题目也没有说明变量是什么类型,它是 int,char,double,还是自定义的结构体,或者是类类型,虽然可以用模板从某种程度上解决这一些问题,但对于自定义的类型,还是不是很好处理。在这里先从最简单的说起,假设这里的变量是int型的。一般如果我们要写一个交换整形变量的代码是1: inline vo

13、id Swap(int *a,int *b)2: 3: int temp;4: temp=*a;5: *a=*b;6: *b=*a;7: 我们用到了临时变量temp。思考一个问题我们为什么要用到临时变量呢?我们平常交换东西时,是否用到了临时的媒介呢?比方说我手上有一个苹果,你手上有一个桔子,我想和你交换,是如何进行的,用到媒介没有呢?我们好像是直接交换的,这其中似乎没有用到什么媒介,但是仔细想想过程是用到了媒介的。交换可以归纳为两种方法,1:将物品直接从一个人的左手放到一个人的右手2:借助其它的容器,如果盘,先将物品放在果盘中,再去拿自己要的。其实这两个也只是一个方法而已,第一个借助的“容器”

14、是我们的身上的另一只空闲的手,很容易忽略掉这个问题,以为我们没有用到其它媒介,要是想想我们只有一只手如何交换呢。结论1:物质是有载体的,不管这物质是实体的,还是虚体。一个载体在同一时间上只能承载一种物质,于是在交换两个载体承载的物质时,是不可能同时进行的,估都要用到其它的媒介载体。回到计算机的问题上,存放变量也有载体的,它是内存,而变量可以说是一种虚体物质。这从直观上解释一般为什么我们写交换变量函数时要用到临时变量,也直接反映了编程不是离生活很远的,它不是另一个世界的思维。即然如此,是不是没有办法了?方法只给那些勤于思考的人的。是一定要用到载体媒介吗?不用到就不行吗?结合程序设计特点,可以有如

15、下的代码1: inline void Swap(int *a,int *b)2: 3: *a=*a+*b;4: *b=*a-*b;5: *a=*a-*b;6: 是不是很有意思,真的没有用到其它的临时变量啊!那我们上面的结论1是不是错了呢?当然不是,为什么可以直接就可以交换了呢?还是用上面的例子来说,两个人交换苹果和桔子但是每人只有一个手,也不借助其它的容器,那如何进行哟?有办法,就只是用一个手拿两样东西,也就是说先将苹果(桔子)给另一个人用一只手拿着,再从他手上拿桔子(苹果)。基本也就是代码的意思了。结论2:看似不可能,但却可以的,要做的是仔细的去挖掘,多思考。那上面的代码是不是就没有问题了?

16、有。要考虑溢出的问题,程序中有一个加法,很可能使结果超出范围,也就是说一只手要是拿不了一个苹果和桔子怎样办?要相信有问题是可以解决的,可以用assert等等方面处理一下,但不是最好,继续有如下的代码1: inline void Swap(int *a, int *b)2: 3: *a = *b;4: *b = *a;5: *a = *b;6: 这是什么?一眼看不出是什么是吗?不慌,我们可以慢慢的一步步的分析。对于位运算与、或、非,我们在处理数据的时候是经常要用到的。我们经常要用到的东西也是在基础的。设uBitTest为测试数,值为2n,也就是第n位为1。uDate为需要处理的数据。1.将uDa

17、te的第n位置1uDate = uDate|uBitTest2.将uDate的第n位置0uDate = uDate&uBitTest3.将uDate的第n位取反uDate = uDate uBitTest掌握熟悉了这些在看上面的代码是不是要轻松多了,不是要溢出吗?不是一只手拿不下苹果和桔子吗?不要紧,我们可以一部分的交换啊,把苹果切开,桔子剥开来去交换,这样总可以拿下来吧。结论3:载体承载物质是必需的,我们不能以改变,却可以改变载体承载的性质。载体的容量大小不能改变,但可以去改变承载物体的大小,照样可以达到想要的目的。真的是山穷水复疑无路,柳暗花明又一村。到这里为止,对于简单的整形变量的交换的

18、情况,已经基本上说清楚了,但并不是说可以去支持任何类型,在这里只是从最简单的例子开始,只是起个引子的作用,是给一些思路的问题。对于这个问题,还可以从内联汇编的方面去处理,可以看看汇编的代码是如何,从中得到一些启发;对于各种不同类型处理也有其各自的问题需要去处理,如对于类对象怎样去处理拷贝。对于好的库也可以去看看他们是怎样去写swap这个函数的。如stl,boost等,从中应该可以学到不少的。巧用C的Time函数进行时间转换-我们经常要遇到时间处理的问题,比如要开发一个schedule的功能,或根据修改时间来过滤文件等。windows API提供了Get*Time()系列函数用于获取当前时间,但

19、是没有提供进行时间转换的,比如我们要得到距离当前时间2年4个月5天的时间,我们就得自己去计算了。但是这里有个问题,如果被减的天数大于当前月份的天数,那么天数就会变成负值。为了解决这个问题,我们就根据不同月份的天数来计算偏移,同时做月和年的变化。不过这种方法很麻烦,因为每个月天数是不同的还需要考虑闰年和平年的问题。其实C的Time系列函数可以很好的解决这个问题, 1. 首先用TM结构进行需要的时间偏移2. 然后利用mktime这个函数将TM结构转换到从1900.1.1开始的秒数值3. 再利用localtime 把秒数转换成TM结构示例代码如下:代码#include stdafx.h#includ

20、e #include #include using namespace std;void OffsetDateTime(const struct tm* inST, struct tm* outST,int dYears, int dMonths, int dDays,int dHours, int dMinutes, int dSeconds)if (inST != NULL & outST != NULL)/ 偏移当前时间outST-tm_year = inST-tm_year - dYears;outST-tm_mon = inST-tm_mon - dMonths;outST-tm_m

21、day = 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_t newRawTime = mktime(outST);/ 将秒数转换成时间结构体outST = localtime(&newRawTime);int _tmain(int argc, _TCHAR* argv)time_t rawtime;struct tm *

22、st;/ 获取本地当前时间time(&rawtime);st = localtime(&rawtime);cout tm_year - tm_mon - tm_mday endl;/ 计算时间偏移struct tm outst;OffsetDateTime(st, &outst, 2, 3, 20, 0, 0, 0);time_t newTime = mktime(&outst);cout outst.tm_year - outst.tm_mon - outst.tm_mday endl;cout rawTime: rawtime endl newTime : newTime endl;ret

23、urn 0;C语言中标准输入流、标准输出流、标准错误输出流-在Linux中,所有对设备和文件的操作都使用文件描述符来进行。 Linux中一个进程启动时,都会打开3个文件:标准输入、标准输出和标准出错处理。这三个文件分别对应文件描述符0、1、2。在C语言中,在程序开始运行时,系统自动打开3个标准文件:标准输入、标准输出、标准出错输出。通常这3个文件都与终端相联系。因此,以前我们所用到的从终端输入或输出都不需要打开终端文件。系统自定义了3个文件指针 stdin、stdout、stderr,分别指向终端输入、终端输出和标准出错输出(也从终端输出)。标准输入流:stdin标准输出流:stdout标准错

24、误输出流:stderrstdinobjectFILE * stdin;Standard input streamThe standard input stream is the default source of data for applications. It is usually directed to the input device of the standard console (generally, a keyboard).stdin can be used as an argument for any function that expects an input stream

25、as one of its parameters, like fgets or fscanf.Although it is generally safe to assume that the source of data for stdin is going to be a keyboard, bear in mind that this may not be the case even in regular console systems, since stdin can be redirected at the operating system level. For example, ma

26、ny systems, among them DOS/Windows and most UNIX shells, support the following command syntax:myapplication example.txtto use the content of the file example.txt as the primary source of data for myapplication instead of the console keyboard.It is also possible to redirect stdin to some other source

27、 of data from within a program using the freopen function.stdoutobjectFILE * stdout;Standard output streamThe standard output stream is the default destination of regular output for applications. It is usually directed to the output device of the standard console (generally, the screen).stdout can b

28、e used as an argument for any function that expects an output stream as one of its parameters, like fputs or fprintf.Although it is generally safe to assume that the default destination for stdout is going to be the screen, bear in mind that this may not be the case even in regular console systems,

29、since stdout can be redirected at the operating system level. For example, many systems, among them DOS/Windows and most UNIX shells, support the following command syntax:myapplication example.txtto redirect the output of myapplication to the file example.txt instead of the screen.It is also possibl

30、e to redirect stdout to some other source of data from within a program using the freopen function.stderrobjectFILE * stderr;Standard error streamThe standard error stream is the default destination for error messages and other diagnostic warnings. Like stdout, it is usually also directed to the out

31、put device of the standard console (generally, the screen).stderr can be used as an argument for any function that expects an output stream as one of its parameters, like fputs or fprintf.Although generally both stdout and stderr are associated with the same console output, applications may differen

32、tiate between what is sent to stdout and what to stderrfor the case that one of them is redirected. For example, it is frequent to redirect the regular output of a console program (stdout) to a file while expecting the error messages to keep appearing in the console screen.It is also possible to red

33、irect stderr to some other destination from within a program using the freopen function.perrorfunctionvoid perror ( const char * str );Print error messageInterprets the value of the global variable errno into a string and prints that string to stderr (standard error output stream, usually the screen

34、), optionaly preceding it with the custom message specified in str.errno is an integral variable whose value describes the last error produced by a call to a library function. The error strings produced by perror depend on the developing platform and compiler.If the parameter str is not a null point

35、er, str is printed followed by a colon (:) and a space. Then, whether str was a null pointer or not, the generated error description is printed followed by a newline character (n).perror should be called right after the error was produced, otherwise it can be overwritten in calls to other functions.

36、Parameters.strC string containing a custom message to be printed before the error message itself.If it is a null pointer, no preceding custom message is printed, but the error message is printed anyway.By convention, the name of the application itself is generally used as parameter.获取当前系统所有进程-#inclu

37、de stdafx.h #include #include int main(int argc, char* argv)HANDLE hSnapshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);if (!hSnapshot)printf(CreateToolhelp32Snapshot ERROR!n);return 1;PROCESSENTRY32 pe32;pe32.dwSize = sizeof(PROCESSENTRY32 );if (!Process32First (hSnapshot, &pe32)printf(Proc

38、ess32First ERROR!n);doprintf(ProcID:%d-%sn,pe32.th32ProcessID ,pe32.szExeFile );while(Process32Next (hSnapshot, &pe32);return 0;有趣的位运算-今天碰到一个问题:在不增加新的变量的情况下,交换两个int型变量的值。题目描述很简单,但是考虑起来还是比较有难度的。这里提出一个位运算的妙用:异或的妙用 异或是这样一种运算:如果两位相同,同为1或同为0,那么异或为0,否则异或为1.同时需要说明的是,在进行异或运算的是补码。知道规则以后,我们可以来看这个问题了。这个题目可以这样做

39、:假设两个int型变量a,b: int a, b;a = a b;b = a b;a = a b;hoho!3次同样的操作,完成了这样一个神奇的运算,位运算大有可为。C中如何显示*.bmp文件-1.*.bmp文件结构 *.bmp文件和大多数图形文件一样,分为文件描述区(头文件信息)和图象存储区(象素数据)两部分。而头文件信息中又包含了信息区和调色板区两部分,信息区又可以细分为文件信息区和图象信息区两部分。这里以256色320*200的bmp图象为例。头文件描述区的偏移长度是1078个字节,也就是说图象存储区是从文件偏移1078后开始读取的。在头文件描述区中头信息区的偏移长度是54个字节,也就是

40、说调色板数据区是从54-1078之间的1024字节。在头信息区中文件信息区占14个字节而图象信息区占40字节。(1) 文件信息区typedef struct BMP_fileunsigned int bfType; /文件类型unsigned long bfSize; /bmp文件长度unsigned int Reserved1;unsigned int Reserved2;unsigned long bfOffset; /文件描述区长度,16色为118,256色为1078bitmapfile;现在算一下,有3个int,2个long,正好3*2+2*4=14字节(2) 图象信息区type st

41、ruct BMP_infounsigned long biSize;unsigned long biWidth;unsigned long biHeight;unsigned int biPlanes;unsigned int biBitCount;unsigned long biCompression;unsigned long biSizeImage;unsigned long biXplosPerMeter;unsigned long biYplosPerMeter;unsigned long biClrUsed;unsigned long biClrImportant;bitmapin

42、fo;现在算一下,2个int,9个long,正好是2+2*9*4=40字节。(3)调色板区typedef struct RGB_BMP_typunsigned char blue;unsigned char green;unsigned char red;unsigned char reserved;RGB_BMP,*RGB_BMP_ptr;说明:三原色+灰度,共4*256=1024字节。下面是bmp文件的完整的结构定义:typedef struct bmp_picture_typbitmapfile file;bitmapinfo info;RGB_BMP palette256;char f

43、ar *buffer;bmp_picture, *bmp_picture_ptr;2. bmp文件的显示(1)图象存储区的读取由于bmp图象是从下至上存储的,所以我们不能进行直接顺序读取。详细的说,bmp图象存储区数据是从1078偏移字节开始。文件内第一个图象点实际上是对应图象(320*200)第200行的最左边的第一个点,而从1078开始的320个点则是图象最下面一行对应的点,之后的321个点是图象倒数第二行最左边的第一个点。这样,bmp文件最后一个字节对应的点是第一行最后边的点了。下面是实现bmp文件图象存储区数据读取到内存的代码:for(i=info.biHeight-1;i=0;i-)

44、lseek(fp,1078+(long)(info.biHeight-i-1)*info.biWidth,0);read(fp,&bmp256-bufferi*info.biWidth,info.biWidth);(2)调色板的读取除了图象存储区的存放规则是倒序的以外,bmp文件调色板内容也是以B,G,R,灰度的顺序存放的。所以读取时不要将文件中的三原色中的兰色对应给调色板结构体变量中的红色。同时,由于三原色只使用64种(6位)色阶,而三原色的存放空间是1字节(8位),所以要将bmp文件三原色的6位数据都放在1个字节的高位,则在读取调色板结构体变量的时候必须进行右移2位。以下给出读取文件调色板数据的代码for(i=0;ipalette.blue,1);read(fp,&bmp256-palette.green,1);read(fp,&bmp256-palette.red,1);read(fp,&bmp256-palette.reserved,1);bmp256-palette.blue=bmp256-

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 技术资料 > 其他杂项

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号© 2020-2023 www.taowenge.com 淘文阁