C程序在Mac OSX和Linux上表现不同

时间:2018-05-01 07:27:34

标签: c linux macos unix ncurses

我编写了一个程序,可以创建一个侧面滚动选框效果,它可以在我的Mac终端上完美运行。但是,当我在我的树莓派(Raspbian Linux)上运行它时,效果会混乱,并开始在新行上打印,并且不会滚动整个文本长度。谁能弄明白问题是什么?我已经试了几天了。

// Compile: gcc marquee.c -o marquee -lncurses
// Usage: ./marquee filename length row col speed
// Reads text from a file and displays 'length' number of chars 
// scrolling sideways at a given 'row, col' position at some indicated 'speed'

#include    <curses.h>
#include    <string.h>
#include    <unistd.h>
#include    <stdio.h>
#include    <stdlib.h> 
#include    <fcntl.h>

#define ROW 10

int main(int ac, char *av[])
{
    if(ac != 6){
        printf("marquee [fileName] [row] [col] [speed (1-99)]\n");
        perror("Insuffecient argument count\n"); 
        exit(1); 
    }

    char message[256];
    int text_length;
    int i;
    int k;  
    int orgPos = atoi(av[4]);
    int pos;
    int row = atoi(av[3]);  
    int dir = 1; 
    int maxPos = atoi(av[2]);

    int speed = atoi(av[5]);

    int filedesc = open(av[1], O_RDONLY); 

    if(filedesc < 0) {
        perror("Could not open file");
        exit(1); 
    } 

    if(speed < 10) 
        speed = 500000; 
    if(speed >= 10 && speed < 20) 
        speed = 250000; 
    if(speed >= 20 && speed < 30) 
        speed = 120000; 
    if(speed >= 30 && speed < 40) 
        speed = 100000; 
    if(speed >= 40 && speed < 60) 
        speed = 80000; 
    if(speed >= 60 && speed < 70) 
        speed = 60000; 
    if(speed >= 70 && speed < 80) 
        speed = 40000; 
    if(speed >= 80 && speed < 90) 
        speed = 20000; 
    if(speed >= 90 && speed < 95) 
        speed = 10000; 
    if(speed >= 95 && speed <= 99) 
        speed = 5000; 

    int bytesRead = 0; 
    while(bytesRead == read(filedesc, message, 256) > 0){

    }

    // Get text length
    text_length = strlen(message);

    initscr(); // initialize curses
    clear();
    curs_set(0);

    while(1) {
        clear(); // clear last drawn iteration

        pos = orgPos;

        char * scroll;
        scroll = malloc(2*maxPos);

        for(i = 0; i<2*maxPos; i++) 
        {
            scroll[i] = message[i%text_length];
        }

        for(i = 0; i < 1000; i++){
                mvaddnstr(row, orgPos, &scroll[i%maxPos], maxPos);
                usleep(speed);
                refresh();
        }
    }

    endwin();

    if(close(filedesc) == -1) 
    {
        perror("Error closing file"); 
        exit(1); 
    }
}

这里肯定会有很多改进,但请让你的调试目标弄清楚为什么这不能在linux上正常运行。以下是一个示例测试用例:

$ ./marquee scroll.txt 25 0 0 50

scroll.txt包含以下内容:

Hello, this is a test for the scrolling marquee. 

1 个答案:

答案 0 :(得分:5)

您有automatic variable char message[256];。你没有明确地初始化它,所以它在开始时包含垃圾。很难(或甚至不可能)想到ASLR或环境如何通过(可能通过main的第三个参数)来预测其中的确切垃圾。

您正在使用read(filedesc, message, 256),但read(2)不会在message的末尾添加任何 NUL 字节,除非该字节位于文件中;所以使用strlen(message)undefined behavior(UB),你应该是scared

  

它完美无缺

<强>错误即可。当你运气不好时,UB似乎偶然(而不是#34;完美&#34;)。这就是 undefined 的原因。在MacOSX下可能就是这种情况。但是,即使你的程序出现(错误地,运气不好),它仍然非常错误。

UB未定义,因此没有关于行为差异的解释,而没有深入了解gory实现细节。如果你真的关心这些(但你真的不应该),研究编译器的来源,你的内核代码(以及execve(2)之后发生的事情,特别是在crt0中),生成的汇编代码(使用gcc -S -fverbose-asm -O)来了解message内的垃圾数据类型。你可能花费数年时间来了解所有血腥细节。

您应该替换(注意您的==非常错误)

     while(bytesRead == read(filedesc, message, 256) > 0){ //BAD

至少(使用comma operatorassignment expression)清除message并保持读取字节数的内容:

    while ((memset(message, 0, sizeof(message)),
           (byteread = read(filedesc, message, sizeof(message)-1) > 0) {

由于message的最后一个字节永远不是read,因此它保持其0值(并且byteread始终小于256,sizeof(message) ...)。

您的计划中还有其他错误。保持恐惧(并this获取灵感)。

尝试使用调试器。使用cross-compilationuse the gdb debugger可以remote debugging。如果-g -WallGCC,请将{{1}}传递给您的交叉编译器。也许在你的Raspberry Pi上使用valgrind

也许还可以使用static program analyzerclang-analyzer之类的Frama-C。注意,他们可能需要额外的技能和注释(例如在ACSL中为Frama-C)有用并且可以提供大量false alarms(请记住Halting Problemundecidable ,所以不要对静态程序分析器和编译器抱太多期望。)

顺便说一下,关于未初始化记忆的正确思考是相信(这是一个有用的虚构)它带有某种疾病&#34;它传播到使用它的每件事物。当然,这不是计算机内部发生的事情。但是这样思考会帮助你避免UB。