【问题标题】:What causes the segmentation fault in the following piece of code?是什么导致以下代码中的分段错误?
【发布时间】:2021-11-25 17:36:19
【问题描述】:

我有另一个文件 (card.raw) 必须检查已删除的图像。我将一个缓冲区读入内存,然后检查它是否是图像的开始,如果是,我写入第一个缓冲区,如果它不是新图像的开始,它会继续写入直到下一个图像开始。图像在 card.raw 文件中是背靠背的。我在代码中放置了一些 printf 函数,并将段错误隔离到最后一个 fwrite 函数,但我不知道是什么原因造成的。我已经尝试过 Valgrind,但我不确定输出的含义或如何修复它。

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

int main(int argc, char *argv[])
{
    FILE *raw = fopen(argv[1], "r");
    int SIZE = sizeof(raw);
    int buffer[512];
    int JPEG_num = 0;
    FILE *img[50];
    char filename[4];

    for(int j = 0; j < SIZE; j++)
    {
        for(int i = 0; i < 512; i++)
        {
            fread(&buffer[i], 1, 1, raw);
        }
        if(buffer[0] == 0xff)
        {
            if(buffer[1] == 0xd8)
            {
                if(buffer[2] == 0xff)
                {
                    if(buffer[3] >= 0xe0 && buffer[3] <= 0xef)
                    {
                        if(JPEG_num == 0)
                        {
                            sprintf(filename, "%03i.jpg", 0);
                            img[0] = fopen(filename, "w");
                            fwrite(&buffer, 1, 512, img[0]);
                            JPEG_num++;
                        }
                        else
                        {
                            fclose(img[0]);
                            sprintf(filename, "%03i.jpg", JPEG_num);
                            img[JPEG_num] = fopen(filename, "w");
                            fwrite(&buffer, 1, 512, img[JPEG_num]);
                            JPEG_num++;
                        }
                    }
                }
            }
        }
        else
        {
            if(JPEG_num != 0)
            {
                fwrite(&buffer, 1, 512, img[JPEG_num]);
                JPEG_num++;
            }
        }
    }
    fclose(img[JPEG_num]);
}

编辑

我将文件名大小和 if 条件以及 SIZE 整数更改为指针的大小(我不确定我是否像建议的那样正确地这样做了)

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

int main(int argc, char *argv[])
{
    FILE *raw = fopen(argv[1], "r");
    int SIZE = sizeof(*raw);
    int buffer[512];
    int JPEG_num = 0;
    FILE *img[50];
    char filename[9];

    for(int j = 0; j < SIZE; j++)
    {
        for(int i = 0; i < 512; i++)
        {
            fread(&buffer[i], 1, 1, raw);
        }
        if(buffer[0] == 0xff && buffer[1] == 0xd8 && (buffer[3] >= 0xe0 && buffer[3] <= 0xef))
        {
            if(JPEG_num == 0)
            {
                sprintf(filename, "%03i.jpg", 0);
                img[0] = fopen(filename, "w");
                fwrite(&buffer, 1, 512, img[0]);
                JPEG_num++;
            }
            else
            {
                fclose(img[0]);
                sprintf(filename, "%03i.jpg", JPEG_num);
                img[JPEG_num] = fopen(filename, "w");
                fwrite(&buffer, 1, 512, img[JPEG_num]);
                JPEG_num++;
            }
        }
        else
        {
            if(JPEG_num != 0)
            {
                fwrite(&buffer, 1, 512, img[JPEG_num]);
                JPEG_num++;
            }
        }
    }
    fclose(img[JPEG_num]);
}

编辑

我删除了int SIZE = sizeof(*raw);,只是将for(int j = 0; j &lt; SIZE; j++) 循环更改为while (fread(buffer, 1, 512, raw) == 512) 循环

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

int main(int argc, char *argv[])
{
    FILE *raw = fopen(argv[1], "r");
    int buffer[512];
    int JPEG_num = 0;
    FILE *img[50];
    char filename[260];

    while( fread(buffer, 1, 512, raw) == 512 )
    {
        for(int i = 0; i < 512; i++)
        {
            fread(&buffer[i], 1, 1, raw);
        }
        if(buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] >= 0xe0 && buffer[3] <= 0xef))
        {
            printf("A\n");
            if(JPEG_num == 0)
            {
                sprintf(filename, "%03i.jpg", 0);
                img[0] = fopen(filename, "w");
                fwrite(&buffer, 1, 512, img[0]);
            }
            else
            {
                fclose(img[JPEG_num - 1]);
                sprintf(filename, "%03i.jpg", JPEG_num);
                img[JPEG_num] = fopen(filename, "w");
                fwrite(&buffer, 1, 512, img[JPEG_num]);
                JPEG_num++;
            }
        }
        else
        {
            if(JPEG_num != 0)
            {
                fwrite(&buffer, 1, 512, img[JPEG_num]);
            }
        }
    }
    fclose(img[JPEG_num]);
}

