动态字符数组 - 堆栈内存溢出

Dynamic Character Array - Stack

我需要一个字符数组,其中包含基于特定文件夹中文件数量的动态字符数组。我能够通过初始化 char (*FullPathNames)[MAX_FILENAME_AND_PATHNAME_LENGTH] 然后使用 FullPathNames = malloc( sizeof(*FullPathNames) * NumOfFiles * MAX_FILENAME_AND_PATHNAME_LENGTH ) ) 在我知道另一个函数有多少文件之后发现(我没有提供)。这个过程完美无缺。

我只能用ANSI C;我专门使用 LabWindows CVI 8.1 来编译我的代码。我不能使用任何其他编译器。下面的代码正在做我想要的。我可以使用以下代码轻松地填充此数组:

Strcpy(FullPathNames[0],”Test Word”);
char (*FullPathNames)[MAX_FILENAME_AND_PATHNAME_LENGTH];
size_t Size;
NumOfFiles = NumberOfUserFiles(“*.txt”, “C:\ProgramData” );
FullPathNames = malloc( sizeof(*FullPathNames) * NumOfFiles * MAX_FILENAME_AND_PATHNAME_LENGTH ) );
Size = sizeof(*FullPathNames) * NumOfFiles;
Memset(FullPathNames,0,Size);

但是,我希望能够将 FullPathNames 这是一个指向可变数量字符数组的指针数组传递给一个方法。我希望此方法能够删除给定索引处的单个字符数组。

我正在使用以下代码调用该方法。

Remove_Element(FullPathNames,1, NumOfFiles);

Remove_Element的代码:

void Remove_Element( char (*Array)[MAX_FILENAME_AND_PATHNAME_LEN], int Index, int Array_Length )    
{
  int i;    
  char String[MAX_FILENAME_AND_PATHNAME_LEN];        
  char (*NewArray)[MAX_FILENAME_AND_PATHNAME_LEN];    
  int NewLength = Array_Length - 1;    
  size_t Size;

NewArray = malloc( sizeof( *NewArray) *  NewLength * ( MAX_FILENAME_AND_PATHNAME_LEN ) );

Size = sizeof( *NewArray ) * NewLength; 

memset(NewArray, 0, Size);
  for ( i = Index; i < Array_Length - 1; i++ )    
  {

    memcpy(String,Array[i+1],MAX_FILENAME_AND_PATHNAME_LEN); // Remove last index to avoid duplication

    strcpy( Array[Index], String );     
  } 
  Array = NewArray;     
}

我对我目前所拥有的的期望是,除了我删除的索引之外,FullPathNames 的原始数据仍然存在,通过从 index + 1 复制数据,并且包含在 FullPathNames 中的原始指针当然会更新。因为我还想缩小数组,所以我尝试将数组设置为等于新数组。以下信息解释了我调试此行为的尝试。

当我进入该方法时,监视变量会显示以下信息。

FullPathNames = XXXXXX
NewArray = Unallocated
Array = XXXXXX

填充新的临时数组后,会发生以下情况:

FullPathNames = XXXXXX
NewArray = YYYYY
Array = XXXXXX

当我退出该方法时,发生以下情况:

FullPathNames = XXXXXX
NewArray = YYYYY
Array = YYYYY

我试图通过将其作为指针传入来修改 FullPathNames。我最初通过使用 realloc 尝试了这个任务,但这只是导致了一个自由指针异常。

备注: MAX_FILENAME_AND_PATHNAME_LENGTH = 516;

首先是对C语言的语法知识似乎存在一定的欠缺。

char (*FullPathNames)[MAX_FILENAME_AND_PATHNAME_LENGTH]

这是一个例子。此处显示的语法将由 c 程序员读取为:

  • 缺少分号 - 可能是某处#define voodoo!
  • char (*FullPathNames)... - 函数指针!哦等等为什么下一个是方括号?!
  • 也许他想说 char *FullPathNames; 或者他想 char FullPathNames[MAX_FILENAME_AND_PATH_NAME_LENGTH]; 嗯...

所以这里是第一个 101:

