You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言课程作业:WAV文件反转的运行异常与内存问题

C语言WAV文件反转程序问题解答

问题1:短音频反转正常,长音频失效的原因

你的程序处理长音频失效,核心原因有三个:

1. 文件指针初始定位错误

你用fseek(input, n, SEEK_END);将指针定位到文件末尾之后n字节的位置,这是完全错误的。对于短文件,系统可能允许指针超出文件范围,后续操作碰巧能凑出正确结果;但长文件会导致fread读取无效数据,甚至触发读写错误。

正确的做法是将指针定位到最后一个音频块的起始位置,即从文件末尾往前偏移n字节:

fseek(input, -n, SEEK_END);

2. 循环条件不严谨且存在大文件溢出风险

while (ftell(input) != sizeof(WAVHEADER))的判断逻辑有两个问题:

  • 当处理第一个音频块时,fread后指针会移动到sizeof(WAVHEADER),此时执行fseek(input, -2 * n, SEEK_CUR)会跳到sizeof(WAVHEADER) - n,循环条件不满足,会漏掉第一个块的写入。
  • ftell返回的long类型在32位系统下最多只能表示2GB文件,长音频文件超出这个范围会导致返回值溢出,循环条件判断完全失效。

正确的循环条件应该是判断当前指针位置大于等于数据区起始位置(即sizeof(WAVHEADER)):

while (ftell(input) >= sizeof(WAVHEADER))

3. 音频块缓冲区溢出

你用WORD temp;存储音频块,但WORD是16位(2字节)类型,而音频块大小n可能是4字节(比如16位双声道)。fread(&temp, n, 1, input)会强行向2字节的变量写入4字节数据,导致栈缓冲区溢出。短文件可能因为操作次数少没触发崩溃,长文件多次溢出后会破坏栈结构,导致程序失效。

正确的做法是根据n动态分配缓冲区:

BYTE *temp = malloc(n);
if (temp == NULL) {
    // 内存分配失败处理
    fclose(input);
    fclose(output);
    return 1;
}
// 后续用temp读取和写入
free(temp);

问题2:删除get_data_size函数时而出现段错误的原因

核心原因:未检查文件操作的返回值,且指针定位错误导致无效读写

get_data_size函数的作用是将文件指针移到文件末尾,但它本身没有任何实际作用。当你保留这个函数时,文件指针已经在末尾,后续fseek(input, n, SEEK_END)会把指针移到末尾+n的位置;删除该函数后,指针停在WAV头之后的位置,同样会被移到末尾+n的位置——两种情况的指针位置是一样的。

出现段错误的差异是因为:

  • 当保留get_data_size时,栈上多了该函数的局部变量,改变了栈布局,缓冲区溢出(问题1中的第3点)没有破坏到关键栈数据(如返回地址),所以程序没崩溃。
  • 删除该函数后,栈布局变化,缓冲区溢出直接覆盖了关键栈数据,触发段错误。
  • 后续再次删除没有出现段错误,可能是编译器优化、测试文件不同或栈布局随机变化导致的巧合,但潜在的缓冲区溢出问题依然存在。

修复后的完整代码示例

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

#include "wav.h"

int check_format(WAVHEADER header);
int get_block_size(WAVHEADER header);

int main(int argc, char *argv[])
{
    // Ensure proper usage
    if (argc != 3)
    {
        printf("Usage: %s <input.wav> <output.wav>\n", argv[0]);
        return 2;
    }

    // Open input file for reading
    char *infile = argv[1];
    char *outfile = argv[2];

    FILE *input = fopen(infile, "rb");
    if (input == NULL)
    {
        printf("Could not open input file: %s\n", infile);
        return 1;
    }

    // Read header
    WAVHEADER inwav;
    if (fread(&inwav, sizeof(WAVHEADER), 1, input) != 1)
    {
        printf("Failed to read WAV header\n");
        fclose(input);
        return 1;
    }

    // Validate WAV format
    if (check_format(inwav) != 0)
    {
        fclose(input);
        printf("Input file is not a valid WAV file\n");
        return 3;
    }

    // Open output file for writing
    FILE *output = fopen(outfile, "wb");
    if (output == NULL)
    {
        printf("Could not open output file: %s\n", outfile);
        fclose(input);
        return 1;
    }

    // Write header to output
    if (fwrite(&inwav, sizeof(WAVHEADER), 1, output) != 1)
    {
        printf("Failed to write WAV header\n");
        fclose(input);
        fclose(output);
        return 1;
    }

    // Calculate block size
    int n = get_block_size(inwav);
    if (n == 0)
    {
        fclose(input);
        fclose(output);
        return 1;
    }

    // Allocate buffer for audio block
    BYTE *temp = malloc(n);
    if (temp == NULL)
    {
        printf("Memory allocation failed\n");
        fclose(input);
        fclose(output);
        return 1;
    }

    // Position pointer to the last audio block
    if (fseek(input, -n, SEEK_END) != 0)
    {
        printf("Failed to seek input file\n");
        free(temp);
        fclose(input);
        fclose(output);
        return 1;
    }

    // Write audio blocks in reverse order
    while (ftell(input) >= sizeof(WAVHEADER))
    {
        // Read one block
        if (fread(temp, n, 1, input) != 1)
        {
            printf("Failed to read audio block\n");
            break;
        }
        // Write the block to output
        if (fwrite(temp, n, 1, output) != 1)
        {
            printf("Failed to write audio block\n");
            break;
        }
        // Seek back two blocks to read the previous one
        if (fseek(input, -2 * n, SEEK_CUR) != 0)
        {
            // If seeking fails, we've reached the start of data
            break;
        }
    }

    // Cleanup
    free(temp);
    fclose(input);
    fclose(output);
    return 0;
}

int check_format(WAVHEADER header)
{
    return (header.format[0] == 'W' && header.format[1] == 'A' && 
            header.format[2] == 'V' && header.format[3] == 'E') ? 0 : 1;
}

int get_block_size(WAVHEADER header)
{
    if (header.numChannels != 1 && header.numChannels != 2)
    {
        printf("Unsupported number of channels: %d\n", header.numChannels);
        return 0;
    }
    if (header.bitsPerSample != 8 && header.bitsPerSample != 16)
    {
        printf("Unsupported bits per sample: %d\n", header.bitsPerSample);
        return 0;
    }
    return (header.bitsPerSample / 8) * header.numChannels;
}

额外修复说明:

  • 打开文件时使用rb和wb模式,确保二进制文件读写的兼容性(Windows系统下必须)。
  • 增加了所有文件操作的返回值检查,避免无效操作导致的未知错误。
  • 完善了错误提示信息,便于调试。
  • 优化了check_format和get_block_size的逻辑,增加了对采样位数的合法性检查。

内容的提问来源于stack exchange,提问作者NRevan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 19:01:28