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

数组元素替换后末尾重复,设置NULL触发Segmentation Fault求助

问题描述

我写的程序会将数组中的某个值替换为下一个值,但最终数组的最后两个值出现重复。尝试直接将最后一个值设为array[i-1] = NULL;(i为数组大小)时触发了Segmentation Fault。

当前程序输出:

found it again
Array value 0: cat:
Array value 1: file1.txt:
Array value 2: output.txt:
Array value 3: output.txt:
Array value 4: (null):
Array value 5: (null):
This is the first line in file1.txt
This is the last line

设置array[i-1] = NULL;后的错误输出:

found it again
Array value 0: cat:
Array value 1: file1.txt:
Array value 2: output.txt:
Array value 3: (null):
Array value 4: (null):
Array value 5: (null):

完整代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>

#define BUFFERSIZE 1024

int main(int argc, char* argv[])
{
    int fd_out = dup(STDOUT_FILENO);
    int fd_in = dup(STDIN_FILENO);
    
    argc++;

    char buf[BUFFERSIZE];
    int n;
    printf("Please enter commands: \n");

    while ((n = read(STDIN_FILENO, buf, BUFFERSIZE)) > 0)
    {
        buf[n] = 0;
        buf[strlen(buf)-1] = '\0';
        int i = 0;
        char* array[10];
        char* token1 = strtok(buf, " ");

        while ((token1 != NULL))
        {
            array[i++] = token1;
            token1 = strtok(NULL, " ");
            
        }//while

        array[i] = NULL;

        if (strcmp(buf, "exit") == 0)
        {
            break;
        }//if
        
        for (int j = 0; j < i; j++) 
        {
            if (!strcmp(array[j], ">"))
            {
                char* filename_out = array[j+1];
                printf("STDOUT redirected\n");
                printf("found it\n");
                int file_out = open(filename_out, O_WRONLY | O_CREAT, 0777);
                int file2_out = dup2(file_out, 1);
                
                for(int k = j; k < i-1; k++)
                {
                    array[k] = array[k + 1];
                }//if
                
                array[i] = NULL;
                printf("found it again\n");

                if ((file_out | file2_out) == -1)
                {
                    return 1;
                }//if

                //array[i-1] = NULL;

                // for (int j = 0; j < i+2; j++)
                // {
                //     printf("Array value %d: %s:\n", j, array[j]);
                // }//for*/
            }//if
            if (!strcmp(array[j], "<"))
            {
                char* filename_in = array[j+1];
                printf("STDIN_FILENO redirected\n");
                int file_in = open(filename_in, O_WRONLY | O_CREAT, 0777);
                int file2_in = dup2(file_in, 0);

                for(int l = j; l < i - 1; l++)
                {
                    array[l] = array[l + 1];
                }//for
                
                if ((file_in | file2_in) == -1)
                {
                    return 1;
                }//if
            }
        }//for

        for (int i = 1; i < argc; i++)
        {
            pid_t pid;
            
            if (argc >= 1) 
            {
                if ((pid = fork()) < 0) 
                {
                    perror("fork");
                }//if
                else if (pid == 0) 
                { // child process
                    if (execvp(array[0], array) == -1) 
                    {
                        perror("execvp");
                        return EXIT_FAILURE;
                    } // if
                }//else if
                else 
                {   // parent process
                    int status;
                    wait(&status);
                    //int again_out = dup2(fd_out, 1);
                    //int again_in = dup2(fd_in, 0);
                    if ((again_out | again_in) == -1)
                    {
                        return 1;
                    }
                    printf("Please enter commands again: \n");
                    
                }//else     
            }//if
            else 
            {
                fprintf(stderr, "Please specify the name of the program to exec as a command line argument\n");
                return EXIT_FAILURE;
            }//if
        }//for
    }//while
    if (n == -1) perror("read");
}//main
问题分析与解决

核心问题

  1. 重定向参数处理错误:当前移位逻辑仅把>替换成后续文件名,未将>和文件名从数组中移除,导致execvp误把文件名当命令参数,同时数组末尾出现重复值。
  2. 数组计数未更新:处理重定向后未调整有效元素个数i,后续操作访问超出有效范围的数组元素,触发段错误。
  3. 文件操作参数错误:输入重定向<使用O_WRONLY | O_CREAT(写权限),读文件应使用O_RDONLY。
  4. 错误判断逻辑有误:用(file_out | file2_out) == -1检测open/dup2失败无效,按位或无法正确识别单个调用失败。
  5. 文件描述符恢复未生效:again_out和again_in被注释且未定义,重定向后无法恢复标准输出/输入。

修复步骤

1. 正确移除重定向参数

处理>或<时,将符号和文件名从数组中完全移除,更新数组计数i:

// 处理>的情况
if (!strcmp(array[j], ">"))
{
    char* filename_out = array[j+1];
    printf("STDOUT redirected\n");
    printf("found it\n");
    // 添加O_TRUNC覆盖文件,权限用0666更合理
    int file_out = open(filename_out, O_WRONLY | O_CREAT | O_TRUNC, 0666);
    int file2_out = dup2(file_out, 1);
    
    // 将j之后的元素往前移2位,跳过>和文件名
    for(int k = j; k < i-2; k++)
    {
        array[k] = array[k + 2];
    }
    i -= 2; // 有效元素个数减少2
    array[i] = NULL; // 正确终止数组,符合execvp要求
    printf("found it again\n");

    // 分别判断open和dup2是否失败
    if (file_out == -1 || file2_out == -1)
    {
        perror("open/dup2 failed");
        return 1;
    }

    // 只打印有效元素
    for (int idx = 0; idx <= i; idx++)
    {
        printf("Array value %d: %s:\n", idx, array[idx]);
    }
}

2. 修复输入重定向参数

// 处理<的情况
if (!strcmp(array[j], "<"))
{
    char* filename_in = array[j+1];
    printf("STDIN_FILENO redirected\n");
    // 读文件用O_RDONLY
    int file_in = open(filename_in, O_RDONLY);
    int file2_in = dup2(file_in, 0);

    for(int l = j; l < i - 2; l++)
    {
        array[l] = array[l + 2];
    }
    i -= 2;
    array[i] = NULL;
    
    if (file_in == -1 || file2_in == -1)
    {
        perror("open/dup2 failed");
        return 1;
    }
}

3. 恢复标准文件描述符

取消注释并定义again_out和again_in:

else 
{   // parent process
    int status;
    wait(&status);
    int again_out = dup2(fd_out, 1);
    int again_in = dup2(fd_in, 0);
    if (again_out == -1 || again_in == -1)
    {
        perror("dup2 restore failed");
        return 1;
    }
    printf("Please enter commands again: \n");
}

4. 避免数组越界

移除或修改越界的打印循环,确保仅访问数组有效元素范围。

段错误原因

处理重定向后未更新i的值,原i仍包含>和文件名的元素个数,此时i-1可能指向数组无效区域甚至超出数组长度(array长度为10),导致非法内存访问,触发Segmentation Fault。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 21:05:19