导航:首页 > 源码编译 > 编译器使用顺序

编译器使用顺序

发布时间:2022-12-13 06:25:11

A. C语言程序的运行顺序

1、这个涉及到函数的调用约定
运行结果跟编译器有一定的关系,不同的编译器参数的入栈的顺序不同
一般的编译器是从右到左
如fun(a,b)这个函数调用,是先计算参数b,入栈,再计算参数a,入栈
2、printf("%d
%d",
a++,++a);
//先计算++a,先自增,a的值变为2,将2入栈
再来计算a++,将a的值2入栈,再使a自增,a的值变为3
printf("
%d\n",a);
//a的值已经变为3了
3、printf(%d
%d",
++a.a++);//先计算a++,将a的值1入栈,再使a自增,a的值变为2,再来计算++a,先自增,a的值为3,将3入栈,输出3
1
printf("
%d\n",a);
//输出3
4、三种调用约定:
__stdcall调用约定。两者实质上是一致的,即函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈,但不同的是函数名的修饰部分(关于函数名的修饰部分在后面将详细说明)。
C调用约定(即用__cdecl关键字说明)和__stdcall调用约定有所不同,虽然参数传送方面是一样的,但对于传送参数的内存栈却是由调用者来维护的(也正因为如此,实现可变参数的函数只能使用该调用约定),另外,在函数名修饰约定方面也有所不同。
__fastcall调用约定是“人”如其名,它的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用CX和EDX传送前两个双字或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。

B. 编译器生成的汇编语句执行顺序为什么与C代码顺序不同

不影响语义的前提下编译器可以任意重排代码顺序;
在乱序执行(Out-of-Order)的CPU里,机器码的执行也可以不按照你在“汇编”层面上看到的顺序执行,只要不影响语义。
所以说这些中间步骤的顺序,作为底层细节平时不需要那么在意——它们多半跟原始源码的顺序是不一样的。

现代优化编译器优化的思路之一是“基于依赖的优化”(dependence-based optimization)。题主引用的CSAPP的例子:

int arith(int x, int y, int z) {
int t1 = x + y;
int t2 = z * 48;
int t3 = t1 & 0xFFFF;
int t4 = t2 * t3;
return t4;
}

所有涉及运算的值都是局部标量变量(local scalar variable),这是最便于编译器做分析的情况,所有依赖都可以显式分析。
由于整个函数没有分支,这里也不需要讨论控制依赖(control dependence),只要讨论数据依赖(data dependence)就好。
把数据依赖图画出来是个DAG(这里正好是棵树,特例了):

x y z 48
\ / \ /
t1 0xFFFF t2
\ / /
t3 /
\ /
t4

优化必须要满足的约束是:每个节点求值之前,其子节点(依赖的数据源)必须要先求了值。
显然,t1和t2之间没有依赖关系,它们的相对求值顺序怎样重排都没关系。

有本我很喜欢的书,里面讲的是各种基于依赖的优化:Optimizing Compilers for Modern Architectures - A Dependence-based Approach

以上是理论部分。

================================================================

下面来看例子。

我们可以用一个实际编译器来看看CSAPP的例子编译出来的结果:

.text
# -- Begin arith
.p2align 4,,15
.globl arith
.type arith, @function
arith:
.p2align 4,,7
/*.L0:*/ /* Block BB[54:2] preds: none, freq: 1.000 */
movl 8(%esp), %edx /* ia32_Load T[139:10] -:1:22 */
addl 4(%esp), %edx /* ia32_Add Iu[141:12] -:2:14 */
movzwl %dx, %edx /* ia32_Conv_I2I Iu[142:13] -:4:15 */
imull 12(%esp), %edx /* ia32_IMul Iu[143:14] -:5:15 */
leal (%edx,%edx,2), %eax /* ia32_Lea Iu[144:15] -:5:15 */
shll $0x4, %eax /* ia32_Shl Iu[146:17] -:5:15 */
ret /* ia32_Return X[152:23] -:6:3 */
.size arith, .-arith
# -- End arith

这里用的是libFirm。可见它跟CSAPP书里所说的汇编的顺序又有所不同。这也是完全合理的。
这个编译结果的顺序是:

edx = y;
edx += x;
edx = zeroextend dx; // edx = edx & 0xFFFF
edx *= z;
eax = edx * 3;
eax <<= 4; // eax = eax * 16

也是完全符合依赖关系的约束的一种顺序。
之所以用libFirm举例是因为它的中间表示(Intermediate Representation)是一种程序依赖图(Program Dependence Graph),可以很方便的看出控制与数据依赖。把CSAPP那里例子对应的libFirm IR画出来,是这个样子的:
(这张图跟我前面画的数据依赖图正好是左右翻转的,不过意思一样。(这张图跟我前面画的数据依赖图正好是左右翻转的,不过意思一样。
Arg 0、1、2分别代表x、y、z。白色方块是普通数据节点,黄色方块是常量节点,蓝色方块是内存相关节点,红色方块是控制流节点,粉红色方块是特殊的开始/结束节点。)

某版LLVM生成的代码:

; MoleID = '/tmp/webcompile/_16355_0.bc'
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-ellcc-linux"

; Function Attrs: nounwind readnone
define i32 @arith(i32 %x, i32 %y, i32 %z) #0 {
entry:
%add = add nsw i32 %y, %x
%mul = mul nsw i32 %z, 48
%and = and i32 %add, 65535
%mul1 = mul nsw i32 %mul, %and
ret i32 %mul1
}

attributes #0 = { nounwind readnone "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.ident = !{!0}

!0 = !{!"ecc 0.1.10 based on clang version 3.7.0 (trunk) (based on LLVM 3.7.0svn)"}

最终生成的x86汇编:

.text
.file "/tmp/webcompile/_15964_0.c"
.globl arith
.align 16, 0x90
.type arith,@function
arith: # @arith
# BB#0: # %entry
movl 8(%esp), %eax
addl 4(%esp), %eax
movzwl %ax, %eax
imull 12(%esp), %eax
shll $4, %eax
leal (%eax,%eax,2), %eax
retl
.Ltmp0:
.size arith, .Ltmp0-arith

.ident "ecc 0.1.10 based on clang version 3.7.0 (trunk) (based on LLVM 3.7.0svn)"
.section ".note.GNU-stack","",@progbits

GCC 4.9.2 x86-64:

arith(int, int, int):
leal (%rdx,%rdx,2), %eax
addl %edi, %esi
movzwl %si, %esi
sall $4, %eax
imull %esi, %eax
ret

Zing VM Server Compiler x86-64:

# edi: x
# esi: y
# edx: z
movl %edx, %eax
shll $0x4, %eax
leal (%rsi, %rdi, 1), %ecx
shll $0x5, %edx
addl %edx, $eax
movzwl %ecx, %edx
imull %edx, %eax

C. 编译器生成的汇编语句执行顺序为什么与C代码顺序不同

你知道“C代码”执行顺序?C源码文件只有书写顺序,而且书写顺序与形成可执行文件的顺序本来就不是完全同的。随便举个例子,如for(i=0;i<N;i++){printf("%d ",i);printf("%d ",i+3);}中的i++在哪里执行?它可不是在i<N后就执行,而是在两个printf后的}处才执行,想与}分开都不可能!再如,在main前自定义一个函数,在main中调用这个函数时,流程又跑到哪里去了?是不是应该理解为从后面跑到前面去执行了?……所以,谈论C代码的执行顺序基本是一个伪命题!

