c编译器:数组作为函数参数
我正在为c编译器:数组作为函数参数,c,compiler-construction,C,Compiler Construction,我正在为类c语言编写编译器。目前,编译器支持本地范围内的数组。可以使用括号表示法--a[0]、a[1]、…访问数组的每个元素。为了支持此数据结构,使用符号表跟踪当前范围内的符号以及下一个可用内存空间的地址。为了演示,请考虑下面的代码: inta[5];int b 使用堆栈实现,并给定一个4字节对齐的内存:为了访问,例如元素a[1],我通过 元素=((索引+1)*4)+a.地址;//a、 地址是存储在符号表中的a的地址,在本例中索引为1。 因此,符号表不存储“a”的每个单独元素的地址,只存储符号的
类c语言编写编译器。目前,编译器支持本地范围内的数组。可以使用括号表示法--a[0]、a[1]、…
访问数组的每个元素。为了支持此数据结构,使用符号表跟踪当前范围内的符号以及下一个可用内存空间的地址。为了演示,请考虑下面的代码:
inta[5];int b代码>
使用堆栈实现,并给定一个4字节对齐的内存:为了访问,例如元素a[1]
,我通过
元素=((索引+1)*4)+a.地址;//a、 地址是存储在符号表中的a的地址,在本例中索引为1。
因此,符号表不存储“a”的每个单独元素的地址,只存储符号的地址以及每个符号的下一个内存地址
我假设c语言对本地范围内的数组使用基于堆栈的实现,就像我所做的那样。但是,C语言如何将本地数组作为参数传递给函数,如下所示
foo(int[]a){}
C编译器会使用堆或堆栈来传递前面的数组吗?int[]a
不是有效的参数,您的意思肯定是int a[]
在C语言中,不能将数组传递给函数,并且这种形式:
void foo(int a[]) { ... }
相当于:
void foo(int *a) { ... }
C总是按值传递,通常指针a
的副本存储在堆栈上。int[]a
不是有效的参数,您的意思肯定是int a[]
在C语言中,不能将数组传递给函数,并且这种形式:
void foo(int a[]) { ... }
相当于:
void foo(int *a) { ... }
C总是按值传递,并且通常会在堆栈上存储指针a
的副本。(以下内容与C有关;如果您想为您的语言更改它,请务必执行。)
首先,要意识到不能将数组传递给函数。您只能向函数传递指针,因此当您看到
void f(int a[]) { ... }
它实际上和
void f(int* a) { ... }
既然我已经说过了,我可以说指针是在堆栈上传递的。(下面是关于C的;如果您想为您的语言更改它,那么无论如何,请继续。)
首先,要意识到不能将数组传递给函数。您只能向函数传递指针,因此当您看到
void f(int a[]) { ... }
它实际上和
void f(int* a) { ... }
既然我已经说过了,我可以说指针是在堆栈上传递的。在C中,数组作为函数参数传递时会衰减为指针foo(int a[])
与foo(int*a)
相同,只有指向第一个元素的指针“存活”到函数调用中。无法从函数调用中的指针恢复数组大小。在C中,数组作为函数参数传递时会衰减为指针foo(int a[])
与foo(int*a)
相同,只有指向第一个元素的指针“存活”到函数调用中。无法从函数调用中的指针恢复数组大小。C不会通过其内容,而是通过其地址将数组传递给函数
因此,函数的参数实际上只是一个int*
,需要发送它的值是a.Address
让我们想象一下你的假设语言。如果语言的语义规定数组需要通过其内容发送到函数,则需要使用堆栈,因为函数的参数位于堆栈上
请注意,这带来了另一个复杂性:
让我们考虑这个函数:
int f(int arg1, struct some_struct arg2, float arg3);
让我们调用这个函数中的某个指针,作为指向这个函数堆栈框架的指针。让我们称之为bp
(基本指针)
因此在函数中,您将知道arg1
位于地址bp+8
(例如,arg2位于地址bp+12
,arg3
位于地址bp+36
(假设sizeof(struct some_struct)
为20)
现在,如果按数组的内容发送数组,这个函数怎么样
int f(int arg1, int arg2[], float arg3);
arg1
和arg2
在同一位置,但是arg3
呢?您如何知道arg3
的位置?为此,您需要知道arg2
的大小
不过,有一个解决办法。您可以将数组的大小存储在其前4个字节中(如果您认为数组可能大于4GB,则可以存储8个字节)。然后,您可以通过数组的内容(包括其大小)安全地传递数组。在这种情况下,a[i]
的地址将是a.address+4(或8)+i*sizeof(*a)
您需要考虑以下几个权衡:
- 保留阵列大小的额外内存。在C诞生时,这曾经是一个问题,但现在可能不再是问题了
- 函数调用的速度要慢得多,因为需要复制数组
- 更健壮的代码,因为绑定检查可以在运行时完成。但我个人不喜欢这样,执行速度会变慢,而且程序也不应该超出数组边界!不过,对于调试模式,它可能是一个非常有用的选项
- 一个更好的
sizeof
操作,用于实际给出数组大小的数组。这对于只接收数组而没有其大小的函数很有用<例如,代码>strlen将是O(1)
- 指向数组中间的指针将是无用的。考虑合并排序
- 等等
C不是通过数组的内容,而是通过其地址将数组传递给函数
因此,函数的参数实际上只是一个int*
,需要发送它的值是a.Address
让我们想象一下你的低血压