我正在编写一个简单的unix shell程序,并且遇到了由fgets()调用引起的分段错误。通过该调用,我试图从命令行获取用户输入并将其保存以备将来使用。应该很简单。在MacOS上,代码编译时带有一个警告,但执行得很好。在Ubuntu虚拟机上进行测试时,出现段错误,并且目前将其范围缩小到特定的fgets()调用。
我对DDD不太熟悉,但是我能够使用它和一些简单的print语句来确定它的fgets()调用,这给我带来了麻烦。我检查了要分配给该调用的指针是否已正确分配。尽管我的两台机器运行的是gcc的不同版本,但我感到困惑的是,为什么我只能在一个系统上而不是两个系统上都出现段错误。
下面是我的源代码,而我遇到问题的特定函数是parse()函数。这不是完整的代码或完成的代码,但是我希望它能不断提示用户输入,从命令行接收输入,保存此输入并将其拆分为令牌以传递给execute()以便进一步使用。我被告知并了解我当前存在内存泄漏和范围错误。我仍然不确定为什么在Ubuntu上,当我第一次调用parse()并使其进入函数内部的fgets()调用时,我会遇到分段错误。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define PROMPT "MyShell> "
#define MAX_SIZE 256
#define EXIT_CMD "exit"
/**
@brief Takes a pointer as an argument and checks whether or not it is NULL
(hasn't been properly allocated in memory). If the pointer is NULL,
behavior is undefined, so an error message is displayed to the user
and the program is terminated.
*/
void validateMemoryAllocation(char* pointer)
{
if (pointer == NULL)
{
printf("%s", "Fatal Error: Failed to allocate memory to save command input. Exiting...\n");
exit(0);
}
}
/**
@brief Fork a child to execute the command using execvp. The parent should wait for the child to terminate
@param args Null terminated list of arguments (including program).
@return returns 1, to continue execution and 0 to terminate the MyShell prompt.
*/
int execute(char **args)
{
if (strcmp(args[0], "exit") == 0) // Check for exit command.
{
printf("Exit received. Terminating MyShell...\n");
return 1; // Return to main with exit value to terminate the program.
} else // Not exit command, proceed attempting to execute.
{
}
return 0; // Return 0 and continue MyShell.
}
/**
@brief gets the input from the prompt and splits it into tokens. Prepares the arguments for execvp
@return returns char** args to be used by execvp
*/
char** parse(void)
{
char *rawInput, *inputDup, *token;
int validCheck, argCount, i, newLineLocation;
/* Save the entire line of user input. */
rawInput = malloc(sizeof(char) * MAX_SIZE);
validateMemoryAllocation(rawInput);
fgets(rawInput, MAX_SIZE, stdin);
inputDup = strdup(rawInput); /* Duplicate the string for modification. */
/* First loop: Count number of total arguments in user input. */
argCount = 0;
while( (token = strsep(&inputDup, " ")) != NULL)
{
argCount++;
}
/* Create array to hold individual command arguments. */
char* tokenArray[argCount];
/* Second loop: save tokens as arugments in tokenArray. */
for (i = 0; i < argCount; i++)
{
token = strsep(&rawInput, " ");
tokenArray[i] = token;
}
/**
Before returning the arguments, trim the dangling new line
character at the end of the last argument.
*/
tokenArray[argCount - 1] = strtok(tokenArray[argCount - 1], "\n");
return tokenArray;
}
/**
@brief Main function should run infinitely until terminated manually using CTRL+C or typing in the exit command
It should call the parse() and execute() functions
@param argc Argument count.
@param argv Argument vector.
@return status code
*/
int main(int argc, char **argv)
{
int loopFlag = 0;
char** input;
/* Loop to continue prompting for user input. Exits with proper command or fatal failure. */
while (loopFlag == 0)
{
printf("%s", PROMPT); // Display the prompt to the user.
input = parse(); // Get input.
loopFlag = execute(input); // Execute input.
}
return EXIT_SUCCESS;
}
我希望将用户输入保存到字符串指针rawInput,在MacOS上是这种情况,但在Ubuntu上是这种情况。
编辑:如果有帮助,这是正在使用的两个系统的一些示例输出。我了解我需要修补一些内存泄漏。
MacOS
D-10-16-18-145:a1 user$ ./myshell
MyShell> hello
MyShell> darkness
MyShell> my
MyShell> old
MyShell> friend
MyShell> exit
Exit received. Terminating MyShell...
D-10-16-18-145:a1 user$
Ubuntu
MyShell> hello
Segmentation fault (core dumped)
答案 0 :(得分:3)
做
char** parse(void) { char* tokenArray[argCount]; ... return tokenArray; }
您返回一个 local 数组的地址,当您读取内部内容时,行为将是不确定的
请勿将 tokenArray 放入堆栈中,对其进行分配,因此请替换
char* tokenArray[argCount];
作者
char** tokenArray = malloc(argCount * sizeof(char *));
您如何知道函数中的条目数?
最好多分配一个条目并将NULL放在末尾,或者将 argCount 作为输出变量
请注意,您的inputDup = strdup(rawInput);
会导致内存泄漏,免费