D. c++编译器以何种顺序编译文件的,先cpp文件,还是.h文件

1. 编译阶段 (头文件 .h)
d工程中在头文件中对导出内容(function, class, type, object, variable)进行定义.

2. 链接阶段 (库文件 .lib)
d工程在link阶段会生成.lib
用户link时需要 这个.lib 解决link时的代码定位.
3. 运行阶段
.exe

E. C语言程序的运行顺序

for多重循环的执行顺序是先执行内部循环,再执行外部循环

通过对代码进行等价转换,按照上述原理即可知道代码的执行顺序

对于上面代码:

for(i=0;i<8;i++)
for(j=0;j<10;j++)
scanf("%d,%d",&i,&j);

它等价于下面

for(i=0;i<8;i++){
for(j=0;j<10;j++){
scanf("%d,%d",&i,&j);
}
}

从上面可以看出,每执行完一次j循环,i的值才增加1

但是注意,该代码有一个陷阱:scanf函数会改变i和j的值

实际执行顺序会受用户输入的i、j值的不同而不同

对于下面的代码

for(i=0;i<8;)
min_m(i++)=i
for(j=0;j<8;j++)

它等价于下面的代码:

for(i=0;i<8;){
min_m(i++)=i
}
for(j=0;j<8;j++){}

会先执行min_m函数8次 然后再执行j循环

注意,该代码有一个问题,就是min_m函数的返回值必须是一个左值。

