2

我正在编写一个 C 程序来查找用户输入中最长的行并打印行的长度和行本身。它成功地计算了字符数,但意外地无法存储行本身。也许我误解了 C 的内存管理,有人可以纠正我。

编辑:后续问题:我现在明白,dummy字符后面的块是未分配的,因此计算机可以对它们做任何事情,但是为什么一些字符的存储仍然有效?在我提到的第二个示例中,程序将字符存储在“未分配”块中,即使它“不应该”。为什么?

变量:

  • getchar()c每次我都存储在getchar()
  • igetchar()是我从的当前行的长度(到目前为止)
  • longest_i是迄今为止最长的线的长度
  • twostr指向两个字符串中第一个字符串的开头:第一个表示当前行,第二个表示迄今为止最长的行。当发现一行最长时,将其复制到第二个字符串中。如果未来的行更长,它会覆盖第二个字符串的一些内容,但这没关系,因为我不再使用它了——第二个字符串现在将从更右侧的位置开始。
  • dummy给出twostr一个指向的地方

这就是我如何可视化程序变量使用的内存:

 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|\n| 7|11|15|c |u |r |r |e |n |t |\0|e |s |t |\0|p |r |e |v |l |o |n |g |e |s |t |\0|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

真实的陈述:

&c == 11
&i == 12
&longest_i == 13
&twostr = 14
&dummy = 15

程序:

#include <stdio.h>

int main()
{
    char c = '\0';
    int i, longest_i;
    char *twostr;
    longest_i = i = 0;
    char dummy = '\0';
    twostr = &dummy;

    while ((c=getchar()) != EOF)
    {
        if (c != '\n')
        {
            *(twostr+i) = c;
            i++;
        }
        else
        {
            *(twostr+i) = '\0';
            if (i > longest_i)
            {
                longest_i = i;
                for (i=0; (c=*(twostr+i)) != '\0'; ++i)
                    *(twostr+longest_i+1+i) = c;
            }
            i = 0;
        }
    }

    printf("length is %d\n", longest_i);
    for (i=0; (c=*(twostr+longest_i+1+i)) != '\0'; ++i)
        putchar(c);

    return 0;
}

*(twostr+longest_i+1))直到'\0'是不可预测的。例子:

输入:

longer line
line

输出:

length is 11
@

输入:

this is a line
this is a longer line
shorter line

输出:

length is 21
this is a longer lineÔÿ"
4

7 回答 7

4

您实际上并没有分配任何要写入的内存!

char dummy = '\0'; // creates a char variable and puts \0 into it
twostr = &dummy; // sets twostr to point to the address of dummy

在此之后,您只需写入由 dummy 保留的 char 之后的内存,然后写入 who-knows-what。

在这种情况下,最简单的解决方法是使 dummy 成为一个指向 char 的指针,然后 malloc 一个缓冲区以用于您的字符串(使其比您期望的最长字符串长!)

例如,buffer下面将指向 256 个字节(在大多数系统上)的内存,允许最多 255 个字符长的字符串(因为您在末尾存储了空终止符 (\0))。

char * buffer = (char *)malloc(sizeof(char) * 256);

编辑:这将从堆中分配内存,稍后您应该free(buffer);在完成后通过调用来释放它。另一种方法是按照 Anders K 的解决方案用完堆栈上的空间。

于 2011-12-09T06:23:20.313 回答
2

您没有分配内存来存储getchar. 您的指针twostr是指向字符变量而不是数组的字符指针,但您将其视为指向 char 数组的指针:

char *twostr;
....
char dummy = '\0';
twostr = &dummy;
....
*(twostr+i) = c;  // when i here is > 0 you are accessing invalid memory.

你需要的是这样的:

char *twostr = malloc(MAX);
// use it.
free(twostr);

whereMAX被定义为比用户输入中字符串的最大长度大一。

于 2011-12-09T06:21:00.030 回答
2

你正在砸你的筹码。您只有 1 个字节分配给 char dummy。真的应该是这样的:

字符虚拟[1024];

您还需要确保写入的字节数不超过 1024 或 1023 个字节以允许空终止符。

于 2011-12-09T06:24:32.673 回答
2

是的,您说您误解了 C 的内存管理模型是正确的。

在行

*(twostr+i) = c;

例如,这将是正确的,除了twostr包含字符地址并且仅*twostr指向您拥有的内存的事实。向其中添加任何内容,除了0获取另一个地址和取消引用会产生未定义的行为,因为属于的内存大小dummy为 1 个字节。

