为什么这个sscanf修改了一些我不想修改的数据?
Why does this sscanf modify some data i don't want to be modified?
我正在读取和保存格式化文件中的字符串,出于某种原因,我发现 sscanf()
更改了 testa_e->ident
内容。
我放了一些printf
,发现问题就出现在sscanf()
之后;我还通过打印检查了 temp2
、temp5
和 testa_e
的地址,但它们不同。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define string 30
#define line 100
typedef const char *identifier;
struct nodo_id {
identifier ident;
struct nodo_id *next;
};
typedef struct nodo_id nodo_id;
nodo_id *testa_e = NULL;
void addent(const char *id_ent) {
if (testa_e == NULL) {
testa_e = malloc(sizeof(nodo_id));
testa_e->ident = id_ent;
testa_e->next = NULL;
} else {
nodo_id *curs = testa_e;
while (curs != NULL) {
curs = curs->next;
}
curs = malloc(sizeof(nodo_id));
curs->ident = id_ent;
curs->next = NULL;
}
}
int main() {
char temp[line];
char temp2[string];
char temp5[string];
fgets(temp, line, stdin);
while (strncmp(temp, "end", 3) != 0) {
if (strncmp(temp, "addent", 6) == 0) {
if (testa_e != NULL)
printf("\nbefore sscanf: %s\n", testa_e->ident);
sscanf(temp, "%s %s", temp5, temp2);
if (testa_e != NULL)
printf("\nafter sscanf: %s\n", testa_e->ident);
addent(temp2);
}
fgets(temp, line, stdin);
}
}
这段代码重现了完全相同的问题;启动后在终端上写 addent firstword
和 addent secondword
,就在 sscanf
附近,它应该会显示 testa_e->ident
内容已更改,我想知道为什么以及如何解决这个问题, 因为我真的不知道...
在函数addent
这个循环
while(curs!=NULL){
curs=curs->next;
}
迭代直到 curs
等于 NULL
。
那你就是在改变指针
curs=malloc(sizeof(nodo_id));
curs->ident=id_ent;
curs->next=NULL;
列表本身没有改变。您只更改了局部变量 curs
。
按以下方式更改循环
while ( curs->next != NULL ){
curs = curs->next;
}
然后
curs->next = malloc( sizeof( nodo_id ) );
curs->next->ident = id_ent;
curs->next->next = NULL;
另一个问题是您使用的是指向本地数组的指针
char temp2[string];
//...
addent(temp2);
因此最后一个将存储在数组中的将被所有节点指向。您需要为将存储在列表中的每个字符串动态分配内存,并将地址分配给数据成员 ident
。在这种情况下,您必须从其声明中删除限定符 const
。
考虑到使函数依赖于全局变量是个坏主意,
更好的函数定义addent
可以看下面的方式
struct nodo_id{
char *ident;
struct nodo_id* next;
};
typedef struct nodo_id nodo_id;
int addent( nodo_id **head, const char *id_ent )
{
nodo_id *new_nodo_id = malloc( sizeof( nodo_id ) );
int success = new_nodo_id != NULL;
if ( success )
{
new_nodo_id->ident = malloc( strlen( id_ent ) + sizeof( ( char )'[=15=]' ) );
success = new_nodo_id->ident != NULL;
if ( ! success )
{
free( new_nodo_id );
}
else
{
strcpy( new_nodo_id->ident, id_ent );
new_nodo_id->next = NULL;
while ( *head != NULL ) head = &( *head )->next;
*head = new_nodo_id;
}
}
return success;
}
而且函数可以这样调用
addent( &testa_e, temo2 );
为什么在函数中使用指向头部的指针?
首先,如果我们想改变原来的头部,我们需要通过引用传递它。其次在循环中
while ( *head != NULL ) head = &( *head )->next;
指针再次指向最后一个节点的数据成员next
。所以我们改变的不是函数实现中的局部变量 curs
,而是最后一个节点的数据成员 next
。所以我们正在更改列表本身。
注意定义这样的typedef
typedef const char* identifier;
是一种不好的做法。
主要问题是(除了@VladFromMoscow 在他的回答中提到的那个),在 addent()
中你只存储 pointer id_ent
在你的结构:
curs->ident=id_ent;
但这只是 temp2
的地址,所以如果您通过调用 sscanf()
将其他内容复制到 temp2
,您将在 [=17] 中看到新值=] 也是。
将上面的行形式改为
curs->ident=strdup(id_ent);
创建副本。
并且不要忘记在释放 curs
之前调用 free(curs->ident)
我正在读取和保存格式化文件中的字符串,出于某种原因,我发现 sscanf()
更改了 testa_e->ident
内容。
我放了一些printf
,发现问题就出现在sscanf()
之后;我还通过打印检查了 temp2
、temp5
和 testa_e
的地址,但它们不同。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define string 30
#define line 100
typedef const char *identifier;
struct nodo_id {
identifier ident;
struct nodo_id *next;
};
typedef struct nodo_id nodo_id;
nodo_id *testa_e = NULL;
void addent(const char *id_ent) {
if (testa_e == NULL) {
testa_e = malloc(sizeof(nodo_id));
testa_e->ident = id_ent;
testa_e->next = NULL;
} else {
nodo_id *curs = testa_e;
while (curs != NULL) {
curs = curs->next;
}
curs = malloc(sizeof(nodo_id));
curs->ident = id_ent;
curs->next = NULL;
}
}
int main() {
char temp[line];
char temp2[string];
char temp5[string];
fgets(temp, line, stdin);
while (strncmp(temp, "end", 3) != 0) {
if (strncmp(temp, "addent", 6) == 0) {
if (testa_e != NULL)
printf("\nbefore sscanf: %s\n", testa_e->ident);
sscanf(temp, "%s %s", temp5, temp2);
if (testa_e != NULL)
printf("\nafter sscanf: %s\n", testa_e->ident);
addent(temp2);
}
fgets(temp, line, stdin);
}
}
这段代码重现了完全相同的问题;启动后在终端上写 addent firstword
和 addent secondword
,就在 sscanf
附近,它应该会显示 testa_e->ident
内容已更改,我想知道为什么以及如何解决这个问题, 因为我真的不知道...
在函数addent
这个循环
while(curs!=NULL){
curs=curs->next;
}
迭代直到 curs
等于 NULL
。
那你就是在改变指针
curs=malloc(sizeof(nodo_id));
curs->ident=id_ent;
curs->next=NULL;
列表本身没有改变。您只更改了局部变量 curs
。
按以下方式更改循环
while ( curs->next != NULL ){
curs = curs->next;
}
然后
curs->next = malloc( sizeof( nodo_id ) );
curs->next->ident = id_ent;
curs->next->next = NULL;
另一个问题是您使用的是指向本地数组的指针
char temp2[string];
//...
addent(temp2);
因此最后一个将存储在数组中的将被所有节点指向。您需要为将存储在列表中的每个字符串动态分配内存,并将地址分配给数据成员 ident
。在这种情况下,您必须从其声明中删除限定符 const
。
考虑到使函数依赖于全局变量是个坏主意,
更好的函数定义addent
可以看下面的方式
struct nodo_id{
char *ident;
struct nodo_id* next;
};
typedef struct nodo_id nodo_id;
int addent( nodo_id **head, const char *id_ent )
{
nodo_id *new_nodo_id = malloc( sizeof( nodo_id ) );
int success = new_nodo_id != NULL;
if ( success )
{
new_nodo_id->ident = malloc( strlen( id_ent ) + sizeof( ( char )'[=15=]' ) );
success = new_nodo_id->ident != NULL;
if ( ! success )
{
free( new_nodo_id );
}
else
{
strcpy( new_nodo_id->ident, id_ent );
new_nodo_id->next = NULL;
while ( *head != NULL ) head = &( *head )->next;
*head = new_nodo_id;
}
}
return success;
}
而且函数可以这样调用
addent( &testa_e, temo2 );
为什么在函数中使用指向头部的指针?
首先,如果我们想改变原来的头部,我们需要通过引用传递它。其次在循环中
while ( *head != NULL ) head = &( *head )->next;
指针再次指向最后一个节点的数据成员next
。所以我们改变的不是函数实现中的局部变量 curs
,而是最后一个节点的数据成员 next
。所以我们正在更改列表本身。
注意定义这样的typedef
typedef const char* identifier;
是一种不好的做法。
主要问题是(除了@VladFromMoscow 在他的回答中提到的那个),在 addent()
中你只存储 pointer id_ent
在你的结构:
curs->ident=id_ent;
但这只是 temp2
的地址,所以如果您通过调用 sscanf()
将其他内容复制到 temp2
,您将在 [=17] 中看到新值=] 也是。
将上面的行形式改为
curs->ident=strdup(id_ent);
创建副本。
并且不要忘记在释放 curs
free(curs->ident)