2015-10-22 17 views
1

我一直在試圖讓這個工作在過去的兩週無濟於事。 我有一個項目來創建一個實現解析和內置命令的shell。我遇到的問題是當我將char *傳遞給我的解析函數並返回時,當我嘗試訪問它的任何部分時,我得到一個段錯誤。我已經嘗試了不同的方法,包括一個持有char **的結構都具有相同的問題,所以我猜這是我的解析器的問題。我將不勝感激任何幫助。對於parser.c 代碼:解析器在C中產生段錯誤

#define BUFSIZE 1024 
#define TOK_BUFSIZE 64 
#define TOK_DELIM " \t\r\n\a" 

char*** Parse(char *line0){ 
char* null_ptr = 0; 
char*** cmd = malloc(MAX_SIZE * sizeof(char**)); 
/* 
char arg[] = argument 
char* argv[] = argument array 
char** cmd[] = array of argument arrays 
*/ 
int bufsize = MAX_SIZE, cmdp = 0, argp = 0, com = FALSE, redir = FALSE; 
char *token; 
char* line = malloc(100*sizeof(char)); 
strcpy(line,line0); 

token = strtok(line, TOK_DELIM); 
while (token){ 
    if (*token == ';'){ // new command string 
     char* tmp1 = malloc(BUFSIZE * sizeof(char)); 
     char** tmpa = malloc(BUFSIZE * sizeof(char*)); 
     strcpy(tmp1, token); 
     tmp1[sizeof(token)] = null_ptr; 
     tmpa[0]=tmp1; 
     cmd[cmdp] = tmpa; 
     argp = 0; 
     cmdp++; 
     com = FALSE; 
     redir = FALSE; 
    } 
    else if (*token == '>' || *token == '<' || token == ">>"){ // redirects 
     argp = 0; 
     char* tmp1 = malloc(BUFSIZE * sizeof(char)); 
     char** tmpa = malloc(BUFSIZE * sizeof(char*)); 
     strcpy(tmp1, token); 
     tmp1[sizeof(token)] = null_ptr; 
     tmpa[argp]=tmp1; 
     argp++; 
     printf("Redirect: %s\n",tmp1); 
     com = FALSE; 
     redir = TRUE; 
    } 
    else if (*token == '|'){  // pipe 
     printf("PIPE\n"); 
     cmdp++; 
     argp = 0; 
     com = FALSE; 
    } 
    else if (redir){  // redirect file name 
     // redirect token stored in arg[] 
     char* tmp1 = malloc(BUFSIZE * sizeof(char)); 
     char** tmpa = malloc(BUFSIZE * sizeof(char*)); 
     strcpy(tmp1, token); 
     tmp1[sizeof(token)] = null_ptr; 
     tmpa[argp]=tmp1; 
     cmd[cmdp]=tmpa; 
     argp = 0; 
     cmdp++; 
     redir = FALSE; 
     com = FALSE; 
     printf("File: %s\n", token); 
    } 
    else if (token == "&")  // background 
    { 
     cmdp++; 
     argp = 0; 
     char* tmp1 = malloc(BUFSIZE * sizeof(char)); 
     char** tmpa = malloc(BUFSIZE * sizeof(char*)); 
     strcpy(tmp1, token); 
     tmp1[sizeof(token)] = null_ptr; 
     tmpa[0]=tmp1; 
     cmd[cmdp]=tmpa; 

     printf("Background"); 
    } 
    else if (!com && !redir){ // command entered 
     argp = 0; 
     char* tmp1 = malloc(BUFSIZE * sizeof(char)); 
     char** tmpa = malloc(BUFSIZE * sizeof(char*)); 
     strcpy(tmp1, token); 
     tmp1[sizeof(token)] = null_ptr; 
     tmpa[argp] = tmp1; 

     argp++; 
     printf("Command %s\n", token); 
     com = TRUE; 
    } 
    else if (com){  // argument to command, all other redirects and pipes taken care of 
     char* tmp1 = malloc(BUFSIZE * sizeof(char)); 
     char** tmpa = malloc(BUFSIZE * sizeof(char*)); 
     strcpy(tmp1, token); 
     tmp1[sizeof(token)] = null_ptr; 
     tmpa[argp] = tmp1; 
     argp++; 
      printf("Argument: %s\n", token); 
      //cmd[cmdp] = argv;  // save current working argument array 
      //cmdp++; 
     } 
     // end of if else statements 

     token = strtok(NULL, TOK_DELIM); 



    } // end of while 
    cmdp++; 
    cmd[cmdp] = NULL; 

return &cmd; 
} 
+1

'我一直試圖讓這個工作在過去的2周無濟於事' - 你在調試器下運行它嗎?如果不是,爲什麼不呢?如果是這樣,你發現了什麼? –

+0

@Martin James調試器說segfault在我的執行函數中,我嘗試訪問數組中的一個參數,當我嘗試訪問cmd [n] [n]時它會發生段錯誤,它應該是char * –

+1

'tmp1 [sizeof (token)] = null_ptr;':'token'是一個指針,所以它總是會計算出相同的值(即64位的8位和32位的4位)。 –

回答

0

當我通過鍵入編譯您的命令行代碼:

gcc /path/to/yourcodefilename.c -Wall -Wextra 

但隨着代碼包含主要功能的實際文件名替換/path/to/yourcodefilename.c這最終調用你的函數(我的文件是test2.c),我收到警告。第一個是:

./test2.c:21: error: 'aaa' undeclared (first use in this function) 
./test2.c:21: error: (Each undeclared identifier is reported only once 
./test2.c:21: error: for each function it appears in.) 

