ImageVerifierCode 换一换
格式:DOCX , 页数:21 ,大小:24.40KB ,
资源ID:22698770      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/22698770.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(高级程序员装逼指南Word文件下载.docx)为本站会员(b****8)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

高级程序员装逼指南Word文件下载.docx

1、* 装逼进阶 *老是装逼也不成,所以我一直在寻找一种秒杀一切程序员的装逼之法说实话,程序员的世界里有三种人大部分是不会写代码的麻瓜,然后是程序员这个群体本身他们认为唯一比程序员牛逼的,就是搞数学的人了所以嘛,你要真想装个牛逼,就去学好傅立叶变换吧另,发明Lisp和Python的人都是数学家,高得纳大神也是数学教授还有更多装逼之法,想到再加C程序员装逼指南文档名称:C程序员装逼指南(C Coder Zhuangbility Manual)文档日期:00 zhuangbility:这可能是我写的最不靠谱的文档了。本文档源于光棍节前的一次玩笑,随后明白,这东西根本没法写。一来,正如回字有几样写法一样

2、迂腐,语言语法级别的东西不是那么上档次;二来,它们确实在实际开发中没有什么用。但语言中确实有一些好的技巧应该被整理收集。比如:char f = char f =%c%c%s%c;%cmain() printf(f,10,34,f,34,10,10);%c;main() printf(f,10,34,f,34,10,10);上面的程序可以输出自己的源代码。这是老牌黑客喜欢玩的quine游戏。下面这个网站收集了很多,我随手抄来:即使我保留了装逼指南的名字,而实际内容却可能是一些杂项和随想。01 char:严格的说unsigned char、signed char和char是三个类型。char是有无

3、符号由实现决定。在中记录char的最大值和最小值,一般是有符号的。因此对于参与计算时,将char定义为byte时,最好显式使用unsigned char:typedef unsigned char byte;VC提供了/J编译选项,使char从有符号变成无符号。下面是使用不当char的错误:#define MAKE_DWORD(x)(DWORD)(x)0 + (x)1 8) + (x)2 16) + (x)3 24)当x是一个PCHAR,它指向了0x000fccd0。但经过MAKE_DWORD后的结果是0x000ecbd0。因为0xd0和0xcc被当成负数,参与计算时,高位被扩展成1。在进行右

4、移操作时,如果操作数为负,那么右移后最高位还是1。sizeof(char)被定义为1。4byte至少是8位,它需要容下127个ASCII码,并保证它们是非负的。中定义了当前处理器平台上byte的位数。而char最少需要容下基本字符集(C定义)。在C标准之前的1960年,8bit System/360最终使用ASCII码作为基本编码。1960年之前byte的大小不统一,8, 9, 16, 32, or 36 bits都存在过。1970年之后便统一为8。避免如下写法:sizeof a /* C中值为4,C+中值为1。 */对于局部字符数组,编译器会在全局数据区保存字符string,在初始化arra

