2017-06-02 26 views
7

见本测试程序:为什么我可以进入alloca:d变量的范围,但不是可变长度的数组?

#include <stdio.h> 
#include <string.h> 

int main(int argc, char *argv[]) 
{ 
    if (argc < 2) 
    goto end; 

    char s[strlen(argv[1]) + 1]; 
    strcpy(s, argv[1]); 
    printf("s=%s\n", s); 

end: 
    return 0; 
} 

它失败,错误“跳与可变类型的标识符的范围”来编译(见other question)。

但是它编译罚款,如果我改变的s申报到这一点(包括alloca.h):

char *s = alloca(strlen(argv[1]) + 1); 

为什么C标准允许跳跃与012​​创建的对象的范围,而不是可变长度数组?我认为它们是相同的。

+1

'alloca'不在标准中,因此编译器可以像处理任何其他函数一样对待它(尽管它们[通常不会]](http://man7.org/linux/man-pages/man3/alloca.3 .html#NOTES)),在这种情况下,根据标准,这一切都很好。虽然,它可能会导致类似的未定义行为 – Kninnug

回答

2

这是因为编译器必须运行时用VLA初始化范围的框架。换句话说,你告诉它跳转到地址:END,但你要求它跳过该范围框架的初始化代码。

用于初始化VLA空间的代码就在计算VLA长度的表达式之前。如果你跳过那些可以执行的代码,所有的程序都会出现段错误。

想象一下这样的:

if (cond) goto end; 
... 
char a[expr]; 
end: 
a[i] = 20; 

在这种情况下,代码只会段错误,因为你跳到VLA一个的突变,但一个未初始化。初始化VLA的代码必须插入到定义的位置。

现在约为alloca。编译器会做同样的事情,但它无法检测到段错误。

所以这将segfault,编译器的部分没有警告/错误。

逻辑与VLA相同。

int main(int argc, char *argv[]) 
{ 
    goto end; 
    char *s = alloca(100); 
end: 
    s[1] = 2; 
    return 0; 
} 

在ISO 9899,这就是为什么他们插入语句:

6.8.6.1 goto语句 - 约束

1 goto语句的标识应位于命名一个 标签封闭函数中的某处。 goto语句不得从 类型的标识符范围之外跳到 以内到该标识符的范围内。

编译器在静态分析过程中无法检测到此问题的正确答案,因为这实际上是halting problem

+0

所以没有技术上的原因('alloca'也可能是'malloc')来禁止它,只是C标准试图使VLAs适合静态分析,可能是基于它们被静态声明的(但无论如何,由于它们的动态尺寸,它们并不完全适合)。在我看来,标准化'alloca'而不是VLA会是一个更好的决定。 – PSkocik

+1

@PSkocik在我看来,语言定义所强加的这种限制是避免“停止问题”的“蛮力”方式。 – alinsoar

2

除了如果程序在其声明之后跳过VLA而解除分配问题,还存在sizeof的问题。

想象一下你的计划扩展这一点:

end: 
    printf("size of str: %zu\n", sizeof s); 
    return 0; 
} 

对于alloca版本,sizeof s == sizeof(char*),它可以在编译时和所有的计算都很好。但是,对于VLA版本,s的长度未知,并且sizeof s不能计算出

+0

你的观点很好,我想补充一点,'sizeof'的值实际上不是在sizeof'调用的位置,而是在声明的位置计算的,sizeof的值也存储在堆栈/一些寄存器,所以在sizeof之前跳跃并在定义的位置跳过初始化代码使得打印尺寸变得很大等等。 – alinsoar

+0

@alinsoar:您能否提供任何关于VLA的大小被储存了? –

+0

int a [m]; m ++; sizeof(a);大小是在定义时计算的,因此存储在某处。 – alinsoar

相关问题