【问题标题】:Segmentation fault on the server, but not local machine服务器上的分段错误,但不是本地机器
【发布时间】:2026-02-22 14:05:01
【问题描述】:

如标题所述,该程序在我的本地机器(ubuntu 9.10)上运行,但在服务器(linux)上运行。这是godaddy的网格托管包。

请帮忙..

代码如下:

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

int main(int argc, char **argv)
{
    long offset;
    FILE *io;
    unsigned char found;
    unsigned long loc;

    if (argc != 2)
    {
        printf("syntax: find 0000000\n");
        return 255;
    }

    offset = atol(argv[1]) * (sizeof(unsigned char)+sizeof(unsigned long));

    io = fopen("index.dat","rb");
    fseek(io,offset,SEEK_SET);
    fread(&found,sizeof(unsigned char),1,io);
    fread(&loc,sizeof(unsigned long),1,io);

    if (found == 1)
        printf("%d\n",loc);
    else
        printf("-1\n");

    fclose(io);

    return 0;
}

编辑:这不是我的程序。 我希望我知道足够的 C 来修复它,但我已经到了最后期限。该程序旨在查找 PI 序列中第一次出现的 7 位数字,index.dat 包含一个巨大的数组编号 => 位置。

http://jclement.ca/fun/pi/search.cgi

编辑 2:我使用更新后的代码测试空指针,仍然得到相同的结果。 该程序在我的本地机器上运行良好,这个错误只发生在服务器上。

【问题讨论】:

  • 故障信息是什么?哪条线失败了?
  • 可能文件不存在,所以fopen返回NULL?
  • 你为什么要将参数乘以 (sizeof(long)+sizeof(char)) 来给你偏移量?...如果调用 fopen,这会让你超过文件末尾工作 - 你真的首先验证过......如果参数是 1234567 怎么办????哦,是的,在继续之前先打开文件时检查 NULL....
  • 您在下面的 cmets 中提到的另一件事...您将其上传到服务器,服务器上的文件名也可能区分大小写? Index.DAT 对 index.dat 区分大小写....先检查....
  • 文件名和代码中的一模一样。

标签: c segmentation-fault


【解决方案1】:

最有可能的是 fopen 失败了——你没有检查它的返回值来查看你是否真的得到了一个有效的指针。 (当您尝试在下一行中使用该 NULL 指针时会出现段错误。)

【讨论】:

  • 我为空指针添加了错误检查,结果相同。
【解决方案2】:

可能unsigned long 的大小在机器上不一样。

以下程序在您的机器上打印什么?

#include <stdio.h>

int main(void)
{
    printf("%zu\n", sizeof(unsigned long));
    return 0;
}

使用gcc -std=c99 file.c 编译。如果打印的尺寸确实不同,那么您需要将unsigned long 替换为uint32_t,并在程序开头添加#include &lt;inttypes.h&gt;

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdint.h>

int main(int argc, char **argv)

{
    long offset;
    FILE *io;
    unsigned char found;
    uint32_t loc;

    if (argc != 2)
    {
        printf("syntax: find 0000000\n");
        return 255;
    }
    /* sizeof(unsigned char) is 1, and I am assuming you wanted
       sizeof(unsigned long) to be 4.  But see below. */
    offset = strtol(argv[1], NULL, 0) * (1+4);

    if ((io = fopen("index.dat", "rb")) == NULL) {
        fprintf(stderr, "Cannot open file\n");
        return EXIT_FAILURE;
    }
    if (fseek(io, offset, SEEK_SET) == -1) {
        fprintf(stderr, "Error seeking\n");
        perror(NULL);
        return EXIT_FAILURE;
    }
    if (fread(&found, 1, 1, io) != 1) {
        fprintf(stderr, "Error in first fread\n");
        return EXIT_FAILURE;
    }
    /* using sizeof loc makes sure that the correct size if always used,
       irrespective of the type of loc */
    if (fread(&loc, sizeof loc, 1, io) != 1) {
        fprintf(stderr, "Error in second fread\n");
        return EXIT_FAILURE;
    }
    if (found == 1)
        printf("%" PRIu32 "\n", loc);
    else
        printf("-1\n");
    fclose(io);

    return 0;
}

以上假设“正常工作”的程序有 4 个字节 unsigned long。如果不是,则需要将程序中的4 替换为正确计算机上unsigned long 的大小。

如果这是造成差异的原因,那么您现在知道读写二进制数据的问题之一:您必须非常小心大小、字节顺序等。

【讨论】:

  • 这比我推荐的快速调试还要好。这是解决问题的正确方法。无需猜测,它改进了软件。
  • 我已经尝试编译 file.c 代码,但由于某种原因它没有编译。无论如何,我已经编译了修改后的代码,我得到了相同的结果:它在我的本地机器上工作,但在服务器上出现“分段错误”。
  • @menachem-almog:你用gcc -std=c99编译了吗?
  • @Alok:他说它在“长偏移”声明之前出现了段错误。我的第一个想法是服务器和工作站之间可能存在硬件差异。
  • @menachem-almog:你是在服务器上编译运行在服务器上的程序吗?您不会从工作站复制程序,对吧?