我收到了一些這些。 「aaa」被您在函數中使用的以前未定義的名稱所取代。這包括單詞TRUE和FALSE。要糾正這種情況,您可以在程序頂部使用:

#define FALSE n 
#define TRUE y 

其中n和y分別代表假和真的數字。糾正它的另一種方法是包含包含「TRUE」和「FALSE」定義的頭文件。

我上了幾行注意到的第二件事是:

warning: assignment makes integer from pointer without a cast 

確保你不會從一個類型轉換到另一個數據。例如,不要將字符變量設置爲指針值。

例如,變化:

tmp1[sizeof(token)] = null_ptr; 

到:

tmp1[sizeof(token)] = '\0'; 

因爲指定索引到char*裝置指定char,和NULL_PTR是char*類型和char*char的是不相同。我所做的是分配了一個空值,即char

我希望可以幫助您與一些故障排除

+1

'tmp1 [sizeof(token)]'肯定是一個錯誤,無論如何,指針的大小與字符串的長度無關 –

0

這裏有幾個問題:

  • 你分配cmd及其子陣列。您在函數結尾處將地址返回給該數組。地址的類型爲char ****,這不是正確的返回類型。更糟的是:該地址是局部變量的地址,它在返回後立即超出範圍。返回從malloc代替了手柄:

    char ***Parse(char *line0) 
    { 
        char ***cmd = malloc(MAX_SIZE * sizeof(*cmd)); 
    
        // fill cmd 
    
        return cmd; 
    } 
    
  • 你的代碼是不必要的長,主要是因爲你的代碼分配內存的步驟,複製一個字符串,並明確地空終止它。 (其他人指出你不適當地做空終止。你還會分配一個固定大小的字節長度爲1024字節的字節,這非常浪費。)你可以編寫一個函數來複制字符串或使用非標準的,但廣泛可用的strdup;這會讓你的代碼更易於閱讀。

  • 所有的臨時分配都很難遵循。例如,在分支if (!com && !redir)中,您分配給tmpa,但您絕不會將該值存儲在cmd中。重定向分支也是如此。

  • 當你開始一個新的命令時,它也不清楚。在解析第一個標記之前,遇到一個管道或遇到分號後,應該有一個新的命令。您還可以爲重定向和後臺&符創建新命令。

  • 比較token == ">>"將總是假:token是在line">>"的地址是一個字符串存儲Ñ靜態存儲器。您應該使用strcmp來比較兩個字符串。

通常,您希望在cmdp增加時分配新列表。在這種情況下,argp重置爲零。否則,您只需追加到當前命令。

我認爲你把所有東西都看作特殊的東西使事情變得複雜。我建議簡化代碼並暫時保留重定向和背景。命令被調用時,它們可以輕鬆解決。 (您的代碼使用redircom來設置狀態,但是例如在重定向之後從不強制文件名)。所有令牌都可以輕鬆實現。

以下代碼僅將管道和分號視爲命令分隔符。當命令是管材,管件令牌前置到下面的命令:

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

#define MAX_SIZE 32 
#define TOK_DELIM " \t\r\n\a" 

char *sdup(const char *str) 
{ 
    size_t len = strlen(str); 
    char *dup = malloc(len + 1); 

    if (dup) { 
     memcpy(dup, str, len); 
     dup[len] = '\0'; 
    } 

    return dup; 
} 

char ***parse(char *line0) 
{ 
    char *token; 
    char *line = sdup(line0); 

    token = strtok(line, TOK_DELIM); 
    if (token == NULL) return NULL; 

    char ***cmd = malloc(MAX_SIZE * sizeof(char **)); 

    int cmdp = 0; 
    int argp = 0; 

    cmd[0] = malloc(MAX_SIZE * sizeof(*cmd[0])); 

    while (token) { 
     if (strcmp(token, ";") == 0 || strcmp(token, "|") == 0) { 
      // begin new command 
      cmd[cmdp][argp++] = NULL; 

      cmdp++; 
      if (cmdp + 1 == MAX_SIZE) break; 

      argp = 0; 
      cmd[cmdp] = malloc(MAX_SIZE * sizeof(*cmd[0])); 

      // prepend pipe token 
      if (*token == '|') { 
       cmd[cmdp][argp++] = sdup(token); 
      } 
     } else { 
      // append to current command 
      if (argp + 1 < MAX_SIZE) { 
       cmd[cmdp][argp++] = sdup(token); 
      } 
     } 

     token = strtok(NULL, TOK_DELIM); 
    } 

    // null-terminate arg and cmd lists 
    cmd[cmdp][argp] = NULL; 
    cmdp++; 
    cmd[cmdp] = NULL; 

    return cmd; 
} 

int main() 
{ 
    char ***cmd = parse("echo start ; ls -l | wc > output ; echo stop"); 
    char ***p = cmd; 

    while (*p) { 
     char **q = *p; 

     while (*q) { 
      printf("'%s' ", *q); 
      free(*q); 
      q++; 
     } 
     puts(""); 
     free(*p); 
     p++; 
    }  

    free(cmd); 

    return 0; 
} 

而且備註:

  • 我不知道目前的格式是否適合這項任務。最好有一個樹狀結構來管理管道,分號,也可以使用&&||,然後讓葉節點的參數是鏈接列表。

  • Tokenisation與strtok需要所有標記之間爲空格,但通常可以書寫標點符號而不用明確的空格,例如:"./a.out>kk&"。所以你需要更好的解析方法。

  • 此刻,您爲每個字符串分配空間,您必須稍後釋放空間。如果您創建一個令牌結構,將令牌描述爲原始字符串中的只讀視圖,您可以在沒有分配的情況下執行此操作。儘管如此,視圖並不是以null結尾的,所以你需要比較方法來處理開始指針加長度。