【问题标题】:Code works logically on macOS but not on Ubuntu 16.04.5代码在 macOS 上逻辑上工作,但在 Ubuntu 16.04.5 上不工作
【发布时间】:2019-01-31 06:21:47
【问题描述】:

我有个任务要写函数:

int read_palindrome(); // input comes from stdin

将从标准输入中读取一行,如果该行是回文则返回 1,否则返回 0。一行由换行符 ('\n') 终止,并且不包括换行符。

需要满足的条件:

没有关于输入长度的假设。 您也不允许两次读取输入,例如读取输入,忘记读取输入但记住长度,再次读取输入。这导致输入被读取两次。

您也不能创建一个非常大的缓冲区来存储输入推理,即输入行可能会小于一个非常大的缓冲区。 这个限制的原因是我们会考虑程序的内存使用情况。

任务是提出具有最佳 CPU 时间和内存使用率的正确程序。

以下是我的尝试。

file1.c

#include <stdio.h>

extern int read_palindrome();

int main() 
{
    if (read_palindrome()) printf("input is a palindrome");
    else printf("input is not a palindrome");
    return 0;
}

file2.c

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


int check_palindrome2(char *, int);

int read_palindrome() {
    unsigned int len_max = 128;
    unsigned int current_size = 0;
    char *pStr = malloc(len_max); 
    current_size = len_max;
    int i = 0;
    char c;
    if (pStr != NULL) {
        while (( c = getchar() ) != '\n') { 
            pStr[i] = (char)c;
            i++;
            if(i == current_size) {
                current_size += len_max; 
                char *tmp = realloc(pStr, current_size);
                if (tmp == NULL) {
                    free(pStr);
                    return 2;
                }
                pStr = tmp;

            }
        }

        pStr[i] = '\0';
        free(pStr);

    }
    return check_palindrome2(pStr,i);
}   


int check_palindrome2(char *s, int length) {
    for (int i = 0; i < length; i++) {
        if (s[i]!= s[length-i-1]) {
            return 0;
        }
    }
    return 1;
}

将文件复制到两台机器上后,在我的 MacOS 和 Ubuntu 上运行适当的编译命令,并输入已知的回文 121。

gcc -c file1.c
gcc -c file2.c
gcc -o output file1.c file2.c
./output

代码在 MacOS 上打印 input is a palindrome,但在 Ubuntu 上打印 input is not a palindrome。谁能告诉我我的代码是否有任何问题,或者我应该在不同的操作系统上做一些不同的事情。

【问题讨论】:

  • 您的程序释放pStr,然后继续访问它。这是未定义的行为。使用valgrind 或地址清理程序来捕获此类错误。
  • 抱歉,您指的是哪个特定的免费(pStr)?
  • 其中有两个。两者都看是不是太复杂了?
  • 删除free 将解决该特定错误,但会导致内存泄漏。您只需要在最后一次取消引用指针后释放即可。
  • OT:在调用malloc() 和调用realloc() 之后立即检查NULL,如果为NULL,则调用perror( "your error message" );,这将输出您的错误消息以及系统认为发生错误的文字原因stderr

标签: c linux macos memory-management


【解决方案1】:

您的错误处理不一致。 在一种情况下你返回 2,在另一种情况下你间接返回 1。这应该改变:

我将对错误使用负值:

int read_palindrome()
{
    unsigned int len_max = 128;
    unsigned int current_size = 0;
    char *pStr = malloc(len_max); 
    current_size = len_max;
    int i = 0;
    char c;
    if (pStr == NULL)
        return -1;

    while (( c = getchar() ) != '\n') { 
        pStr[i] = (char)c;
        i++;
        if(i == current_size) {
            current_size += len_max; 
            char *tmp = realloc(pStr, current_size);
            if (tmp == NULL) {
                free(pStr);
                return -1;
            }
            pStr = tmp;
        }
    }

    pStr[i] = '\0';
    free(pStr);
    return check_palindrome2(pStr,i);  // If pStr==NULL we do not reach this line.
}

现在在任何错误情况下都返回 -1,如果是 pStr,则不要使用 NULL

让我们解决“释放后使用”问题:

    free(pStr);
    return check_palindrome2(pStr,i);

释放pStr后访问它是非法的。重新排列函数调用。

    int retval = check_palindrome2(pStr,i);
    free(pStr);
    return retval;

除了这些更改之外,您还需要正确处理此函数的返回值:

int main() 
{
    int pali = read_palindrome();

    if (pali < 0)
        printf("An error occured.\n");
    else if (pali) 
        printf("Input is a palindrome\n");
    else 
        printf("Input is no palindrome\n");
    return 0;
}

最后让我们加快你的回文检测速度:

int check_palindrome2(char *s, int length) 
{
    for (int i = 0; i < length / 2; i++)  // only walk up to the middle.
    {
        if (s[i] != s[length-i-1])
            return 0;
    }
    return 1;
}

如果s[1] == s[9] 为真,那么s[9] == s[1] 也为真。无需检查两次。

【讨论】:

  • 感谢您的回答,我认为 LINUX 比 macOS 更严格的结论是否正确?虽然有些代码似乎在 macOS 上始终可以运行,但它们不一定在 LINUX 上运行。
  • 也许,也许不是。访问该内存位置只是未定义的行为。任何事情都有可能发生。这包括它可能只是看起来“按预期工作”。
猜你喜欢
  • 2012-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多