C 为什么这段代码真的有效?

C 为什么这段代码真的有效?,c,string,codeblocks,C,String,Codeblocks,我已经读过一个有点类似的问题(),但它实际上并没有解释为什么这段代码能够实际工作: #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct example { char length[2]; } STRUCT; int main (void) { STRUCT test; strcpy(test.length, "********");

我已经读过一个有点类似的问题(),但它实际上并没有解释为什么这段代码能够实际工作:

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

typedef struct example
{
    char length[2];
} STRUCT;

int main (void)
{
    STRUCT test;
    strcpy(test.length, "********");
    puts(test.length);
    return 0;
}
#包括
#包括
#包括
typedef结构示例
{
字符长度[2];
}结构;
内部主(空)
{
结构测试;
strcpy(测试长度,“*******”);
puts(测试长度);
返回0;
}
我使用代码块来编译它,所以我猜它在我的字符串中分配了更多的空间来存储默认情况下的额外星号。。。我真的不知道。也许我只是幸运,但每次我运行它都能正常工作


在我上面展示的示例(链接)中,他将2个元素放在一个2的数组中,在这里,我使用的空间比字符串可以处理或可以处理的空间多得多。

C不检查数组边界。它会导致缓冲区溢出和未定义的行为,正如Ricky Mutschlechner在评论中所说的那样

它之所以能工作,是因为写入(但未分配)内存不被此应用程序的其他部分使用。我不知道内存分配是如何工作的,以及内存块有多大。 只需搜索更多细节

它不会使操作系统崩溃,但在最坏的情况下会导致分段错误。

这是一个未定义行为的示例(正如人们在评论中提到的)

至于为什么会这样,很可能是因为堆栈对齐。因此,当您声明
STRUCT test
时,即使它只需要堆栈上的两个字节,编译器也会向下对齐堆栈(通过向结构中添加额外的填充),因此在
test
之后,您会得到额外的未使用空间,而strcpy会写入到堆栈中。因为程序的其他部分没有使用这个空间,所以它给人一种工作没有问题的错觉。

你真幸运

该结构只声明为两个字节长,但编译器或运行时可能会分配更多的内存,或者根本不关心
结构
之后的内存位置——事实是
strcpy将覆盖
结构
之后的内存,如果在更大的上下文中使用,它最终将覆盖该内存,重写某些对其他内容至关重要的内存,它将根据您已重写的内存类型及其用途创建灾难性错误


在您的情况下,程序刚刚退出,所以(幸运的是)没有人会使用您损坏的内存。

它只是溢出了堆栈,因为测试在堆栈上。
目前,它可能不会导致任何问题,但如果您在堆栈上声明更多的变量以及测试,则可能会导致一些问题

您正在写入位于
test.length
之后的堆栈内存。为了更好地说明问题,我在代码中添加了两行:

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

typedef struct example
{
    char length[2];
} STRUCT;

int main (void)
{
    STRUCT test;
    unsigned int foo = 42;
    strcpy(test.length, "********");
    puts(test.length);
    printf("%u", foo);
    return 0;
}

是的,你很幸运。在您分配的范围之外写入是未定义的行为,可能会导致任何问题,从完全没有到程序显示和格式错误导致操作系统崩溃(或更糟)。只是运气让它运行起来,运气和运气。欢迎来到未定义行为的世界。这是一种最糟糕的未定义行为——代码可以正常工作。它将持续数年,直到真正令人讨厌的事情发生;有时在CNN或slashdot上结束时,“为什么这样做?”在C中通常不是一个有效的问题。如果一些不应该起作用的东西似乎起作用,那可能是未定义行为的症状,这真的不需要解释,除非您正在寻找一种方法将其作为安全漏洞加以利用。否则,请避免这样做。结构是在堆栈上实现的。超限长度项的结尾意味着数据正在写入堆栈中结构分配位置后面的字节。这意味着堆栈帧已损坏,除其他外。但是,由于调用函数的堆栈中没有任何其他内容(希望取决于编译器),因此它运行时就像一颗炸弹,等待爆炸。从技术上讲,由于它是UB,它可能会使操作系统崩溃。具体取决于操作系统。旧的、小的、嵌入式的——他们会同意,这取决于操作系统。是的,我知道,我只是觉得编译器在这种情况下可能会有所帮助。@matt_s-不,它不会。堆栈溢出是另一种错误。您可能想说“它正在溢出到您的堆栈中”。
********
707406378