用字符串复制到链接列表不起作用
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;
}
其中有很多修复程序。
- 它为
Frame
和 FrameNode
分配了 space。
- 代码检查分配失败。
- 它尝试在检查失败之前分配所有内存,这简化了错误处理。只有一个错误 return。如果第一次分配失败,其余的也有可能失败,将变量初始化为
NULL
,可以安全地传递给 free()
.
- 它计算字符串的长度。
- 它为字符串及其空终止符分配 space。
- 因为它知道字符串有多长,所以它可以使用
memmove()
(或memcpy()
)来复制数据。
- 它使用
sizeof(*variable)
表示法而不是 sizeof(VariableType)
。
- 它 return 是
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] = '[=33=]';
和 list->path[STR_LEN] = '[=34=]';
— 这就是成员定义中有 + 1
的原因。
这种变体将分配的数量从 4 减少到 2。您甚至可以在 Frame
结构中包含 next
指针并完全取消 FrameNode
结构 - 再次减少所需的内存管理量,从而简化代码。将 FrameNode
与 Frame
分开也可能有充分的理由,但问题中的信息并不清楚——也不需要;这是你考虑的事情,仅此而已。
对于初学者来说,函数应该这样声明
FrameNode * createframe( const char name[], const char path[], int duration );
因为函数中name
和path
都没有改变。
您没有为 list->name
、list->path
和 frame
分配内存。
而且 FrameNode *
类型的 frame
函数 returns list
类型 Frame *
.
当动态分配字符数组时,命名常量 STR_LEN
没有多大意义,就像结构 [=28= 的数据成员 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 );
和
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.
我有一项任务是将一些输入复制到链接列表,但是当我尝试使用 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;
}
其中有很多修复程序。
- 它为
Frame
和FrameNode
分配了 space。 - 代码检查分配失败。
- 它尝试在检查失败之前分配所有内存,这简化了错误处理。只有一个错误 return。如果第一次分配失败,其余的也有可能失败,将变量初始化为
NULL
,可以安全地传递给free()
. - 它计算字符串的长度。
- 它为字符串及其空终止符分配 space。
- 因为它知道字符串有多长,所以它可以使用
memmove()
(或memcpy()
)来复制数据。 - 它使用
sizeof(*variable)
表示法而不是sizeof(VariableType)
。 - 它 return 是
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] = '[=33=]';
和list->path[STR_LEN] = '[=34=]';
— 这就是成员定义中有+ 1
的原因。
这种变体将分配的数量从 4 减少到 2。您甚至可以在 Frame
结构中包含 next
指针并完全取消 FrameNode
结构 - 再次减少所需的内存管理量,从而简化代码。将 FrameNode
与 Frame
分开也可能有充分的理由,但问题中的信息并不清楚——也不需要;这是你考虑的事情,仅此而已。
对于初学者来说,函数应该这样声明
FrameNode * createframe( const char name[], const char path[], int duration );
因为函数中name
和path
都没有改变。
您没有为 list->name
、list->path
和 frame
分配内存。
而且 FrameNode *
类型的 frame
函数 returns list
类型 Frame *
.
当动态分配字符数组时,命名常量 STR_LEN
没有多大意义,就像结构 [=28= 的数据成员 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 );
和
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.