我2年前开始编程,当我编程并且我沉默时,有一个问题让我不知所措。
我理解微处理器架构和低级编程的基础知识,我知道没有数据类型这样的东西。它只是限制数据处理方式和控制内存资源的抽象。
所以我知道这是一个深刻且有点不清楚的问题,但希望你能理解这个难题中的部分,让我无法理解高级编程与硬件实际内容之间的联系。
所以我的问题是:数据类型到底是什么,在何时何地实施?
答案 0 :(得分:4)
数据类型是语言语义的一个元素。它是一组关于什么样的信息可以由语言中的变量表示的规则,以及适用于这些类型信息的转换。
它是在语言的编译器或解释器中实现的。在编译语言中,它在编译时实现。在解释语言中,它是在运行时实现的 - 在“初始解析过程”期间应用的一些规则,以及在执行期间根据语言的语义操作数据本身时应用的一些规则。
根据OP的评论进行详细说明:
正在进行的具体示例可能是在C:
中处理此代码
int i = "foo";
C编译器首先了解这一点,并得出结论它有一个关键字后跟一个标识符,后跟一个运算符后跟一个常量。从语法上讲,它确定它是一个初始化语句。然后进行语义分析并确定它被要求将字符串常量赋给整数变量。此时,它得出结论,这在语义上不允许,因为不允许整数数据类型具有字符串值。 C编译器为此产生一个错误语句,不产生输出代码,没有汇编,没有二进制。
数据类型的效果是导致编译停止。
数据类型的实现位于C编译器本身 - 编译器的代码/逻辑中。
您无法在程序本身的“汇编代码”中“看到”数据类型。它们存在于实现语言(编译器或解释器)的机制中,而不是在生成的程序中。
因此,没有“一段汇编代码说明数据类型”。
答案 1 :(得分:2)
仔细考虑C
在The History of the C Language中,它说Dennis Ritchie提出C
的原因之一是B
( UNIX的语言大部分是在C
之前写的}因为打字真的很弱,所以Dennis Ritchie"转向"通过添加类型和结构将B
语言转换为C
语言。
B语言的一个缺点是它不知道数据类型。 (一切都用机器词表达)。 B语言没有提供的另一个功能是使用“结构”。这些事情的滞后形成了Dennis M. Ritchie开发编程语言C的原因。
我会尽快覆盖这个......
查看一个典型的x86 32位寄存器,例如eax
,你有;
00-00-00-f0h <- A bit-mask just to add some bits
扩展为;
**** <-- [nybble] 4 bits
0000-0000 0000-0000 0000-0000 1111-0000b
^^^^^^^^^
^ah ^^^^^^^^^ <-- [byte] 8 bits
^al
^^^^^^^^^^^^^^^^^^^^ <-- [word] 16 bits
^ax
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <-- [dword] 32 bits
^eax
dword word byte
是您可以通过指令实际操作的大小,这些(在某种程度上)是装配级编程中的基本类型,但这些只是大小,这还不够,我们会喜欢使用类型来表示各种事物,而不仅仅是数据的大小,例如characters
,我们如何判断位模式是number
还是字符串characters
,更好的是,我们如何判断number
是signed
还是unsigned
,你不能,特定的位模式只有在你使用它的任何环境中才有意义在,这可能导致错误和令人困惑的代码,因此更高级别的语言实现类型,以帮助使数据保持意义,并有助于防止难以找到错误。
在C
中,假设我们在char
类型的字符串中等于&#34; hello world&#34;,键入char *
,如果我们在调试器中打开它并首先检查一些指令和记忆,也许我们可以对此有所了解。
使用GDB
检查我们获得的main
函数中的前8条指令;
(gdb) x/8i $eip
=> 0x4015d3 <main+3>: and esp,0xfffffff0
0x4015d6 <main+6>: sub esp,0x10
0x4015d9 <main+9>: call 0x401ff0 <__main>
0x4015de <main+14>: mov DWORD PTR [esp+0xc],0x409064
0x4015e6 <main+22>: mov eax,0x0
0x4015eb <main+27>: leave
0x4015ec <main+28>: ret
0x4015ed <main+29>: nop
注意这个mov DWORD PTR [esp+0xc],0x409064
这个地址(0x409064
)被移入堆叠(esp+0xc
)的是什么?
好吧,如果我们检查一下我们得到的地址;
(gdb) x/s 0x409064
0x409064 <__register_frame_info+4231268>: "hello world"
这是我们的字符串在内存中开始的地址,所以当我们在char *
中创建一个类型C
时,我们实际上是将一个指向数据的指针存储到堆栈中,然后当我们引用该类型我们只需要从堆栈中获取它的地址,关于地址的好处是我们不需要为堆栈上的每个地址多于32位(dword
),无论类型大小。
我可以假设C
在创建单个char ch = 'a'
时做同样的事情,让我们检查一下;
(gdb) x/8i $eip
=> 0x4015d3 <main+3>: and esp,0xfffffff0
0x4015d6 <main+6>: sub esp,0x10
0x4015d9 <main+9>: call 0x402000 <__main>
0x4015de <main+14>: mov DWORD PTR [esp+0xc],0x409064
0x4015e6 <main+22>: mov BYTE PTR [esp+0xb],0x61
0x4015eb <main+27>: mov eax,0x0
0x4015f0 <main+32>: leave
0x4015f1 <main+33>: ret
不,它不会将指针存储在堆栈上
好了改变它,让我们在变量被压入堆栈后快速检查堆栈;
注意:gdb调用words
我所谓的dword
,所以当我要求5 hex words (5xw)
时,我的意思是5 hex dwords
,这就是我得到的。
(gdb) x/5xw $esp
0x28fea0: 0x00401f80 0x00000000 0x61000023 0x00409064
0x28feb0: 0x00000023
查看第一行dwords
上的最后两个0x61000023 & 0x00409064
:
0x00409064 是我们数据的地址(char *)
0x61000023 这个dword需要松散几个字节才有意义。忽略000024
我们留下0x61
&#39; a&#39;的ascii值。
编译器存储了&#39; a&#39; | 0x61作为数据本身紧挨着我们在堆栈esp+0xb = char
和esp+0xc = (char *)
上的字符串,正如您在C
中看到的(类似于汇编)类型与大小和大量密切相关如果很难确定类型的大小C
似乎使用指针(这是寄存器的大小),那么工作由编译器完成,否则如果它是可以确定大小的类型,则编译器只是将数据放在堆栈上。
(通过确定我的意思是控制)
从所有那些我只检查char
s !!!!
我确信他们在C
中只有许多其他方式可以实现不考虑所有其他语言以及他们可能采用的所有其他方式。
无论如何,我希望能帮助你解决一些事情,而且我并没有搞砸任何事情。
额外信息:
快速搜索compiler design
i found this pdf
有关任何语言的信息,我觉得我应该推荐你的标准;
这是C's standard
查找语言信息的另一种快捷方法是:
为google search
[x language's] documentation
有关types
i found this paper的具体信息。
我如何找到最后一篇论文是另一种查找信息的好方法;
为您正在寻找的任何内容执行wiki search
,并在页面底部查看further reading
以及页面上的任何引用。
现在关于程序集代码部分;
您可以而且应该使用调试器并检查自己的工作方式。
这个名为Beej's quick guide to GDB的指南似乎是GDB
快速:在-S
中编译C
程序时包含gcc
标记,将为您提供实际的汇编代码列出一个程序;
即gcc -S file.c
将为您file.s
填充汇编代码,添加-masm=intel
标志以将语法从AT&amp; T更改为Intel's。
请记住,编译器并没有尝试编写你的程序,所以人类会理解它们,所以一开始你可能看起来有些疯狂!