【问题标题】:Why is my C program behaving differently based on the directory it is executed from?为什么我的 C 程序的行为会根据它执行的目录而有所不同?
【发布时间】:2013-11-01 15:07:03
【问题描述】:

这快把我逼疯了。我编写了一个程序,它接受用户输入的文件名。它在我的 ~/documents/cs 目录中执行时按预期运行,但在我的 ~/documents/cs/assign5 目录中失败。这对我来说完全没有意义。为什么程序的行为会根据它所在的目录而有所不同?

在父目录中执行的良好输出:

./a.out - file2
Enter the filename: file1
FILE1
FILE2

assign5 目录的错误输出:

./a.out - file2
Enter the filename: file1
file1
n: No such file or directory

我什至尝试将 assign5 目录重命名为其他目录,效果很好。

该程序基本上采用两个命令行参数。如果存在“-”命令行参数,它会询问文件名。然后它将两个文件的内容打印到标准输出。这是程序失败的地方(仅在assign5目录中......)。似乎当程序在assign5目录中运行时,userInput变量存储的值是“n”而不是“file1”。为什么!?

if(strcmp(argv[1], "-") == 0) // use standard-in for input file 1
        {
            printf("Enter the filename: ");
            fflush(NULL);
            read(STDIN_FILENO, userInput, sizeof(userInput));
            userInput[strlen(userInput)-1] = '\0';
            if((input_file1 = open(userInput, O_RDONLY)) < 0)
            {
                perror(userInput);
                exit(1);
            }

更新:

我在名为“assign5”的目录中的远程 linux 服务器上运行完全相同的代码,并按预期编译和执行。那么,我的电脑有什么问题吗?

【问题讨论】:

  • 那个代码充满了危险。我会避免在这个应用程序中使用 read()。
  • 如果你使用 read,它会返回一个值。使用它。
  • strlen() 再次。几乎所有与该调用一起发布的代码都将空终止符问题作为问题的根本原因。
  • userInput[strlen(userInput)-1]; 函数中删除 -1,请参阅下面的答案以获得解释。

标签: c directory


【解决方案1】:

不要使用read。除非你知道自己在做什么,否则它会搞砸你所有的 I/O。

strlenread 返回的输入最多会导致SIGSEGV 最坏的情况是像您所观察到的那样未定义的行为。使用scanf

scanf("%s", userInput); // will add a null terminator itself

如果您真的必须使用read,那么您需要手动完成scanf 为您完成的工作:

// reserve space for the null terminator
int bytes_read = read(STDIN_FILENO, userInput, sizeof(userInput) - 1);
if (bytes_read < 0) {
    perror("read");
    abort();
}         

// add the null terminator
userInput[bytes_read] = '\0';

// you will most likely have a newline in the input
while (isspace(userInput[bytes_read - 1]))
     userInput[--bytes_read] = '\0';

char * filename = userInput;

// you may have preceding spaces
while (isspace(*filename))
     filename++;

另请注意,由于未定义的原因,read 可能会在读取整个输入之前返回,在这种情况下,您最好再次调用它,直到它返回 0。scanf 将为您完成所有这些操作。

【讨论】:

  • @Smith 那么你需要使用它的返回值!它将返回读取的字符数。确保删除所有换行符 (isspace) 并添加一个空终止符。
【解决方案2】:

您正在用 '\0' 替换字符串中的最后一个字符,而不是像我怀疑的那样将 '\0' 附加到末尾:更改此行:

userInput[strlen(userInput)-1] = '\0';  

到:

userInput[strlen(userInput)] = '\0';  

我没有加载 linux 或 unix,但是这个 windows 代码很小,可以更好地说明我的建议:

#include <windows.h>
#include <ansi_c.h>
int main(void)
{
    DWORD len=260;
    LPTSTR lpBuffer;
    lpBuffer = malloc(260);
    GetCurrentDirectory(len, lpBuffer);

    lpBuffer[strlen(lpBuffer)]=0; //This works (although not necessary here
                                  //as GetCurrenentDirectory appends a '\0'
                                  //however, it is safe to do)

    lpBuffer[strlen(lpBuffer)-1]=0; //This will destroy the path information
                                    //by removing the last necessary char from 
    return 0;                       //your path string
}

【讨论】:

  • 如上所述,您不能在 read 的输出上使用 strlen,因为它不是以空值结尾的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-06-11
  • 1970-01-01
  • 1970-01-01
  • 2016-10-15
  • 2013-07-19
  • 1970-01-01
  • 2018-04-05
相关资源
最近更新 更多