为什么我的代码 运行 即使我在 3 大小的数组上分配 4 个值?

Why is my code running even though I'm assigning 4 values on a 3 sized array?

顺便说一句,我对编码还很陌生:)

基本上我正在尝试做一个程序,询问学生他有多少成绩(div),并计算总成绩(nota=grade,im portuguese)。 请注意,在每次尝试中,我总是输入“3”和 div 的值,而且我只显示了部分代码

至于[正确版本],我觉得一切正常,运行没问题。

**[CORRECT VERSION]**

    short unsigned int div, i;
    printf("Pretende fazer a media de quantas notas?(Máximo é 10): ");
    scanf("%hd", &div);
    float nota[div], media, soma = 0;
    for(i = 0; i < div; i++) { //duvida linhas 25-26
        pergunta:
        printf("A %dª nota foi: ", i+1);
        scanf("%f", &nota[i]);
       if((nota[i] < 0) || (nota[i] >20)) {
        printf("\n(Erro: Por favor insira valores de 0 a 20\n");
        goto pergunta;

我的疑问与 for 循环中的 ["NON-SENSE" VERSION] 有关。 我知道我制作 i++,循环的第一个 运行 将假定 i0,但在 for 循环内, i 将是 1。因为array_size=3,三年级(nota)就没有sl​​ot可以分配了(因为nota[0]会被跳过,所以是0吧?,只剩下2个slot),但是仍然循环 运行s 3 次,询问 3 个成绩并给我总体成绩。

**["NON-SENSE VERSION]**

    short unsigned int div, i;
    printf("Pretende fazer a media de quantas notas?(Máximo é 10): ");
    scanf("%hd", &div);
    float nota[div], media, soma = 0;
    for(i = 0; i++ < div;) { //duvida linhas 25-26
        pergunta:
        printf("A %dª nota foi: ", i);
        scanf("%f", &nota[i]);
       if((nota[i] < 0) || (nota[i] >20)) {
        printf("\n(Erro: Por favor insira valores de 0 a 20\n");
        goto pergunta;

然而,将 div 替换为 3,即使它与 div 在先前版本中假定的值相同,我得到了 分段故障(以及一些其他实验总线错误

[VERSION WITHOUT div GIVING A NORMAL ERROR]
    short unsigned int div, i;
    //printf("Pretende fazer a media de quantas notas?(Máximo é 10): ");
    //scanf("%hd", &div);
    float nota[3], media, soma = 0;
    for(i = 0; i++ < 3;) { //duvida linhas 25-26
        pergunta:
        printf("A %dª nota foi: ", i);
        scanf("%f", &nota[i]);
       if((nota[i] < 0) || (nota[i] >20)) {
        printf("\n(Erro: Por favor insira valores de 0 a 20\n");
        goto pergunta;

你能解释一下我遗漏了什么吗?为什么即使是 [“无意义”版本] 运行s,即使它与上一个几乎相同?

这里有很多东西要打开,但让我们按顺序进行。

首先,不要这样做:

unsigned div;
scanf("%hd", &div);
float nota[div], media, soma = 0;

如果您知道学生可以输入的最大分数或使用动态分配,您应该预定义 MAX_SIZE。可以在这个 Whosebug question 中找到有关此的更多信息。 您正在做的是创建一个 VLA - 可变长度数组。这是在 C99 标准中添加的,但使用它会降低可移植性,因为并非所有编译器都遵守该标准。

其次,您正在使用 non-idiomatic for 循环。编程中的一个成语是做某些事情的 de-facto 标准。我们使用习语是因为它减少了出错的机会,并使其他人更容易阅读和维护我们的代码。这就是为什么我们坚持

for(int i = 0, i < n; i++){}

我想您已经意识到这一点(正如您所说的 non-sense),但我只是把它放在那里,以供可能尝试使用类似这样的代码的任何人使用 for(i = 0; i++ < 3;) - 这会给你留下你不想要的讨厌的错误和行为。

结合这两个,我们可以看到这里发生了什么: 在您的 non-sense 示例中,您使用的是 VLA。它的工作方式取决于 ,但我猜测在这种情况下,编译器会查看变量的类型 div - 因为它是一个无符号短整数,所以它知道该值为在 (0 - 65,535)* 范围内,所以这个值比较小。因为它是一个小值,所以它在堆栈上分配了足够的内存来容纳一个最多 ~65k 长的数组。这意味着您可以“安全地”读取和写入该内存而不会出现堆栈溢出,因为 OS 已授予您的程序写入内存中 65k 元素点的权限。

在另一种情况下,您告诉编译器您的数组长度为 3 个元素,因此您的程序仅从 OS 中请求 space 的 3 个元素。 OS 然后不喜欢你尝试从它没有给你的内存中读取和写入,所以你最终会遇到错误。

最后说明: 请不要使用 goto.

*假设 unsigned short 的长度为 16b,这是最小值 - 它可以更长,具体取决于系统。