例如:在下面的代码中,如何以及在何处存储用于比较的数字“10”?
#include<stdio.h>
#include<conio.h>
int main()
{
int x = 5;
if (x > 10)
printf("X is greater than 10");
else if (x < 10)
printf("X is lesser than 10");
else
printf("x = 10");
getch();
return 0;
}
请原谅我没有提供足够的细节。如果我们扫描并从用户那里获取x,我们就知道如何为x分配内存,而不是直接用5初始化x。但是,如何为没有存储在任何变量中的字面数“10”分配内存呢?
常数10可能作为立即常数存储在操作码流中。发出操作码中包含常数的CMP AX,10
,通常比CMP AX、[BX]
小且快,后者必须从内存加载比较值。
如果常量太大,无法放入操作码,另一种选择是像静态变量一样将其存储在内存中,但是如果指令集允许嵌入常量,一个好的编译器应该使用它——毕竟,寻址模式可能是因为它比其他人更有优势。
在您的特定代码中,x
被初始化为5,并且从未更改。优化编译器能够不断折叠和传播这些信息。因此,它可能会产生相当于
int main() {
printf("X is lesser than 10");
getch();
return 0;
}
请注意,编译器也会进行死代码消除。
所以常数5和10都消失了。
BTW,
Kilian回答说,一般来说(取决于目标处理器的指令集和ABI),小常量通常嵌入在某些单一机器代码指令中(作为直接操作数)。一些大常量(例如浮点数、文字字符串、most
const
global或static
数组和聚合)可能被插入并编译为代码段中的只读数据(那么机器寄存器加载指令中的常量将是一个地址或相对于PIC PC的某些偏移量);另见此。某些体系结构(例如SPARC、RISC-V、ARM和其他RISC)能够通过两条连续指令在寄存器中加载宽常量(将常量分为两部分加载),这会影响链接器的重新定位格式(例如,在对象文件和可执行文件中,通常在ELF中)。
我建议让编译器发出汇编代码,并浏览一下汇编代码。如果使用GCC(例如,在Linux上,或与Cygwin或MinGW一起),请尝试使用
GCC-Wall-O-fverbose asm-S
进行编译;在我的Debian/Linux系统上,如果我将代码中的getch
替换为getchar
,我将得到:
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "X is lesser than 10"
.text
.globl main
.type main, @function
main:
.LFB11:
.cfi_startproc
subq $8, %rsp #,
.cfi_def_cfa_offset 16
movl $.LC0, %edi #,
movl $0, %eax #,
call printf #
movq stdin(%rip), %rdi # stdin,
call _IO_getc #
movl $0, %eax #,
addq $8, %rsp #,
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE11:
.size main, .-main
.ident "GCC: (Debian 4.9.2-10) 4.9.2"
.section .note.GNU-stack,"",@progbits
如果您使用的是64位Windows系统,那么您的体系结构很可能是x86-64。有大量的文档描述ISA(请参阅本文的答案)和x86调用约定(以及Linux x86-64 ABI;您将找到适用于Windows的等效文档)。
顺便说一句,您不应该真正关心这些常量是如何实现的。无论编译器为实现它们而选择做什么,代码的语义都不应该改变。因此,将优化(以及此类低级选择)留给编译器(即您的C实现)。
我有一个c模块: 我做了File fileName; 我有一个初始化函数: 所以我做了fileName=filename;我这样做的原因是我有另一个函数,我称为start(): 一开始我有文件名,但它没有找到它,所以我想用fileName代替。但我现在得到一些错误: 在这一行:fileName=fileName;在=符号上,我得到红线错误: 错误1错误C2440:“=”:无法从“常量字符*”转换为
推荐首先阅读 内存管理 Objective-C 中的内存分配 在 Objective-C 中,对象通常是使用 alloc 方法在堆上创建的。 [NSObject alloc] 方法会在对堆上分配一块内存,按照NSObject的内部结构填充这块儿内存区域。 一旦对象创建完成,就不可能再移动它了。因为很可能有很多指针都指向这个对象,这些指针并没有被追踪。因此没有办法在移动对象的位置之后更新全部的这些指
作为示例,我们的高级开发人员编写了以下代码: (还有一个“Token.cs”类,它只有一个name属性作为string。) 我们解码的JWT负载如下所示: 我遇到的问题是,当我尝试按“sub”类型获取索赔时,什么都没有出现(而且它不在列表中)。但“sub”似乎是一个极为常见的说法。 我做错了什么?我能拿到主题(“sub”)索赔吗? 编辑:适用于推荐系统。IdentityModel-我在尝试使用它时
1. 函数的声明和定义 函数头与函数体两部分组成。其中函数头部分包含函数的返回值类型,函数名,函数的参数;函数体部分由实现函数功能的一条至多条语句组成。 返回值类型 函数名(函数) { 程序语句 } 使用函数要先定义再使用。 2. 示例程序 #include <stdio.h> int week(int w) { switch (w) { case 1:
以下节目旨在复制一个基本的酒店客房预订方案。创建hotel number的实例后,通过调用checkIn()方法,程序检查是否有可用的房间,如果有,则保留房间。 但是,如果在分配完所有房间后,有人从一个房间(通过方法)退房,例如1号房间,然后试图入住该房间,则该方法无效。当调用hasRoomsAvailable()方法时会出现此问题,在这种情况下,由于currentRoomNumber是5,该方法
本文向大家介绍C语言实现超市管理系统,包括了C语言实现超市管理系统的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家分享了C语言实现超市管理系统的具体代码,供大家参考,具体内容如下 超市管理系统 1.包括管理员和顾客 2.管理员有登入,录入商品信息,显示所有商品信息功能 3.顾客有购物车和结算功能 4.购物车具有显示商品列表,显示购物车商品,往购物车添加商品的功能 函数 建立库存函数 在购物