重用动态分配的指针数组时遇到问题:C 语言

问题描述 投票:0回答:1

.h 文件

#ifndef MDSHELL_H
#define MDSHELL_H

#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <ctype.h>

void getCommand(char * command, char *commandcopy);
void getNumOfTokens(char *command, char ***arrayofTokens, int *numberofTokens);
void getTokens(char *commandcopy, char ***arrayofTokens, int *numberofTokens);


#endif

主.c文件:

#include <stdio.h>
#include <stdlib.h>
#include "mdshell.h"



int main()
{
  printf("Created by Matthew Lewis\n");
  printf("Type \"help\" to see implemented commands\n\n");

  char *command= NULL;
  char *commandcopy = NULL;
  char **arrayofTokens;
  int numberofTokens;

  while(command == NULL || (strcmp(command, "exit")) != 0)
  {
    if(command == NULL)
    {
      command = malloc(100 * sizeof(char));
      commandcopy = malloc(100 * sizeof(char));
    }
    else{
      free(command);
      free(commandcopy);
      command = malloc(100 * sizeof(char));
      commandcopy = malloc(100 * sizeof(char));
    }
    getCommand(command, commandcopy);
    getNumOfTokens(command, &arrayofTokens, &numberofTokens);
    getTokens(commandcopy, &arrayofTokens, &numberofTokens);

    for(int i = 0; i < numberofTokens; i++)
    { //used for testing to see tokens being returned. will not be in final build
      printf("%s\n", arrayofTokens[i]);
    }

    if((strcmp(command, "help")) == 0)
    {
      printf("pwd - Print the current working directory\n");
      printf("exit - Exit shell\n");
      printf("help - Display this message\n");
      printf("<UNIX cmd>  - Spawn child process, excecute <UNIX cmd> in the foreground\n\n");
    }

    for (int i = 0; i < numberofTokens; i++)
    {
      free(arrayofTokens[i]); //free each element in array of pointers
    }
    free(arrayofTokens);  //frees the array of pointers

  }

mdshell.c 文件

#include "mdshell.h"
#include <stdio.h>
#include <stdlib.h>


void getCommand(char * command, char *commandcopy)
{
  char word[100];
  int size = 0;
  printf("mdshell> ");
  fgets(word, 100, stdin);

  // removes the newline char at the end of whatever user entered
  size = strlen(word);
  word[size - 1] = '\0';

  strcpy((command), word);
  strcpy((commandcopy), word);
}


void getNumOfTokens(char *command, char ***arrayofTokens, int *numberofTokens)
{   
  int isDone = 0;
  char delim[] = " ";
  

  while(!isDone)    // used to find out how many words there are
  {
    if((*numberofTokens) == 0)
    {
      char *word = strtok(command, delim);
      if(word == NULL)
      {
        isDone = 1;
        (*numberofTokens)++;
      }
      else
      (*numberofTokens)++;
    }
    else
    {
      char *word = strtok(NULL, delim);
      if(word == NULL)
      {
        isDone = 1;
        (*numberofTokens)++;
      }
      else
      (*numberofTokens)++;
    }
  }

  //dynamically allocate the array of pointers to the number of elements needed
  (*arrayofTokens) = malloc((*numberofTokens) * sizeof(char *));

}

void getTokens(char *commandcopy, char ***arrayofTokens, int *numberofTokens)
{
  char delim[] = " ";
  int size = 0;
  int arrElement = 0;
  
  char *word = strtok(commandcopy, delim);
  if(word != NULL)
  {
    size = strlen(word);
    (*arrayofTokens)[arrElement] = malloc((size) * sizeof(char));
    strcpy((*arrayofTokens)[arrElement], word);
    arrElement++;
  }
  else
  {
    (*arrayofTokens)[arrElement] = NULL;
  }
  while(word != NULL)
  {
    word = strtok(NULL, delim);
    if(word == NULL)
    {
      (*arrayofTokens)[arrElement] = malloc((size) * sizeof(char));
    }
    else
    {
      size = strlen(word);
      (*arrayofTokens)[arrElement] = malloc((size) * sizeof(char));
      strcpy((*arrayofTokens)[arrElement], word);
      arrElement++;
    }
  }
}

我正在为 Linux 编写一个小 shell 程序。我让用户输入一个命令,然后将其分解为一个以 null 结尾的数组或指向 execvp() 函数所需的 char 的指针。我还没有讲到那部分。 execvp() 需要一个以 null 结尾的字符串数组。我已将字符串放入已计算出的字符串数组中,除了在字符串末尾获取 null 之外。即使我将该元素分配给 NULL,它也只是显示空白而不是 (NULL)

这是我得到的输出示例:

output example

我有一种感觉,我没有为 arrayofTokens 正确释放内存。我正在释放每个动态分配的部分,所以我不知道为什么会出现分段错误。任何帮助将不胜感激,以便我可以继续完成其余的作业。祝你有美好的一天!

我希望能够在 shell 中使用不同的命令时多次使用 arrayofTokens。这在第一次通过后不起作用。

arrays c pointers dynamic-memory-allocation
1个回答
0
投票

很明显,您确实非常努力地想让它发挥作用。荣誉!

隐藏在代码堆中的是@BoP 在评论中指出的错误。也许教训是努力编写更少的代码。

评论:

  1. 有多少次拨打
    malloc()
    的电话未被接听?不要假设事情总是有效!
  2. MDShell.h - 很好地使用“标头防护”,但是...避免在不必要时将 #includes 添加到头文件中。它表明这些标头的内容与该标头文件的其余部分相关。他们在这里的存在既不必要又会分散注意力。
  3. 较短的变量名称会更容易扫描。无论如何,使用描述性名称,但不要强迫读者放慢速度来检查多音节的怪物,尤其是当一个或多个几乎无法区分时。
  4. void getCommand()
    传递了一个缓冲区,但最初使用自己的缓冲区。为什么?而且,如果程序需要第二个副本,则这里不是进行克隆的地方。
  5. void getNumOfTokens()
    采用 3 个参数,然后通过成为
    void
    函数来浪费其返回值。令牌的数量可以返回给调用者,并且函数签名得到简化。 (循环
    if/else
    可以大大简化。
  6. void getTokens()
    ...同样,代码重复隐藏了第二个错误。不仅分配的缓冲区大小对于尾随
    '\0'
    来说太短,而且
    argv[argc+1]
    的值本来就是
    NULL
    。太多胆怯和试探性代码让人很难看出出了什么问题。
  7. main()
    ???用
    malloc()
    free()
    冲击动态内存表示需要重新考虑代码。

下面是代码的单个文件重写。我希望这能为您指明更好的方向。

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

#define MAX_CMD_LEN 128

char **tokenise( char *str, int *n ) {
    static const char *dlm = " \t";
    char cpy[ MAX_CMD_LEN ]; // disposable copy
    strcpy( cpy, str );

    // chop disposable copy to determine # of tokens (pointers)
    // start with '1' to allow for extra NULL as sentinel at the end of array
    int i = 1;
    for( char *cp = cpy; (cp = strtok(cp, dlm)) != NULL; cp = NULL )
        i++;

    char **arr = calloc( cnt, sizeof *arr ); // use calloc for consistency
    /* verification of allocation success left as an exercise */

    // chop actual buffer storing pointers to each segment
    i = 0;
    for( char *xp = str; (xp = strtok(xp, dlm)) != NULL; xp = NULL )
        arr[ i++ ] = xp;

    *n = cnt - 1;
    return arr;
}

int main( void ) {
    for( ;; ) {
        char iBuf[ MAX_CMD_LEN ]; // small stack buffer

        printf( "mdshell> " );
        fgets( iBuf, sizeof iBuf, stdin );
        iBuf[ strcspn( iBuf, "\n" ) ] = '\0'; // trim LF from end

        if( strcmp( iBuf, "help" ) == 0 ) {  // early resolution
            puts( "pwd - Print the current working directory" );
            puts( "exit - Exit shell" );
            puts( "help - Display this message" );
            puts( "<UNIX cmd>  - excecute <UNIX cmd> in the foreground\n" );
            continue;
        }

        int arrgc;
        char **arrgv = tokenise( iBuf, &arrgc ); // KISS

        // demonstrate results
        for( int i = 0; i <= arrgc; i++)
            printf( "[%d] '%s'\n", i, arrgv[i] );

        free( arrgv ); // easy...
    }

    return 0;
}

结果:

mdshell> fools rush in where wise men fear to go
[0] 'fools'
[1] 'rush'
[2] 'in'
[3] 'where'
[4] 'wise'
[5] 'men'
[6] 'fear'
[7] 'to'
[8] 'go'
[9] '(null)'
mdshell>      // just pressed RETURN here
[0] '(null)'
mdshell>

注意! 我的编译器很友善,打印

(null)
而不是崩溃。您从其他实施中获得的里程可能会有所不同。

© www.soinside.com 2019 - 2024. All rights reserved.