5、y时,有一个隐式的复制过程,这会带来意想不到的性能损失。void foo(void) char array = 02 :根据C标准,E1E2的含义是(*(E1)+(E2)。因此,只要E1和E2中有一个为指针即可,而没有指定E1或E2:char array4;array0 = 1array = (2*1)array = 以上都是合法的。03 do-while:do while(0)至少有两种用法:一、代替goto;二、消除宏歧义。下面看例子:do p = malloc(0); if (!p) break; . while (0);if (p) p = free();如果代码风格规定确实不能使用g

6、oto,那么这里的break可以起到跳转的作用。#define stat_macro(i) do i = 0; i+; while(0)if (con) stat_macro(i);elsedo-while(0)巧妙的解决了之后的;,并在没有的if, else的语句中保持原意。04 fastcall:Borland C+ fastcall字符、整型、指针类型的参数在传递时依次是EAX、EDX、ECX。而远指针和浮点类型依然是通过堆栈传递的。VC+ fastcall字符、整型、指针类型的参数在传递时依次使用ECX、EDX而没有使用第三个寄存器。而_int64、浮点、远指针是通过堆栈传递的。在gc

7、c 3.4.6中引入了fastcall:fastcallOn the Intel 386, the fastcall attribute causes the compiler topass the first argument (if of integral type) in the register ECXand the second argument (if of integral type) in the register EDX.Subsequent and other typed arguments are passed on the stack.The called functi

8、on will pop the arguments off the stack. If thenumber of arguments is variable all arguments are pushed on thestack.这和VC+的fastcall调用方式是兼容的。而_attribute_(regparm(3)和bcb中的fastcall兼容。注意:gcc中的fastcall关键字和_attribute_(regparm(3)是不相同的。05 function:参数个数可变的函数必须标记为 _cdecl。显式标记为 _cdecl、_fastcall或_stdcall 的函数使用指定

9、的调用约定。采用的参数个数可变的函数总是使用_cdecl 调用约定。对于 C,_cdecl 命名约定使用前面带下划线 (_) 的函数名;除非声明为 extern C,否则 C+ 函数将使用不同的名称修饰方案。_fastcall 函数的一些参数传入寄存器(对于 x86 处理器,为 ECX 和 EDX),而其余的参数按从右向左的顺序入栈。被调用函数在返回之前从堆栈中弹出参数。esp指向栈顶,栈顶地址值却是比较小的值。每次push的结果都使esp减小,而pop则相反。cdecl调用按声明顺序由右向左压栈,调用函数清栈。函数名前加_。stdcall调用按声明顺序由右向左压栈,被调用函数清栈。,后加和参

10、数个数。下面是在main中调用两种函数的汇编例子:main() push ebp mov ebp,esp sub esp,44h ebx esi edi lea edi,ebp-44h ecx,11h eax,0CCCCCCCCh rep stos dword ptr ediint c;c = CdeclCall(1, 2); 2 1 call ILT+0(_CdeclCall) (00401005) add esp,8 dword ptr ebp-4,eaxc = StdCall(1, 2); ILT+5(_StdCall8) (0040100a) pop cmp _chkesp (0040

11、10f0) esp,ebp retCdeclCall比StdCall多了清栈指令add esp,8。cdeclCall函数内部: |stdcall函数内部: |int _cdecl CdeclCall(int a, int b) |int _stdcall StdCall(int a, int b) | ebp | ebp,esp esp,40h esp,40h ebx esi edi edi,ebp-40h edi,ebp-40h ecx,10h ecx,10h eax,0CCCCCCCCh dword ptr edi return a + b; eax,dword ptr ebp+8 ea

12、x,dword ptr ebp+8 eax,dword ptr ebp+0Ch| eax,dword ptr ebp+0Ch | esp,ebp ret 8其中开头指令都是:push ebpmov ebp,espsub esp,Xxx其后的三个保存寄存器的指令是规定:push最后它们会被释放pop函数返回时会根据调用方式使用ret n 或者 ret。在给函数下断点时esp以上是该函数参数,以下是该函数局部变量。06 wchar_t:wchar_t在C+中是内置的关键字,但在C中不是。C是通过typedef定义的。VC的/Zc:wchar_t和gcc的-fshort-wchar选项都可以控制wc

13、har_t的宽度。在Windows平台上sizeof(wchar_t)为2,而Linux平台上为4。-fshort-wchar选项可以使wchar_t变成unsigned short int。07 macro:预处理可以计算uintmax_t大小的常量。C标准定义:typedef long long intmax_t;typedef unsigned long long uintmax_t;利用预处理器这个特点可以处理更大的数值计算:#define test_macro (163)#if test_macro = 0#error error!#endifunsigned long long t

14、est = test_macro;在C程序中,常量的大小被限制在unsigned long之内,对于long long大小常量可以通过预处理器计算。上面的error!并没有输出,因为test_macro没有溢出。08 _VA_ARGS_:变参宏使得你可以定义类似的宏:#define LOG( format, . ) printf( format, _VA_ARGS_ )LOG( %s %d, str, count );_VA_ARGS_是系统预定义宏,被自动替换为参数列表。这个特性在GCC和VC中都是支持的。然而Gcc还一种独有的语法扩展:#define LOG(fmt.) printf(fm

15、t)即使没有标准的支持,Gcc依然可以支持变参数宏。09 gcc:void * 类型在gcc可以当作整数计算,VC不可以。这导致sizeof(void)值为1,而VC中,它的值为零,并打印一条警告。_func_为C99定义。Gcc支持_func_和_FUNCTION_,然而VC只支持不标准的_FUNCTION_。_func_是字符串数组,而_FUNCTION_是宏,可以连接字符:_FUNCTIN_(i1 i2 ? i1 : i2;);Gcc可以有,VC真没有。这是Gcc扩展语法,内的表达式值可以作为整个()的值。这就可以使宏的行为和函数更相近,并同样起到和do-while(0)相同的消除歧义作

16、用。strcut test_struct int test;struct test_struct test = (struct test_struct)1;宏替换方式两者也不相同,Gcc嵌套替换宏,VC会一次全部替换宏。10 stack:通过栈中返回地址和变量的压栈关系,我们可以得到调用者的地址:void foo(int a, int b) printf(%xn, (unsigned long *)&a)-1);下面这个技巧可能是最危险的,它需要四个条件才能正常工作。1.源代码函数的编写顺序和生成顺序相对应,并且先实现的函数在低地址,后实现的在高地址。2.禁止增量链接。增量链接会生成ITL跳转

17、表,函数首地址只是表中偏移地址。增量编译的原理主要是通过改写跳转表代替重新编译函数调用。3.禁止内联。只有禁止内联才能生成函数调用框架(/Oy)。4.禁止FPO。打开FPO的程序有参数压栈指令而没有call、ret指令。#include #pragma comment(linker, /INCREMENTAL:NO)#pragma optimize(y, off)_declspec(noinline) void foo(int a, int b);_declspec(noinline) void foo_only_called(int a, int b);, on)void foo_only_

18、called(int a, int b) foo(0, 0); unsigned long ret_addr = (unsigned long *)&a)-1; assert(ret_addr (unsigned long)foo_only_called);int main(int argc, char* argv) foo_only_called(0, 0);foo只能从foo_only_called中调用,main调用foo会引发断言。如果函数没有参数,则需要用内置函数_AddressOfReturnAddress和_ReturnAddress。在这里_ReturnAddress()和re