这意味着某些变量的值发生了更改,可能会影响到循环。

F. 编译器生成的汇编语句执行顺序为什么与C代码顺序不同

这是开启了编译器优化是AT&T汇编,其实这里也没做多少功能或者并非说多么智能。只是把同一个变量的操作汇聚在一起了,不用来回的重复访问。与之类似的是处理器优化有用到Cache line。就是一次从主存取尽可能多的数据以提高缓存命中率。举个俗的例子,就比如你去接个水再顺便上个厕所而往往不会先接完水回来又跑去上厕所再回来。

G. stm32cubeide设置文件编译顺序

对一个加法函数的库项目的建立和编译整个过程如下:
1、这里就要选择ARMMCU的GCC编译器了。
2、选择和MCU型号的绑定关系了,毕竟编译的库是给MCU项目所用,这里选择STM32F401CCU6的芯片。
2、添加源文件编写加法函数,编写加法函数进行编译。
3、编译成功,在工程目录里就可以找到编译好的库文件了,库文件的调用,配置一个对应MCU的基本工程,建立一个STM32F401CCU6的工程,并配置USART2作为串口输出。
4、保存后产生基本工程代码,重载printf函数方便打印输出,参考STM32UART串口printf函数应用及浮点打印代码空间节省。
5、在工程的core/inc/目录新建一个和库文件同名的头文件,将库文件libLib_C_Demo.a放置到工程的源文件目录core/src/,则在工程目录树立可以看到。
6、因为编译器默认只是识别C语言源代码(.c文件),还需要进行库文件的指定,这样编译器才会对二进制库文件(.a)进行识别。
7、先打开属性菜单,进行库文件所在目录和库文件名的添加指定,注意库文件名前需要加冒号,ApplyandClose后,就可以在工程文件里对库文件进行调用,首先要引入库文件的头文件。
8、在while循环里进行打印输出,打印数据为库函数调用的加法和,编译下载到STM32F401CCU6芯片后运行,通过串口工具观察打印结果,输出打印结果正确,库函数正常调用成功。

H. java编译顺序的问题

存在父类时对象的实例化先编译父类;
如果子类没有实现父类的接口,属于语法错误,无法通过编译;
子类没有实现父类的接口是子类的错,父类没有错,即使不存在子类,父类也是可以编译得过的。
你的答案应该选B:
但是,实际编译是这样:
如果你直接javac Square.java,语法检查未通过,编译器将不工作;而正确编译时将得到父类与子类的class文件。
如果你先javac Shape.java,将得到Shape.class,再执行javac Square.java(未实现接口的)时,你仍将只会看到Shape.class。

I. 如何让编译器按代码顺序执行(((急急急)))

问题是你do的值就是c.me提供的,想要优化那就要考虑把do(c.me())这整个逻辑进行优化

J. 编译器生成的汇编语句执行顺序为什么与C代码顺序不同

编译器不仅是无序的,而且还展开、合并、删除代码(如果没有发现代码),,,C语言是好的,和C++编译器编译,经常连上帝也不知道结果。


暂停现代CPU为了弥补CPU和内存之间的速度差距的接入方案,以所谓的缓存(L1,L2,L3;在CPU芯片的速度比内存快得多,但容量很小),CPU缓存中常见的事情似乎保持镜子,需要在缓存中直接读取使用的数据是好的,比内存快几倍的速度。

注意名字的区别。名称风险是两个没有数据流的指令,数据冒险是两个指令之间的数据流。

阅读全文

与编译器使用顺序相关的资料

热点内容
python最常用模块 浏览:182
温州直播系统源码 浏览:110
程序员在上海买房 浏览:382
生活解压游戏机 浏览:907
季羡林pdf 浏览:716
php支付宝接口下载 浏览:814
ipad怎么把app资源库关了 浏览:301
量柱比前一天多源码 浏览:416
电子书app怎么上传 浏览:66
国家反诈中心app注册怎么开启 浏览:804
全波差分傅里叶算法窗长 浏览:41
程序员如何讲自己做过的项目 浏览:7
程序员要看的书颈椎 浏览:946
php文章cms 浏览:553
CSS权威指南第三版PDF 浏览:496
android怎么搭建框架 浏览:184
正宗溯源码大燕条一克一般多少钱 浏览:917
电脑感染exe文件夹 浏览:916
wpsppt怎么转pdf格式 浏览:88
腾讯文档在线编辑怎么添加密码 浏览:880