0

私の関数には、とりわけ、引数を持つコマンドを構成する単語へのポインターの NULL 終了配列を含む構造体が渡されています。

引数のリストでグロブ一致を実行して、それらをファイルの完全なリストに展開し、渡された引数配列を新しい展開された配列に置き換えたいと考えています。

グロビングは正常に機能しています。つまり、g.gl_pathv に予期されるファイルのリストが入力されています。ただし、この配列を指定された構造体にコピーするのに問題があります。

#include <glob.h>

struct command {
  char **argv;
  // other fields...
}

void myFunction( struct command * cmd )
{
  char **p = cmd->argv;
  char* program = *p++; // save the program name (e.g 'ls', and increment to the first argument

  glob_t g;
  memset(&g, 0, sizeof(g));
  g.gl_offs = 1;
  int res = glob(*p++, GLOB_DOOFFS, NULL, &g);
  glob_handle_res(res);
  while (*p)
  {
      res = glob(*p, GLOB_DOOFFS | GLOB_APPEND, NULL, &g);
      glob_handle_res(res);
  }

  if( g.gl_pathc <= 0 )
  {
      globfree(&g);
  }

  cmd->argv = malloc((g.gl_pathc + g.gl_offs) * sizeof *cmd->argv);

  if (cmd->argv == NULL) { sys_fatal_error("pattern_expand: malloc failed\n");}
   // copy over the arguments
  size_t i = g.gl_offs;
  for (; i < g.gl_pathc + g.gl_offs; ++i)
      cmd->argv[i] = strdup(g.gl_pathv[i]);

  // insert the original program name
  cmd->argv[0] = strdup(program);
  ** cmd->argv[g.gl_pathc + g.gl_offs] = 0; **
  globfree(&g);
}

void 
command_free(struct esh_command * cmd)
{
    char ** p = cmd->argv;
    while (*p) {
        free(*p++); // Segfaults here, was it already freed?
    }
    free(cmd->argv);
    free(cmd);
}

編集 1: また、プログラムを cmd->argv[0] としてそこに戻す必要があることに気付きました
編集 2: calloc への呼び出しを追加しました
編集 3: Alok からのヒントを使用してメモリ管理を編集します
編集 4: Alok からのその他のヒント
編集 5:ほとんど動作しています..コマンド構造体を解放するときにアプリのセグメンテーション違反

最後に:終端のNULLが欠けていたようですので、次の行を追加してください:

cmd->argv[g.gl_pathc + g.gl_offs] = 0;  

それを機能させるように見えました。

4

2 に答える 2

1

argvのポインタの配列ですchar *。これは、値argvのためのスペースがあることを意味しargc char *ます。それ以上のchar *値をコピーしようとすると、オーバーフローが発生します。

ほとんどの場合、呼び出しの結果、フィールド (つまり、 ) 内globに複数のargc要素が返されます。これは未定義の動作です。gl_pathvgl_pathc > argc

以下のコードに似ています。

/* Wrong code */
#include <string.h>

int a[] = { 1, 2, 3 };
int b[] = { 1, 2, 3, 4 };
memcpy(a, b, sizeof b);

解決策:glob_t構造体を直接操作するか、新しいスペースを割り当てgl_pathvて新しいにコピーする必要がありchar **ます。

char **paths = malloc(g.gl_pathc * sizeof *paths);
if (paths == NULL) { /* handle error */ }
for (size_t i=0; i < g.gl_pathc; ++i) {
    /* The following just copies the pointer */
    paths[i] = g.gl_pathv[i];

    /* If you actually want to copy the string, then
       you need to malloc again here.

       Something like:

       paths[i] = malloc(strlen(g.gl_pathv[i] + 1));

       followed by strcpy.
     */
}

/* free all the allocated data when done */

編集:編集後:

cmd->argv = calloc(g.gl_pathc, sizeof(char *) *g.gl_pathc);

動作するはずですが、 to のそれぞれはによってargv[1]「所有」されています。あなたの呼び出しはポインタをコピーするだけです。後で を実行すると、これらのポインターはもはや何の意味もありません。したがって、使用するために文字列をコピーする必要があります。argv[g.gl_pathc + g.gl_offs - 1]char *struct globmemcpyglobfree()

size_t i;
cmd->argv = malloc((g.gl_pathc+g.gl_offs) * sizeof *cmd->argv);
for (i=g.gl_offs; i < g.gl_pathc + g.gl_offs; ++i)
    cmd->argv[i] = strdup(g.gl_pathv[i]);

これにより、文字列の独自のプライベート コピーが確実に作成されます。argv完了したら、必ずそれらを解放してください (および)。

コードには他にもいくつかの問題があります。

  1. 逆参照の値を使用していないため、実行しています。*p++実行する必要があります。p++
  2. の戻り値を実際に確認する必要がありglobます。
  3. 変数にはではなく要素pathsが必要です。(または、より正確には、 timesバイトを割り当てる必要があります。)g.gl_pathc + 1g.gl_pathcg.gl_pathc + g.gl_offssizeof *paths
  4. 文字for列をコピーするループはfor (j=1; j < g.gl_pathc + g.gl_offs; ++j).
  5. シェルがグロブを拡張しないようにしてください。./a.out '*'つまり、代わりに呼び出します./a.out *
于 2010-03-15T01:37:33.710 に答える
0

sizeof(char *) で g.gl_pathc を複数にする必要はありませんか?

于 2010-03-15T01:22:09.540 に答える