编辑

我放置了几个printf 函数来查看问题出在哪里。它只是打印出 1 和 2。它从不进入 if 条件。

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

int main(int argc, char *argv[])
{
    FILE *raw = fopen(argv[1], "r");
    int buffer[512];
    int JPEG_num = 0;
    FILE *img[50];
    char filename[260];

    while( fread(buffer, 1, 512, raw) == 512 )
    {
        printf("1\n");
        for(int i = 0; i < 512; i++)
        {
            fread(&buffer[i], 1, 1, raw);
        }
        printf("2\n");
        if(buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] >= 0xe0 && buffer[3] <= 0xef))
        {
            printf("A\n");
            if(JPEG_num == 0)
            {
                printf("B\n");
                sprintf(filename, "%03i.jpg", 0);
                printf("C\n");
                img[0] = fopen(filename, "w");
                printf("D\n");
                fwrite(&buffer, 1, 512, img[0]);
                printf("E\n");
            }
            else
            {
                printf("!\n");
                fclose(img[JPEG_num - 1]);
                printf("@\n");
                sprintf(filename, "%03i.jpg", JPEG_num);
                printf("#\n");
                img[JPEG_num] = fopen(filename, "w");
                printf("^\n");
                fwrite(&buffer, 1, 512, img[JPEG_num]);
                printf("&\n");
                JPEG_num++;
            }
        }
        else
        {
            if(JPEG_num != 0)
            {
                printf("3\n");
                fwrite(&buffer, 1, 512, img[JPEG_num]);
                printf("4\n");
            }
        }
    }
    fclose(img[JPEG_num]);
}


编辑

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

int main(int argc, char *argv[])
{
    FILE *raw = fopen(argv[1], "r");
    int buffer[512];
    int JPEG_num = 0;
    FILE *img[50];
    char filename[260];

    while (fread(buffer, 1, 512, raw) == 512)
    {
        if(buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] >= 0xe0 && buffer[3] <= 0xef))
        {
            if(JPEG_num == 0)
            {
                sprintf(filename, "%03i.jpg", 0);
                img[0] = fopen(filename, "w");
                fwrite(&buffer, 1, 512, img[0]);
            }
            else
            {
                fclose(img[JPEG_num - 1]);
                sprintf(filename, "%03i.jpg", JPEG_num);
                img[JPEG_num] = fopen(filename, "w");
                fwrite(&buffer, 1, 512, img[JPEG_num]);
                JPEG_num++;
            }
        }
        else
        {
            if(JPEG_num != 0)
            {
                fwrite(&buffer, 1, 512, img[JPEG_num]);
            }
        }
    }
    fclose(img[JPEG_num]);
}

【问题讨论】:

  • 你编译时是否打开了所有警告?还是在调试器中运行?其中任何一个都有能力立即指出问题。
  • char filename[4];。您实际上在其中存储了超过 4 个字节。你应该质疑int SIZE = sizeof(raw)...
  • 什么意思我应该质疑 int SIZE = sizeof(raw)?
  • for(int i = 0; i &lt; 512; i++){ fread(&amp;buffer[i], 1, 1, raw);} 最好用单次读取写入 fread(buffer, 1, 512, raw);
  • 您的 SIZE 循环应该是 while( fread(buffer, 1, 512, raw) == 512 )。除非您阅读文件或统计文件,否则您无法知道文件中有多少数据。但是当文件是管道时,统计文件会失败,所以最好只读取直到没有更多数据。

标签: c segmentation-fault


【解决方案1】:

“导致分段错误的原因...”

有几个地方可能会出现分段错误。一个突出的是:

char filename[4];
...
sprintf(filename, "%03i.jpg", 0);

在此示例中,filename 有足够的空间来包含 3 个字符 + nul 终止符。它需要至少声明为 8 以包含 "%03i.jpg", 0 的结果。 (如果有足够的空间,filename 将填充 000.jpg。)

如果您不使用小型嵌入式微处理器,则没有理由不创建具有足够空间的path 变量。例如:

char filename[PATH_MAX];//if PATH_MAX is not defined, use 260   

请注意,写入您的进程不拥有的内存区域会调用未定义的行为,这可能以分段错误的形式出现,或者更糟糕的是,似乎可以正常工作。例如,如果您的代码碰巧将变形值写入filename 变量,然后该变量随后用于打开文件:

img[0] = fopen(filename, "w");  

不知道结果会怎样。因为您的代码没有检查此调用的结果,所以存在更多潜在问题。