char foo[50]; // A fixed size array with capacity 50 (49 chars + '[=11=]' max).
char *foo = NULL; // a uninitialized pointer to some char.
char (*foo)(); // a pointer to a function of signature: char(void).
char *foobar[50]; // This is an array of 50 pointers to char.

根据您的 char foo[50]; 所在的位置(在代码文件中、函数中、结构定义中),用于它的存储会有所不同。

char foo1[50]; // zerovars section.
char foo2[50] = { 0 }; // initvars section
char foo3[50] = "Hello World!"; // also initvars section

void FooTheFoo( const char *foo )
{
     if(NULL != foo )
     {
          printf("foo = %s\n", foo);
     }
}

int main(int argc, const char *argv[])
{ 
    char bar[50] = "Message from the past."; // bar is located on the stack (automatic variable).
    FooTheFoo(bar); // fixed size array or dynamic array - passed as a (const pointer) in C.
    return 0;
}

现在我们了解了基础知识,让我们看一下二维动态数组。
char **matrix = NULL;
指向 char 指针的指针。或指向指向字符的指针数组或指向指向字符数组的指针的指针数组的指针。

如前所述,没有关于 char*char ** 指向什么的 "meta" 信息,最终取消引用的项目将是 char 类型。而且它是一个指向指针的指针。

如果你想用它制作一个二维数组,你必须相应地初始化:

const size_t ROW_COUNT = 5;
const size_T COL_COUNT = 10;
char **myMatrix = malloc(sizeof(char *) * ROW_COUNT);
// check if malloc returned NULL of course!
if( NULL != myMatrix )
{
    for(size_t row = 0; row < ROW_COUNT; row++ )
    {
         myMatrix[row] = malloc(sizeof(char) * COL_COUNT);
         if( NULL == myMatrix[row] ) PanicAndCryOutLoudInDespair();
         for(size_t col = 0; col < COL_COUNT; col++ )
         {
              myMatrix[row][col] = 0;
         }
         // of course you could also write instead of inner for - loop:
         // memset(myMatrix[row], 0, sizeof(char) * COL_COUNT);

    }
}

最后,如何将这样的二维数组传递给函数?由于 char** 构造不包含有关大小的元信息,在一般情况下(内部不是 0 终止字符串),您可以这样做:

void FooIt( const char **matrix, size_t rowCount, size_t colCount )
{    // Note: standard checks omitted! (NULL != matrix, ...)
     putchar(matrix[0][0]);
}

最后,如果你想再次摆脱你的二维动态数组,你需要正确地释放它。

