为什么我的 const 变量在执行时发生变化?

Why my const variable change on execution?

我有一个随机数生成器,它应该是所有执行的相同数字,但数字发生变化并且不是 4 个数字的偶数?

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
    srand(1); 
    int const numero=rand() % 7000 + 1000;
    char *caracter;

    while (scanf("%s",&caracter)!=EOF){
        printf("%i\n",numero);
    }
    return 0;
}

我使用了一个带有随机名称的 .txt,结果是这样的


masa6144
pupu6144
ogro6144
pelad1oo
pedo0
tucan110
lloron28271
cheso111

正如你所看到的,三个名字的长度都是 4,并且相同,但是在这 3 个都变得奇怪之后,会是什么?

&caracter是指针本身的地址。它可以是 2(AVR)、4 或 8 个字节长。使用指针作为字符数组毫无意义,但您可以在其中存储 1、3 或 7 个字符的字符串。

char *caracter;定义了一个未初始化的指针。

您需要为字符串分配内存。

int main(int argc, char const *argv[])
{
    srand(1); 
    int const numero=rand() % 9000 + 1000;
    char character[100];
    //or char *character = malloc(100);

    while (scanf("%99s",caracter)!=EOF){
        printf("%s",caracter); 
        printf("%i\n",numero);
    }
    return 0;
}

任何半途而废的编译器都会告诉您有问题。不幸的是,许多编译器默认不告诉您是否有任何错误:您需要明确打开警告。使用 GCC 或 Clang,始终使用 -Wall -Wextra -Werror 进行编译。除非调试,否则还要传递 -O 选项,因为没有它会省略一些警告。

$ gcc -O -Wall -Wextra a.c
a.c: In function ‘main’:
a.c:12:21: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘char **’ [-Wformat=]
                     while (scanf("%s",&caracter)!=EOF){
                     ^
a.c:6:14: warning: unused parameter ‘argc’ [-Wunused-parameter]
 int main(int argc, char const *argv[])
              ^
a.c:6:32: warning: unused parameter ‘argv’ [-Wunused-parameter]
 int main(int argc, char const *argv[])

scanf("%s",&caracter)

这会将用户键入的单词存储在从地址&caracter 开始的字符数组中。字符数组需要足够大以容纳单词。如果不是,C 不提供任何保护。这是一个 buffer overflow. The consequences of a buffer overflow can be anything (it's undefined behavior)。如果它到达未分配的内存或只读的内存,这可能会导致崩溃,但它也可以覆盖恰好位于那里的任何内容。确切的行为往往在很大程度上取决于处理器、编译器、周围的代码、编译器如何优化它等。

鉴于您看到的行为,显然,在您的测试中,numero 在内存中就在 caracter 之后,并且 caracter 有 4 个字节的空间。请注意,这是变量 caracter 本身,而不是 caracter 指向的东西。当你输入 masa 时,这会填充 caracter 的四个字节,并在 numero 中写入一个空字节。 6144是256的倍数不是巧合,你的平台是little-endian(所有PC和非古董Mac都是little-endian),所以[=16=的内存表示的第一个字节] 是最低有效字节。因此空字节覆盖了最低有效字节,使数字成为 256 的倍数。

我不知道 pelad1oo 发生了什么(你输入了什么?)。使用 tucan110,很清楚发生了什么:您键入 tucan,它将 tuca 写入 caractern 的 4 个字节,并将一个空字节写入低字节numero 的两个字节,使 numero 的值为 0 * 256 + 'n' 其中 'n' 是字符 n 的值,在 ASCII 中为 110 . 对于 lloron,溢出到 numero 的是 on 和空字节,使得值 0 * 256 * 256 + 'n' * 256 + 0 = 28271。等等。


当您将指针传递给一个函数时,您必须始终确保它指向某个东西(或者它是一个空指针,如果该函数接受空指针)。如果函数需要数组,则必须确保指针指向足够大的数组。如果该函数写入一个字符串,则它是一个包含最后一个空字符的字符数组。

简单的做法是使 caracter 成为一个足够大的数组。

char caracter[100];
…
scanf("%99s", caracter);

注意 caracter 之前没有 &:当数组用作值时,它会自动转换为指向数组开头的指针。并注意 %s 中的 99:这是 scanf 将写入 caracter 的最大字符数(加上一个空字符以结束字符串)。请注意,如果 scanfcaracter 写入一个 99 个字符的字符串,则从用户读取的单词可能不完整。