因此,长话短说,您需要分配一块内存来存储字符串。最简单的方法是向您展示如何正确执行此操作,因此这里是经过更正的代码:

#include <stdio.h>

int main()
{
    char c;
    int i, longest_i;
    char twostr[1024]; // twostr points to a block of memory 1024 bytes long
    char longest[1024]; // so does longest, where we will store the longest string

    longest_i = i = 0;
    char dummy = '\0';

    while ((c=getchar()) != EOF && i < 1024) // we check that i < 1024 so we don't
                                             // go outside the bounds of our arrays
    {
        if (c != '\n')
        {
            *(twostr+i) = c;
            i++;
        }
        else
        {
            twostr[i] = 0;
            if (i > longest_i)
            {
                longest_i = i;
                for (i = 0; twostr[i] != 0; ++i) { // 0 is the same as '\0'
                    longest[i] = twostr[i];
                    twostr[i] = 0; // fill twostr with NULLs
                }
            }
            i = 0;
        }
    }

    printf("length is %d\n", longest_i);
    for (i=0; longest[i] != 0; ++i)
        putchar(longest[i]);

    return 0;
}

此外,您可视化程序变量的方式是不正确的。它真的是这样的:

堆:

+---------+
|    c    |   1 byte
+---------+
|         |
|         |
|         |
|    i    |   4 bytes
+---------+
|         |
|         |
|         |
|longest_i|   4 bytes
+---------+
|         |
|         |
|         |

~~~~~~~~~~~

|         |
|         |
|  twostr |   1024 bytes
+---------+
|         |
|         |
|         |

~~~~~~~~~~~

|         |
|         |
| longest |   1024 bytes
+---------+
于 2011-12-09T06:32:30.773 回答
1

twostr 指向一个字符,但是您将其视为缓冲区。

您需要做的是制作一个缓冲区,而不是可以容纳更多字符

例如

static char dummy[512];
twostr = dummy;
于 2011-12-09T06:21:09.530 回答
1

首先,您需要确保 twostr 有足够的空间来保存您正在管理的字符串。您可能需要添加一些额外的逻辑来分配初始空间以及在需要时分配额外的空间。就像是:

size_t twostrLen = 256;
char* twostr = malloc(twostrLen);

然后将数据插入其中,如果您的索引将超过 twostrLen 的当前长度,您需要确保分配额外的内存:

if (i >= twostrLen) {
   char* tmp = twostr;
   twostrLen *= 2;
   twostr = malloc(twostrLen);
   memcpy(twostr, tmp, i-1);
   free(tmp);
}

您将要写入i的偏移量在哪里。twostr

最后,当从当前字符串复制到最长字符串时,您的循环终止条件是c=*(twostr+i)) != '\0'. c这将在匹配时触发,在写入终止 null之前'\0'退出循环。您需要确保写入 null 以便循环打印字符串能够正常工作。在最里面的 for 循环之后添加以下内容应该可以解决问题:

*(twostr+longest_i+1+i) = 0;

没有这个,我们的最后一个循环将继续读取,直到遇到空字符。这可能是立即的(如您的第一个示例中所见,它似乎可以工作),也可能是稍后的一些字节(如您的第二个示例,其中打印了其他字符)。

同样,请记住longest_i+1+i < twostrLen在写入该位置之前检查一下。

于 2011-12-09T06:38:56.847 回答
1

试试下面的代码。希望你能得到你预期的结果:

#include <stdio.h>

#define LENGTH 1024

int main()
{
    char c;
    int i, longest_i;
    char twostr[LENGTH]=""; // twostr points to a block of memory 1024 bytes long
    char longest[LENGTH]=""; // so does longest, where we will store the longest string
longest_i = i = 0;
char dummy = '\0';

while ((c=getchar()) != EOF && i < LENGTH) // we check that i < 1024 so we don't
                                         // go outside the bounds of our arrays
{
    if (c != '\n')
    {
        *(twostr+i) = c;
        i++;
    }
    else
    {
        twostr[i] = 0;
        if (i > longest_i)
        {
            longest_i = i;
            for (i = 0; twostr[i] != 0; ++i) { // 0 is the same as '\0'
                longest[i] = twostr[i];
                twostr[i] = 0; // fill twostr with NULLs
            }
        }
        i = 0;
    }
}

printf("length is: %d\n", longest_i);
printf("And the word is: ");
puts(longest);
printf("\n");
return 0;
}
于 2011-12-09T07:24:07.197 回答