void Cleanup2DArray( char **matrix, size_t rowCount )
{
    for(size_t row = 0; row < rowCount; row++ )
    {
         free(matrix[row];
    }
    free(matrix);
}

唯一要说的是我留给其他温和的贡献者。想到的一件事是如何正确表达那些多维事物的const-ness。

const char **
const char const * const *
etc.

有了这个,您应该能够找出代码中出错的地方并进行修复。

您传递的指针只是一个值。它拥有一个地址意味着您可以取消引用它以修改它 指向 的内容,但这并不意味着直接更改其值(您的赋值语句)会影响调用者参数。与 C 中的其他所有内容一样,如果您想按地址修改某些内容,那么地址正是您需要做的。如果你修改的是一个指针,那么指针的地址(通过指针到指针的参数)就是通常规定的解决方案。

但是,我可以告诉您执行此操作的语法和内务管理……在您的情况下并不吸引人。一个简单的指针就足够简单了,但是指向 N 数组的指针就没那么简单了。如果我是你,他会简单地使用函数本身的 return 结果,否则它当前未被使用并且 void。像这样声明你的函数:

char (*Remove_Element( char (*Array)[MAX_FILENAME_AND_PATHNAME_LEN], 
    int Index, int Array_Length ))[MAX_FILENAME_AND_PATHNAME_LEN]
{
    ....

    return Array; // or whatever else you want to return so
                  // long as the type is correct.
}

让来电者这样做:

Array = RemoveElement(Array, Index, Array_Length);

如果我没理解错的话,你要做的是修改初始化原始数组的代码部分中的 FullPathNames 指针。

根据您对 FullPatchNames 的声明

char (*FullPathNames)[MAX_FILENAME_AND_PATHNAME_LENGTH]

你基本上声明了一个指向 MAX_FILENAME_AND_PATHNAME_LENGTH char 元素数组的指针。通过调用 void Remove_Element(...),您只需将此指针的副本提供给函数内部有效的局部变量 Array。因为这个 Array = NewArray;,只更改函数内部指针的本地副本,而不是 FullPathNames

如果您想更改 FullPathNames 的值,您必须将指向此指针的指针指向您的函数。 Remove_Element 的原型必须如下所示:

 void Remove_Element( char (**Array)[MAX_FILENAME_AND_PATHNAME_LEN],
                      int Index, int Array_Length ) 

现在 Array 是指向 char 的(一维)数组的指针。通过取消引用此指针,您可以更改原始指针 FullPathNames 以指向您在函数内创建的新对象。您必须将对此函数的调用修改为 Remove_Element(&FullPathNames,1, NumOfFiles);。要从数组中读取,您必须使用 * 运算符取消引用它:

memcpy(String,*Array[i+1],MAX_FILENAME_AND_PATHNAME_LEN);
...
Array = NewArray;

警告:这段代码现在会产生内存泄漏,因为您正在丢失对原始对象的引用。您应该使用代码中某处的 free() 函数删除它!

下面显示了我的解决方案的一个工作变体。我必须这样做的原因是因为虽然我能够取消引用 (**Array)[MAX_FILENAME_AND_PATHNAME_LEN] 我只能修改数组中的第一个字符串数组。

字符串数组已初始化并填充了多个字符串。虽然我可以引用 *Array[0] 中包含的字符串,但无法引用任何其他字符串。生成的数组将替换原始数组。该方法仅在初始化待替换数组的初始代码块中有效。

#define MAX_FILENAME_AND_PATHNAME_LEN MAX_FILENAME_LEN + MAX_PATHNAME_LEN


/*

    This method was designed to free the memory allocated to an array.

*/
void FreeFileAndPathArrays( char (*Array)[MAX_FILENAME_AND_PATHNAME_LEN] )
{   
    free( Array );
}                 

/*

    This method was designed to remove an index from an array.  The result of this method will shrink the array by one.

*/
void Remove_Element( char (**ArrayPointer)[MAX_FILENAME_AND_PATHNAME_LEN],int Index, int *Array_Length, char (*Array)[MAX_FILENAME_AND_PATHNAME_LEN] )
{
    int i = 0;
    int j = 0;
    char String[MAX_FILENAME_AND_PATHNAME_LEN];    
    char (*NewArray)[MAX_FILENAME_AND_PATHNAME_LEN];
    char (*GC)[MAX_FILENAME_AND_PATHNAME_LEN];
    int Length = *Array_Length;
    int NewLength = Length - 1;
    size_t Size;
    
    NewArray = malloc( sizeof( *NewArray) *  NewLength * ( MAX_FILENAME_AND_PATHNAME_LEN ) );
    
    Size = sizeof( *NewArray ) * NewLength;
    memset(NewArray, 0, Size);
    UI_Display("Test Block:");
    
    for ( j = 0; j < NewLength; j++ )
    {
        if ( j != Index )
        {
            memcpy(String,Array[j],MAX_FILENAME_AND_PATHNAME_LEN);
            strcpy( Array[Index], String ); 
            Fill(NewArray,String,j);
            UI_Display(String);
        }
    }

    GC = Array;
    *ArrayPointer = NewArray;
    free(GC);          
    *Array_Length = *Array_Length - 1;
    
}

/*

    This method was designed to place a string into an index.

*/
void Fill( char (*Array)[MAX_FILENAME_AND_PATHNAME_LEN], const char * String, int Index)
{  
        strcpy( Array[Index], String ); 
}

/*

    This method was designed to place fill each string array contained within the array of string arrays with 0's.

*/
void PrepareFileAndPathArrays( char (*FullPathNames)[MAX_FILENAME_AND_PATHNAME_LEN], int ROWS )
{
    size_t Size;
    
    Size = sizeof( *FullPathNames ) * ROWS;
    memset(FullPathNames, 0, Size);
  
}