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*
- 它避免使用
,因为这不能保证复制的字符串以null结尾,这会导致其他地方出现问题strncpy()
- 它不注意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';
}