编辑以解决文件大小...

int SIZE = sizeof(*raw);

不提供文件的大小。它将返回指针的大小,即 4 或 8 字节,具体取决于应用程序是构建为 32 位还是 64 位。考虑使用 this approach 之类的东西来获取文件大小的实际值,从而产生如下调用:

unsigned long SIZE = fsize(argv[1]);

【讨论】:

  • 我更改了文件名大小,但仍然出现分段错误。
  • @Dylan - 在哪里?您是否在调试模式下运行代码并单步执行?
  • 我将其编辑为 char filename[260];它仍然在同一个地方给出一个段错误。
  • @Dylan ...结果如何?
  • @Dylan - 您是否已采取措施执行已提出的任何其他建议?您是否使用调试器逐步完成?您是否在启用所有警告的情况下进行编译。将这些建议与您在答案/cmets 方面收到的输入以及您自己对其余部分的故障排除一起,对您的好处会大得多。如果在执行此操作的过程中您发现 另一个 问题您无法回答,那么一定要回来发布关于该新问题的 另一个 问题。
【解决方案2】:

正如 ryker 所说,这里有几个可能的故障点。

另一个是 int SIZE = sizeof(raw); 将 SIZE 设置为指针的大小(4/8 字节)。

【讨论】:

    【解决方案3】:

    除了其他答案所说的之外,文件指针的处理也被破坏了:

            if(buffer[0] == 0xff && buffer[1] == 0xd8 && (buffer[3] >= 0xe0 && buffer[3] <= 0xef))
            {  // We found a new header, lets create a new file...
                if(JPEG_num == 0)
                {
                    sprintf(filename, "%03i.jpg", 0);
                    img[0] = fopen(filename, "w");   // Open img[0]
                    fwrite(&buffer, 1, 512, img[0]); // Write to img[0]
                    JPEG_num++;                      // JPEG_num is 1 ahead of the used index in `img` array!
                }
                else
                {
                    fclose(img[0]);   // This will close the same FILE* again and again...
                    sprintf(filename, "%03i.jpg", JPEG_num);
                    img[JPEG_num] = fopen(filename, "w");  
                    fwrite(&buffer, 1, 512, img[JPEG_num]);
                    JPEG_num++;
                }
            }
            else
            {  // No new header, just write
                if(JPEG_num != 0)  
                {  // Only write after we found first header
                    fwrite(&buffer, 1, 512, img[JPEG_num]);  // OUCH! Remember: JPEG_num is 1 ahead of the index in `img` array.
                    JPEG_num++; // OUCH: We use same file but now JPEG_num is 2 or more ahead of index in `img` array.
                }
            }
        }
        fclose(img[JPEG_num]);
    

    因此,您通过阵列的速度太快了。

    要么使用JPEG_num-1,并且仅在创建新文件后递增,

    只需删除整个数组,然后改用单个 FILE *outfile;

    改进的版本是(由 OP 添加错误检查):

    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char *argv[])
    {
        FILE *raw = fopen(argv[1], "rb");
        int buffer[512];
        FILE *outfile = NULL;
        char filename[9];
        int JPEGnum = 0;
    
        while (fread(buffer, 1, 512, raw) == 512)
        {
            if (buffer[0] == 0xff 
             && buffer[1] == 0xd8 
             && buffer[2] == 0xff
             && (buffer[3] >= 0xe0 && buffer[3] <= 0xef))
            { // We found a new header, let's create a new file...
                if (outfile != NULL)
                {
                    fclose(outfile);
                }
                sprintf(filename, "%03i.jpg", JPEG_num);
                outfile = fopen(filename, "wb");  
                fwrite(buffer, 1, 512, outfile);
                JPEG_num++;
            }
            else
            { // No new header, just write
                if (outfile != NULL)  
                {  // Only write after we found first header
                    fwrite(buffer, 1, 512, outfile); 
                }
            }
        }
        if (outfile != NULL) // Check if we found at least one JPEG header
            fclose(outfile);
    }
    

    在这里,我还修复了FILE 数据类型而不是文件大小的错误循环。 文件也以二进制模式打开。

    【讨论】:

    • 我把它改成了JPEG_num - 1 并去掉了最后一个JPEG_num ++
    • 然后发生了什么?
    • 还是同样的段错误
    • 那么您需要提供另一个更新版本。或者你可以测试我的改进版本。
    • 您是否打算建议 OP 编辑​​帖子以更改有问题的代码?
    猜你喜欢
    • 2012-08-14
    • 2011-01-10
    • 1970-01-01
    • 1970-01-01
    • 2017-08-18
    • 1970-01-01
    • 2013-02-16
    • 1970-01-01
    相关资源
    最近更新 更多