【解决方案3】:

我刚用 godaddy 遇到了这个问题,发现 FTP 客户端上传的是 ASCII 而不是二进制。我在上传之前将扩展名更改为.bin,现在可以正常使用了。

【讨论】:

    【解决方案4】:

    我最初的猜测是文件无法打开,因此 io 为 NULL。

    但是 Wade 是对的:您应该首先在您的程序中添加一些错误处理 - 至少可以让您(和我们)更好地了解问题所在。

    【讨论】:

      【解决方案5】:

      我的猜测是服务器上的 sizeof(long) 是 8(因为它是 64 位系统),而您的本地机器上是 4(假设是 32 位机器)。因此,您计算的文件偏移量将是 2 倍。如果您需要转到文件中的特定偏移量,您应该使用固定大小的类型 - 即 uint32_t 等。

      【讨论】:

        【解决方案6】:

        首先,您没有向我们提供有关错误发生位置的上下文。

        其次,在人们花费时间之前,您需要添加错误检查。

        【讨论】:

        • 这不是答案。这是一条评论。
        • 具体来说,检查文件是否真的被打开了,也许它足够长。也许打印出该偏移量以确定它是您所期望的。
        【解决方案7】:

        也许“index.dat”不在您的服务器上,但在您的机器上。因为你是用“rb”打开它,而不是在 fopen 之后检查 io,所以可以很好地做到这一点。

        【讨论】:

        • 或者你的本地机器不区分大小写,文件名为 Index.dat。
        • 我已经按原样上传了整个目录。
        • @Wade:本地机器是 Ubuntu,它是一个 Linux 发行版,因此文件名区分大小写。
        • @David - 哎呀我的坏。我误读了它,并认为 Ubuntu 是服务器。
        【解决方案8】:

        你可以在服务器上运行 gdb 吗?如果是这样,请在启用调试的情况下构建(gcc 的 -g 选项)并在 gdb 中运行它。这将告诉您程序在哪里失败。方法如下(将 中的内容替换为相关信息):

        # gdb <program>
        
        (gdb) set args <arg>
        (gdb) run
        

        gdb 将捕获 segfault 并显示它崩溃的行。

        【讨论】:

        • 新手程序员。 Alok 的回答在这里可能是最正确的。
        • 我无法在服务器上运行 gdb。
        【解决方案9】:

        我认为您正在阅读超出文件限制的内容。我添加了一部分来检查文件大小并确保您在文件限制范围内阅读。

        #include <stdio.h>
        #include <stdlib.h>
        
        int main(int argc, char **argv)
        {
            long offset;
            FILE *io;
            unsigned char found;
            unsigned long loc;
        
            if (argc != 2)
            {
                printf("syntax: find 0000000\n");
                return 255;
            }
        
            offset = atol(argv[1]) * (sizeof(unsigned char)+sizeof(unsigned long));
        
            io = fopen("index.dat","rb");
        if (io==NULL) {fputs ("File error",stdout); exit (1);}
        fseek (io , 0 , SEEK_END);
        long fileSize = ftell (io);
        long offsetEnd = offset+sizeof(unsigned char)+sizeof(unsigned long);
        printf("file size: %d\nseek: %d\nseekEnd: %d\n",fileSize,offset,offsetEnd);
        
        if (offsetEnd> fileSize) {fputs ("Reading outside of file...",stdout); exit (1);}
        
            fseek(io,offset,SEEK_SET);
            fread(&found,sizeof(unsigned char),1,io);
            fread(&loc,sizeof(unsigned long),1,io);
        
            if (found == 1)
                printf("%d\n",loc);
            else
                printf("-1\n");
        
            fclose(io);
        
            return 0;
        }
        

        【讨论】:

          【解决方案10】:

          将代码的中间部分更改为:

          fseek(io,offset,SEEK_SET);
          printf("fseek worked\n");
          fread(&found,sizeof(unsigned char),1,io);
          printf("fread 1 worked\n");
          fread(&loc,sizeof(unsigned long),1,io);
          printf("fread 2 worked\n");
          

          并查看运行程序时打印了哪些行。这应该会提示您问题的确切位置。

          编辑:当我说函数调用“有效”时,我的意思是“没有导致段错误”。理想情况下,您希望检查对 fseekfread 的每次调用,以确保它们没有遇到任何错误,但您提到您在截止日期前,所以这只是一些快速的错误跟踪尝试和追踪段错误。

          【讨论】:

          • 我不会说成功到达下一行表明 fseek 或 fread “工作” - 你需要检查返回值来确定。
          • fseek 在失败的情况下返回-1
          • @Andrew,Alok:是的。我写这篇文章是为了让不熟悉 C 语言的人容易添加和理解(正如 OP 承认的那样)。我同意应该检查来自fseek 的返回,并且代码应该验证在fseek 之后有足够的文件留给fread,但我试图在截止日期前为新手保持简单。 “工作”是指“没有导致段错误”。
          【解决方案11】:

          也许您应该尝试为 found 和 loc 变量分配一些内存?

          【讨论】:

          • found 和 loc 在堆栈上。
          最近更新 更多