用字符串复制到链接列表不起作用

Copy with string to linked list doesn't work

我有一项任务是将一些输入复制到链接列表,但是当我尝试使用 strncpy 复制它时,它不起作用并且出现错误

Exception thrown at 0x0F4C0E15 (ucrtbased.dll) in 
ProjectA.exe: 0xC0000005: Access violation writing location 0xCDCDCDCD.

代码:

typedef struct Frame
{
    char*       name;
    unsigned int    duration;
    char*       path;  
} Frame;

typedef struct FrameNode
{
    Frame* frame;
    struct FrameNode* next;
} FrameNode;

FrameNode* createframe(char name[], char path[], int duration)
{
    Frame* list = (Frame*)malloc(sizeof(Frame));
    strncpy((list->name), name, STR_LEN);
    list->duration = duration;
    strncpy(list->path, path, STR_LEN);
    FrameNode* frame = list;
    frame->next = NULL;
    return list;
}

在目标中复制字符串之前,您需要用 malloc 分配 space。第一个 malloc 只为 Frame 分配 space 而不是为其内部的 char *

createframe 中的代码应该是:

    Frame* list = malloc(sizeof(Frame));
    list->name = malloc(STR_LEN);
    strncpy((list->name), name, STR_LEN);
    list->duration = duration;
    list->path= malloc(STR_LEN);
    strncpy(list->path, path, STR_LEN);
    //FrameNode* frame = list; // <- nope. FrameNode* should point to a FrameNode not a Frame
    FrameNode* frame = malloc(sizeof(FrameNode)); 
    frame->frame = list;
    frame->next = NULL;
    return frame;

最好在使用动态分配变量之前检查 malloc 是否成功,如下所示:

 Frame *list = malloc(sizeof(Frame));
 if(list==NULL){
     perror("problem allocating frame");
     return NULL;
 }
 list->name = malloc(STR_LEN);
 if(list->name==NULL){
     free(list);//free the already allocated memory
     perror("error message");
     return NULL;
  }
 strncpy((list->name), name, STR_LEN);
 ...
 return frame;
 }

createframe returns 你应该检查它是否已经返回NULL 或没有,如果它返回NULL 处理错误,通常通过释放分配的内存并终止程序。


You should not cast the result of malloc

基本诊断和处方

malloc()之后,list->name是一个未初始化的指针。您需要为字符串分配足够的 space,然后复制到 space。与 path 类似。不要忘记在字符串的末尾允许空字节。您没有为 FrameNode 分配 space;你也不 return 它。

FrameNode *createframe(char name[], char path[], int duration)
{
    Frame *list = (Frame*)malloc(sizeof(*list));
    size_t name_len = strlen(name) + 1;
    char  *name_cpy = malloc(name_len); 
    size_t path_len = strlen(path) + 1;
    char  *path_cpy = malloc(path_len);
    FrameNode *frame = malloc(sizeof(*frame));
    if (list == NULL || name_cpy == NULL || path_cpy == NULL || frame == NULL)
    {
        free(name_cpy);
        free(path_cpy);
        free(frame);
        free(list);
        return NULL;
    }
    list->duration = duration;
    memmove(path_cpy, path, path_len);
    memmove(name_cpy, name, name_len);
    list->name = name_cpy;
    list->path = path_cpy;
    frame->frame = list;
    frame->next = NULL;
    return frame;
}

其中有很多修复程序。

  • 它为 FrameFrameNode 分配了 space。
  • 代码检查分配失败。
  • 它尝试在检查失败之前分配所有内存,这简化了错误处理。只有一个错误 return。如果第一次分配失败,其余的也有可能失败,将变量初始化为 NULL,可以安全地传递给 free().
  • 它计算字符串的长度。
  • 它为字符串及其空终止符分配 space。
  • 因为它知道字符串有多长,所以它可以使用memmove()(或memcpy())来复制数据。
  • 它使用 sizeof(*variable) 表示法而不是 sizeof(VariableType)
  • 它 return 是 FrameNode * 而不是 Frame *
  • 它避免使用 strncpy(),因为那不能保证复制的字符串以 null 结尾,这会导致其他地方出现问题。
  • 它不关注 STR_LEN — 不清楚它有什么价值。

结构的替代设计

如果 namepath 的大小确实有固定的上限,您最好使用如下结构:

typedef struct Frame
{
    unsigned int    duration;
    char            name[STR_LEN + 1];
    char            path[STR_LEN + 1];  
} Frame;
  • 使用固定大小的成员来节省分配。
  • 允许长度为 STR_LEN 的字符串加上空终止符。
  • 无论 STR_LEN.
  • 的值如何,将字符数组放在结构的末尾以最小化填充
  • 然后您需要使用 strncpy() 并确保设置 list->name[STR_LEN] = '[=33=]';list->path[STR_LEN] = '[=34=]'; — 这就是成员定义中有 + 1 的原因。

这种变体将分配的数量从 4 减少到 2。您甚至可以在 Frame 结构中包含 next 指针并完全取消 FrameNode 结构 - 再次减少所需的内存管理量,从而简化代码。将 FrameNodeFrame 分开也可能有充分的理由,但问题中的信息并不清楚——也不需要;这是你考虑的事情,仅此而已。

对于初学者来说,函数应该这样声明

FrameNode * createframe( const char name[], const char path[], int duration );

因为函数中namepath都没有改变。

您没有为 list->namelist->pathframe 分配内存。

而且 FrameNode * 类型的 frame 函数 returns list 类型 Frame *.

当动态分配字符数组时,命名常量 STR_LEN 没有多大意义,就像结构 [=28= 的数据成员 namepath 的情况一样].

您应该首先为 FrameNode.

类型的对象动态分配内存

那么你应该为Frame类型的对象及其数据成员namepath分配内存。

因此函数定义可以如下所示。

FrameNode * createframe( const char name[], const char path[], int duration )
{
    FrameNode *frame = malloc( sizeof( FrameNode ) );

    if ( frame != NULL )
    {
        char *name_data = NULL;
        char *path_data = NULL;

        size_t n = strlen( name );

        name_data = malloc( n + 1 );

        if ( name_data != NULL ) strcpy( name_data, name );

        if ( name_data != NULL )
        {
            n = strlen( path );

            path_data = malloc( n + 1 );

            if ( path_data != NULL ) strcpy( path_data, path );   
        }

        Frame *list = NULL;

        if ( name_data != NULL && path_data != NULL )
        {
            list = malloc( sizeof( Frame ) );

            if ( list != NULL )
            {
                list->name     = name_data;
                list->duration = duration;
                list->path     = path_data;
            }
        }

        if ( list == NULL )
        {
            free( name_data );
            free( path_data );
            free( frame );
        }
        else
        {
            frame->frame = list;
            frame->next  = NULL; 
        }
    }        

    return frame;
}

如果您确实需要限制动态分配的字符串的长度,那么这些语句

size_t n = strlen( name );

name_data = malloc( n + 1 );

if ( name_data != NULL ) strcpy( name_data, name );

n = strlen( path );

path_data = malloc( n + 1 );

if ( path_data != NULL ) strcpy( path_data, path );   

应替换为

name_data = malloc( STR_LEN );

if ( name_data != NULL ) 
{
    strncpy( name_data, name, STR_LEN );
    name_data[STR_LEN - 1] = '[=14=]';
}           

path_data = malloc( STR_LEN );

if ( path_data != NULL ) 
{
    strncpy( path_data, path, STR_LEN );   
    path_data[STR_LEN - 1] = '[=15=]';
}               

否则会出现与存储数据不一致的情况:一些节点将存储字符串,而其他节点将包含 non-strings.