本文目录一览:
- 1、大学本科研究生科目网络—软件开发课程—软件操作课程有哪些?(比如ps fl 之类的要说全哦
- 2、汇编问题
- 3、汇编遇到奇怪的问题
- 4、C语言:用extern和不用extern声明方法有什么区别?
- 5、C语言问题 在线等
- 6、为什么包含初始化式的extern声明不能位于函数内
大学本科研究生科目网络—软件开发课程—软件操作课程有哪些?(比如ps fl 之类的要说全哦
1. 计算机底层
这实际上就是兵哥哥说的《计算机组成原理》,我推荐的书是《深入理解计算机系统》,1000%的会比你的课本给你更多的东西。不管是写程序做软件还是搞科研发论文,不对底层有一个清晰的认识,你的整个前进道路都是模糊不清的。而只对体系结构的了解是远远不够的,深挖才是归途,为什么现在很多解析内存CPU技术的博文博客火呢?因为大家都忽略啊!又因为大家都知道这是多么有用啊!不做底层的分析,你怎么去精准的定位问题呢?怎么去写出切合真实计算逻辑的简单而又高效的优秀代码呢?别闹了。我同意一个观点——读优秀的代码,从OS开始。读这些就是你学习认知底层的过程,就是在站在巨人肩膀上远眺的历程。
2. 数学
过硬的数学功底使得你能看到一些新技术的出现而不茫然,看到的旧的技术能快速的跟进上手。这就是所谓“知识”和“技能”的关系,将工具运用到纯熟的地步,你会发现别人遇到的很多问题在你这里都不是问题了,看起论文来也得心应手啦,这就是潜移默化和下意识的作用。
3. C/C++
排在第三位是因为我觉得不是极其重要了。当然,C语言是基本功我就不在赘述。
一门语言对你的作用是认识到计算机的“思考方式”与人类的不同,想要更加精确简单(KISS原则)的实现自己的想法,就要按照这个逻辑思路去执行。而选择C语言是因为它是成熟的,是最为值得探究的语言,也是你今后可能进入Unix,Linux,嵌入式,甚至于IOS方向的基石。
4. 算法设计
依托数学的知识,在算法上的成绩应该不会太次。《算法导论》是要看的没错。如果时间少的话,《编程珠玑》《编程之美》《APUE》,这些书是要接触的。
5. 英语/(日语)
不得不承认美帝和倭寇就是比我们先进。
学习英语的好处不言而喻,打破这层壁垒就会让你感受到真正的世界的样子,去那些纯英文的编程网站,BBS,讨论组,个人博客,增长见识的同时是你在逐步的认清这个世界,知道外面的人在做什么在想什么,现如今的潮流是什么(为什么不提媒体我觉得你应该懂得),更进一步你未来的发展方向是什么,是否和大环境有冲突,这是你这一生究竟能达到多大成就的一个很重要的潜在因素,也是为你今后走出校园做一些非常必要的知识储备和心理预备役。
日语的问题,是在英语之后的一个可选项,IT方面的日企非常多,待遇也非常好,我们怀抱着“师夷长技”的心态去学习就好了。
汇编问题
机器在存储时不区分有符号数和无符号数,只是在做一些比较和位扩展时用到的寄存器和机器指令不相同
具体看《深入理解计算机系统》第二,三章节
一、只有一个标准!
在汇编语言层面,声明变量的时候,没有 signed 和 unsignde 之分,汇编器统统,将你输入的整数字面量当作有符号数处理成补码存入到计算机中,只有这一个标准!汇编器不会区分有符号还是无符号然后用两个标准来处理,它统统当作有符号的!并且统统汇编成补码!也就是说,db -20 汇编后为:EC ,而 db 236 汇编后也为 EC 。这里有一个小问题,思考深入的朋友会发现,db 是分配一个字节,那么一个字节能表示的有符号整数范围是:-128 ~ +127 ,那么 db 236 超过了这一范围,怎么可以?是的,+236 的补码的确超出了一个字节的表示范围,那么拿两个字节(当然更多的字节更好了)是可以装下的,应为:00 EC,也就是说 +236的补码应该是00 EC,一个字节装不下,但是,别忘了“截断”这个概念,就是说最后的结果被截断了,00 EC 是两个字节,被截断成 EC ,所以,这是个“美丽的错误”,为什么这么说?因为,当你把 236 当作无符号数时,它汇编后的结果正好也是 EC ,这下皆大欢喜了,虽然汇编器只用一个标准来处理,但是借用了“截断”这个美丽的错误后,得到的结果是符合两个标准的!也就是说,给你一个字节,你想输入有符号的数,比如 -20 那么汇编后的结果是正确的;如果你输入 236 那么你肯定当作无符号数来处理了(因为236不在一个字节能表示的有符号数的范围内啊),得到的结果也是正确的。于是给大家一个错觉:汇编器有两套标准,会区分有符号和无符号,然后分别汇编。其实,你们被骗了。:-)
二、存在两套指令!
第一点说明汇编器只用一个方法把整数字面量汇编成真正的机器数。但并不是说计算机不区分有符号数和无符号数,相反,计算机对有符号和无符号数区分的十分清晰,因为计算机进行某些同样功能的处理时有两套指令作为后备,这就是分别为有符号和无符号数准备的。但是,这里要强调一点,一个数到底是有符号数还是无符号数,计算机并不知道,这是由你来决定的,当你认为你要处理的数是有符号的,那么你就用那一套处理有符号数的指令,当你认为你要处理的数是无符号的,那就用处理无符号数的那一套指令。加减法只有一套指令,因为这一套指令同时适用于有符号和无符号。下面这些指令:mul div movzx … 是处理无符号数的,而这些:imul idiv movsx … 是处理有符号的。
举例来说:
内存里有 一个字节x 为:0x EC ,一个字节 y 为:0x 02 。当把x,y当作有符号数来看时,x = -20 ,y = +2 。当作无符号数看时,x = 236 ,y = 2 。下面进行加运算,用 add 指令,得到的结果为:0x EE ,那么这个 0x EE 当作有符号数就是:-18 ,无符号数就是 238 。所以,add 一个指令可以适用有符号和无符号两种情况。(呵呵,其实为什么要补码啊,就是为了这个呗,:-))
乘法运算就不行了,必须用两套指令,有符号的情况下用imul 得到的结果是:0x FF D8 就是 -40 。无符号的情况下用 mul ,得到:0x 01 D8 就是 472 。(参看文后附录2例程)
三、可爱又可怕的c语言。
为什么又扯到 c 了?因为大多数遇到有符号还是无符号问题的朋友,都是c里面的 signed 和 unsigned 声明引起的,那为什么开头是从汇编讲起呢?因为我们现在用的c编译器,无论gcc 也好,vc6 的cl 也好,都是将c语言代码编译成汇编语言代码,然后再用汇编器汇编成机器码的。搞清楚了汇编,就相当于从根本上明白了c,而且,用机器的思维去考虑问题,必须用汇编。(我一般遇到什么奇怪的c语言的问题都是把它编译成汇编来看。)
C 是可爱的,因为c符合kiss 原则,对机器的抽象程度刚刚好,让我们即提高了思维层面(比汇编的机器层面人性化多了),又不至于离机器太远 (像c# ,java之类就太远了)。当初KR 版的c就是高级一点的汇编……:-)
C又是可怕的,因为它把机器层面的所有的东西都反应了出来,像这个有没有符号的问题就是一例(java就不存在这个问题,因为它被设计成所有的整数都是有符号的)。为了说明c的可怕特举一例:
#include stdio.h
#include string.h
int main()
{
int x = 2;
char * str = “abcd”;
int y = (x – strlen(str) ) / 2;
printf(“%d\n”,y);
}
结果应该是 -1 但是却得到:2147483647 。为什么?因为strlen的返回值,类型是size_t,也就是unsigned int ,与 int 混合计算时类型被自动转换了,结果自然出乎意料。。。
观察编译后的代码,除法指令为 div ,意味无符号除法。
解决办法就是强制转换,变成 int y = (int)(x – strlen(str) ) / 2; 强制向有符号方向转换(编译器默认正好相反),这样一来,除法指令编译成 idiv 了。我们知道,就是同样状态的两个内存单位,用有符号处理指令 imul ,idiv 等得到的结果,与用无符号处理指令mul,div等得到的结果,是截然不同的!所以牵扯到有符号无符号计算的问题,特别是存在讨厌的自动转换时,要倍加小心!(这里自动转换时,无论gcc还是cl都不提示!!!)
为了避免这些错误,建议,凡是在运算的时候,确保你的变量都是 signed 的。
汇编遇到奇怪的问题
你的问题有误。
并不是在执行了 mov bx, 0指令“以后”出现问题,而是在执行它“以前”。也就是说,只要mov sp, 10h执行了,问题就出现了。
出现这个现象的原因,是你在“调试”。如果不用单步方式调试,而是直接运行,或者用设置在后面较远的适当位置的断点方式调试,结果是不一样的。
DOS下调试一个程序,所有对寄存器、内存数据的修改,是真实的CPU寄存器和内存修改。它不是用一个隔离过的虚拟环境外部控制。
单步调试,靠的是 INT 01H内部单步中断。
每执行一条指令,就会发生一次 int 01h中断。
而中断,是需要用堆栈的(保存断点现场)。
所以,你设置的堆栈区的数据,在单步调试过程中,就被改写了。
其实,将堆栈设置到关键数据区或关键代码区,正是防止跟踪破解的技巧之一。这样设置,可以让程序正常运行不受影响,但用单步调试进行跟踪就不行。
解决办法:
堆栈单独设置。
考虑到中断(包括调试中断和随机出现的硬件中断)会改变堆栈中内容,要保护重要数据区的安全,堆栈的设置要避开它们。
在涉及利用堆栈进行的操作中途不要用断点或单步方式调试,调试时使用断点方式,断点设置在堆栈操作开始前或全部完成后,堆栈操作开始前关中断(用CLI指令),完成后开中断(用STI指令)。
C语言:用extern和不用extern声明方法有什么区别?
在C语言中,修饰符extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的,要在此处引用。
1.
extern修饰变量的声明。举例来说,如果文件a.c需要引用b.c中变量int v,就可以在a.c中声明extern int
v,然后就可以引用变量v。这里需要注意的是,被引用的变量v的链接属性必须是外链接(external)的,也就是说a.c要引用到v,不只是取决于在a.c中声明extern
int
v,还取决于变量v本身是能够被引用到的。这涉及到c语言的另外一个话题--变量的作用域。能够被其他模块以extern修饰符引用到的变量通常是全局变量。还有很重要的一点是,extern
int v可以放在a.c中的任何地方,比如你可以在a.c中的函数fun定义的开头处声明extern int
v,然后就可以引用到变量v了,只不过这样只能在函数fun作用域中引用v罢了,这还是变量作用域的问题。对于这一点来说,很多人使用的时候都心存顾虑。好像extern声明只能用于文件作用域似的。
2.
extern修饰函数声明。从本质上来讲,变量和函数没有区别。函数名是指向函数二进制块开头处的指针。如果文件a.c需要引用b.c中的函数,比如在b.c中原型是int
fun(int mu),那么就可以在a.c中声明extern int fun(int mu),然后就能使用fun来做任何事情。就像变量的声明一样,extern
int fun(int
mu)可以放在a.c中任何地方,而不一定非要放在a.c的文件作用域的范围中。对其他模块中函数的引用,最常用的方法是包含这些函数声明的头文件。使用extern和包含头文件来引用函数有什么区别呢?extern的引用方式比包含头文件要简洁得多!extern的使用方法是直接了当的,想引用哪个函数就用extern声明哪个函数。这大概是KISS原则的一种体现吧!这样做的一个明显的好处是,会加速程序的编译(确切的说是预处理)的过程,节省时间。在大型C程序编译过程中,这种差异是非常明显的。
3.
此外,extern修饰符可用于指示C或者C++函数的调用规范。比如在C++中调用C库函数,就需要在C++程序中用extern
“C”声明要引用的函数。这是给链接器用的,告诉链接器在链接的时候用C函数规范来链接。主要原因是C++和C程序编译完成后在目标代码中命名规则不同
C语言问题 在线等
以下合法的一组常量是
A. 1.24e3 08211 0xFF 12L //合法
B. 38.00e8f 12UL 01777777 ‘X’ // 38.00e8f 不合法
C. 20FA 07321 0xffff ”A“ //20Fa不合法
D. 3.14E2.3 0x32768 -27 1UL // 3.14E2.3 不合法
2. 以下常量合法的一组是
A. 1L 0380 1.2e2.3 ‘S’ // 1.2e2.3 不
B. 3.8e3 1.8f 反斜杠ff ”x“ // 反斜杠ff 不
C. ‘反斜杠反斜杠’ 0732 反斜杠xFFL 363u // 反斜杠xFFL
D. 01000 12.34 12UL 2AF0 \\2AFo 不
第二题应该是没有答案。。
为什么包含初始化式的extern声明不能位于函数内
extern定义的变量必须是全局的,这样才可能在其他文件中使用,所以,不能再语句块里定义。位于函数内的变量是局部变量。
变量只能定义一次,所以,不管怎样,只能有一个extern int i=1这样的定义。
extern可置于变量或者函数前,以表示变量或者函数的定义在别的文件中,提示编译器遇到此变量或函数时,在其它模块中寻找其定义。另外,extern也可用来进行链接指定。
在子程序中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量。
全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序。
当全局变量与局部变量同名时:
在定义局部变量的子程序内,局部变量起作用;在其它地方全局变量起作用。
参考:
谢谢你让学到一点知识。
原创文章,作者:ACOE,如若转载,请注明出处:https://www.506064.com/n/139724.html