C 使用字符串复制到链表中';行不通

C 使用字符串复制到链表中';行不通,c,string,memory-management,linked-list,singly-linked-list,C,String,Memory Management,Linked List,Singly Linked List,我有一项任务,要将一些输入复制到链表中,但当我尝试使用strncpy复制时,它不起作用,并且出现了一个错误 Exception thrown at 0x0F4C0E15 (ucrtbased.dll) in ProjectA.exe: 0xC0000005: Access violation writing location 0xCDCDCDCD. 代码: 在复制目标中的字符串之前,需要使用malloc分配空间。第一个malloc只为帧分配空间,而不为其内部字符*分配空间 createfra

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

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


在复制目标中的字符串之前,需要使用
malloc
分配空间。第一个malloc只为
分配空间,而不为其内部
字符*
分配空间

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;
createframe
返回时,您应该检查它是否返回了
NULL
,如果它返回了
NULL
则通常通过释放分配的内存和终止程序来处理错误


基本诊断和处方 在
malloc()
之后,
list->name
是一个未初始化的指针。您需要为字符串分配足够的空间,然后复制到该空间中。与
路径类似
。不要忘记在字符串的末尾允许空字节。您不为
FrameNode
分配空间;你也不退

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;
}
这里面有很多修正

  • 它为
    帧节点
    分配空间
  • 代码检查分配失败
  • 它尝试在检查故障之前分配所有内存,这简化了错误处理。只有一个错误返回。如果第一次分配失败,其余的可能也会失败,将变量初始化为
    NULL
    ,可以安全地传递到
    free()
  • 它计算字符串的长度
  • 它为字符串及其空终止符分配空间
  • 因为它知道字符串的长度,所以可以使用
    memmove()
    (或
    memcpy()
    )来复制数据
  • 它使用
    sizeof(*variable)
    符号,而不是
    sizeof(VariableType)
  • 它返回
    FrameNode*
    ,而不是
    Frame*
  • 它避免使用
    strncpy()
    ,因为这不能保证复制的字符串以null结尾,这会导致其他地方出现问题
  • 它不注意STR_LEN——不清楚它有什么值
结构的替代设计 如果对
name
path
的大小有一个固定的上限,则最好使用以下结构:

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]='\0'
    列表->路径[STR_LEN]='\0'-这就是为什么在成员定义中有一个
    +1

这种变化将分配的数量从4减少到2。您甚至可以将
next
指针包含在
Frame
结构中,并完全取消
FrameNode
结构-再次减少所需的内存管理量,从而简化代码。也可能有很好的理由将
FrameNode
框架
分开,但问题中的信息并不清楚,也不需要这样做;这是你要考虑的问题,就是这样。初学者的函数应该被声明为

FrameNode * createframe( const char name[], const char path[], int duration );
因为函数中的
name
path
都没有更改

您没有为
list->name
list->path
frame
分配内存

此外,函数返回
frame*
类型的
list
,而不是
frame*
类型的
frame

当动态分配字符数组时,命名常量
STR\u LEN
就没有什么意义了,就像您使用结构
Frame
的数据成员
name
path
时一样

首先,应该为类型为
FrameNode
的对象动态分配内存

然后,您应该为
Frame
类型的对象及其数据成员
name
path
分配内存

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

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 );

应替换为

name_data = malloc( STR_LEN );

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


否则,将存在与存储数据相关的不一致性:一些节点将存储字符串,而其他节点将包含非字符串。

malloc()
之后,
list->name
是未初始化的指针。您需要为字符串分配足够的空间,然后复制到该空间中。与
路径类似
。别忘了在字符串的末尾允许空字节。你可以在代码中告诉我,我不明白你给我提供了什么
malloc(sizeof(Frame))
帧分配存储,但不为要存储在组件
名称
路径
中的字符串分配存储。请注意,这些是指针,但不是数组。因此,
strncpy((list->name),name,STR_LEN)是U.B.您既没有初始化组件
列表->名称
,也没有为复制到其中的数据分配存储空间。对于
list->path
也是如此。一种解决方案是将
Frame
更改为
struct Frame{char name[STR_LEN];unsigned int duration;char path[STR_LEN];}假设
stru_LEN
表示一个足够的常量。另一种解决方案是使用

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] = '\0';
}           
path_data = malloc( STR_LEN );

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