【问题标题】:An unexplained overflow in CC 中无法解释的溢出
【发布时间】:2026-01-26 08:00:02
【问题描述】:

我正在编写代码来查找输入流中最长的行并将其打印出来。但是,在我定义了一个名为max_count = 0的int之后,我总是发现一个溢出,它显示max_count为1633771873。我已经初始化了那个变量,所以我不知道问题出在哪里。您可能不需要弄清楚所有功能,但每个功能都有其注释。

这是我的代码:

#include <stdio.h>
#define DEFAULT 10

int getline(char line[], int limit);
void copy(char from[], char to[]);
int enlarge(int lim, char s[]);

main() 
{
    int i;
    int max_count = 0;
    char line[DEFAULT];
    char maxline[DEFAULT];
    while ((i = getline(line, DEFAULT)) != 0) {
        if (i > max_count) {    // where weird thing happend (max_count=1633771873)
            max_count = i;
            copy(line, maxline);
        }
    }
    if (max_count > 0) {
        printf("maxline: %s", maxline);
    } else {
        printf("No maxline");
    }
    return 0;
}

/*get a row from input stream and return its length*/
int getline(char s[], int lim)
{
     int i, c;
     for (i = 0; ((c = getchar()) != EOF) && (c != '\n'); ++i) {        
        if (i == lim - 1) {
            lim = enlarge(lim, s); 
        }
        s[i] = c;
     }
     if (c == '\n') {
        s[i] = c;
        ++i;
     }
     if (i == lim) {
        enlarge(lim, s); 
     }
     s[i] = '\0';
     return i;
}

/*copy an array to another */
void copy(char from[], char to[])
{
    int i = 0;
    while (from[i] != '\0') {
        to[i] = from[i];
        ++i;
    }
}

/*expand an array twice as its capacity*/
int enlarge(int lim, char s[]) 
{   
    s[lim - 1] = '\0';
    lim *= 2;
    char temp[lim];
    copy(s, temp);
    s = temp;   
    return lim;
}

这是控制台窗口:

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
^Z
maxline: 
--------------------------------
Process exited after 15.19 seconds with return value 3221225477

【问题讨论】:

  • returns 3221225477 - 你的意思是 prints maxline: 3221225477?请发布您的程序的输出。请将输入发布到您的程序中。
  • 确实如此。这个程序不打印 max_count 那么你怎么知道 max_count 有什么值呢?你在用调试器吗? (没关系,但如果是,请说出来)
  • 如何神奇地将阵列扩展至其容量的两倍?它的长度是10,就是这样。您必须使用 mallocrealloc 来执行此操作。
  • @user253751,是的,我使用了调试器,它告诉我这个值
  • 碰巧你的十六进制“溢出值”是61616161,这是'a'的ASCII值。你有缓冲区溢出。

标签: c ansi-c


【解决方案1】:

您有一个可容纳 10 个字符的缓冲区:

#define DEFAULT 10
char line[DEFAULT];

您输入 37 个字符,包括换行符:

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

您的getline 函数尝试将它们全部存储在line 中(顺便说一句,enlarge 没有做任何有用的事情)。

前 10 个字符适合 line。其他 27 个字符和终止符 '\0' 会覆盖内存中 line 之后的其他随机变量。

这就是为什么max_count 拥有aaaa 的ASCII 代码。

【讨论】:

  • 我的目标是创建一个更大的缓冲区并将内容从较小的缓冲区复制到较大的缓冲区,然后更改指针以引用更大的数组。为什么我的放大功能没有意义?
  • 因为您正在使用堆栈并使用 VLA,所以一旦函数返回您的“新数组”获取无效。
【解决方案2】:

您的enlarge 函数没有按照您的想法执行。

int enlarge(int lim, char s[]) 
{   
    s[lim - 1] = '\0';
    lim *= 2;
    char temp[lim];
    copy(s, temp);
    s = temp;   
    return lim;
}

您正在函数范围内创建一个新数组temp。然后将数组的起始地址复制到s。由于s是函数的参数,修改s不会反映在调用函数中。所以这个函数返回后s中的getline是不变的。

即使您要通过返回 char * 或更改函数以接受 char ** 并将 temp 分配给取消引用的指针来解决此问题,您也会将本地变量的地址返回到 @987654332 @。当函数返回时,该变量超出范围,因此指针将无效。

更改数组大小的唯一方法是先使用malloc 动态分配它,然后再使用realloc 更改其大小。

另外,getline 是 POSIX 系统上的函数名称。您应该将名称更改为其他名称。

【讨论】:

  • 我基本上同意你的观点,但我刚刚了解到,当我将一个数组传递给一个函数时,这意味着我将 s 的引用更改为另一个,因为数组表示的是地址而不是值。所以除了你的第一段,我完全赞成你的回答。我不知道我是否正确。 @dbush
  • @SaeronMeng 是的,当您将数组传递给函数时,实际上是在传递第一个成员的地址。但是,s 仍然是一个局部变量,因此更改 s 不会改变调用者中的任何内容。类型是指针并不重要。如果您取消引用一个指针并修改它所指向的内容,那么调用者将看到这一点。
  • 我明白了。 @dbush