19、t_addr是相等的,都是mov eax,dword ptr ebp+4。gcc相关参数是-enable-frame-pointer/-fomit-frame-pointer,_attribute_(noline), #pragma GCC optimize (O0)或设置函数属性O0禁止了FPO优化。栈的故事到这里还没有结束。众所周知,内存分配往往是性能瓶颈,如果小量并且频繁分配的内存从栈分配而不是从堆分配,那么程序的性能会有一定的提升,并且栈内存不会泄漏,它不需要释放。一般从栈中分配内存是用alloca函数,它会帮你刺探栈内存是否够用,它失败也仅仅因为此。内存分配好后,它会自动更新esp。

20、类似的方案还有C99支持的变长数组,但变长数组不好控制,并且编译器未必兼容。X86的栈回溯的工作原理也是通过EBP寄存器一步一步得到每个栈信息:_asm mov FramePointer, EBP我们知道在函数开始处都有:mov ebp, esp作为函数的最开始两句代码。这样根据EBP就可以找到所有的函数地址。NextFramePointer = *(PULONG_PTR)(FramePointer);ReturnAddress = *(PULONG_PTR)(FramePointer + sizeof(ULONG_PTR);更多信息请参考作者的另一篇文档Windows NT Stack Trace。11 trap:有时程序为了调试等目的,需要主动触发异常。下面两句都可以达到这样的效果:_asm ud2_asm int 3x86为触发错误指令异常定义了0x0f0xb9和0x0f0x0b两个未概念指令,也就是ud1和ud2。int 3是群众喜闻乐见的唤醒调试器指令,值为0xcc,中文为烫。12 bit:用C操作bit是很酷的事:所谓酷就是玩得好很精彩,玩不好就很惨。下面是例子:x & (x-1) 将X的最低位1置0x | (x+1) 将X的最低位0置1(WORD)(B

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1