[第三次更新]
我对David的代码进行了一些更改。我将:n->next = s->top;s->top = n;
更改为n->next = NULL;s->top = n;
,就像H.S.建议并在主要功能中添加了不同的功能。我正在尝试在程序运行时从用户的输入中获取char名称和int值。该程序应继续从用户那里获取数据,将其存储在堆栈中并相应地分配内存。仅当用户键入“ end”(字符)或0(整数)时程序才结束。代码被编译,但是仅此而已,之后,任何输入都会停止。另外,我是否在使用fgets(str,20,stdin)
浪费内存?如果我不使用所有的大小怎么办。非常感谢任何反馈!
///PROGRAM STRUCTURE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct stack_node{
int value;
char *name;
struct stack_node * next;
};
typedef struct stack_node stack_node;
struct stack{
stack_node *top;
int size;
};
typedef struct stack stack;
stack *create_stack()
{
stack *s = malloc (sizeof *s);
if (!s) {
perror ("malloc-s");
return NULL;
}
s->size = 0;
s->top = NULL;
return s;
}
int empty_stack (stack * s) {
return s->size == 0;
}
stack_node *push_stack (stack *s, int value, char *name) /// ****
{
stack_node *n = malloc (sizeof *n);
if (!n) {
perror ("malloc-n");
return NULL;
}
n->value = value;
n->name = malloc (strlen (name) + 1);
if (!n->name) {
perror ("malloc-name");
return NULL;
}
strcpy (n->name, name);
n->next = NULL; /// ****
s->top = n;
s->size++;
return n;
}
stack_node *pop_stack (stack *s)
{
if (s->size > 0) {
stack_node *node = s->top;
s->top = s->top->next;
s->size--;
return node;
}
return NULL;
}
void free_node (stack_node *n)
{
if (n->name)
free (n->name);
free (n);
}
void free_stack (stack *s)
{
while (s->size > 0) {
stack_node *victim = s->top;
s->top = s->top->next;
free_node (victim);
s->size--;
}
free (s);
}
int main (void) {
stack *s = create_stack();
stack_node *node = NULL;
char *str; ///NAME
int vs; ///VALUE
int i=0;
if (!s)
return 1;
do{
printf("NAME{%d]: ",i);
fgets(str,20,stdin); ///***?
printf("VALUE[%d]: ",i);
scanf(" %d",&vs);
if (push_stack (s, vs, str) == NULL) {
fprintf (stderr, "error: push node failed '%d'.\n", i);
return 1;
}
i++;
}while(str != 'end' || vs != 0);
i=0;
while ((node = pop_stack(s)) != NULL) {
printf ("value[%d]: %d name[%d]: %s\n", i,node->value, i,node->name);
i++;
free_node (node);
}
free_stack (s);
return 0;
}
///**********
do{
printf("NAME[%d]: ",i);
if(fgets(buf,sizeof buf,stdin)){
int rtn = sscanf (buf, "%63[^\n]", name);
if(rtn == 1){
printf("VALUE[%d]: ",i);
scanf(" %d",&vs);
}
}
push_stack(s,vs,name);
if (s == NULL) {
fprintf (stderr, "error: push node failed '%d'.\n", i);
return 1;
}
i++;
}while(node->name != "end" || node->value != 0);
答案 0 :(得分:2)
好吧。很明显,您已经在代码中投入了很多精力,但是很显然,您仍在努力将结构作为堆栈中的节点存储(以及基本的字符串处理问题)。
首先,不需要强制转换malloc的返回值,请参见:Do I cast the result of malloc?。接下来,请始终根据对象的取消引用指针调整对象的分配大小。例如,stack *s
声明s
作为指向类型stack
的指针。如果s
是指向 {em> stack
的指针,则*s
的类型为stack
。使用实际指针本身来调整您的分配大小,可以消除大小不正确的任何可能性。此外,您有责任通过检查返回结果是否为NULL
来验证。这样,您的create_stack()
可能是:
stack *create_stack()
{
stack *s = malloc (sizeof *s); /* size from dereferenced pointer */
if (!s) { /* validate every allocation */
perror ("malloc-s");
return NULL;
}
s->size = 0;
s->top = NULL;
return s;
}
您的empty_stack
未使用,否则可以。
您的push_stack
函数在这里存在许多问题。首先,您不能同时分配节点和字符串! (即使可以,但由于忘记了字符串末尾的 nul-终止字符的空间,您的字节数也将太小一个字节)。您声明:
struct stack_node{
int value;
char *name;
struct stack_node * next;
};
您必须先为您的节点分配,然后 为指针name
分配存储空间–记住要像在{{1}中所做的那样验证分配},例如
create_stack()
接下来,您不能在C中分配字符串(除了初始化指向 string-literal 的指针,例如 stack_node *n = malloc (sizeof *n); /* allocate node only */
if (!n) { /* validate every allocation */
perror ("malloc-n");
return NULL;
}
n->value = value;
n->name = malloc (strlen (name) + 1); /* allocate strlen + 1 chars */
if (!n->name) { /* validate! */
perror ("malloc-name");
return NULL;
}
)在C中,您必须将字符串复制到为char *name = "foo";
创建的有效存储中。例如:
n->name
此外,如果您将 strcpy (n->name, name); /* you cannot assign strings, use strcpy */
声明为push_stack
类型,您将如何向调用方指示分配成功还是失败?
始终在需要时选择可以指示成功/失败的返回类型。考虑到这一点,您可以简单地返回指向新节点的指针(无论如何方便立即使用),或者在失败时返回void
,例如
NULL
您不希望使用两个分开的/* choose return type that can indicate success/failure */
stack_node *push_stack (stack *s, int value, const char *name)
{
stack_node *n = malloc (sizeof *n); /* allocate node only */
if (!n) { /* validate every allocation */
perror ("malloc-n");
return NULL;
}
n->value = value;
n->name = malloc (strlen (name) + 1); /* allocate strlen + 1 chars */
if (!n->name) { /* validate! */
perror ("malloc-name");
return NULL;
}
strcpy (n->name, name); /* you cannot assign strings, use strcpy */
n->next = s->top;
s->top = n;
s->size++;
return n;
}
函数。而不是pop
和pop_stack_value
,您只需要一个pop_stack_name
返回指向顶部节点的指针。然后,您可以根据需要访问值或名称(例如pop_stack
或node->value
)。
声明几个简单的辅助函数以在不再需要时释放分配的内存也很有意义。您正在处理节点和堆栈,因此node->name
帮助器和free_node
帮助器才有意义。 (请记住,您可能要在堆栈为空之前先释放它,因此请对free_stack
进行编码),例如
free_stack
在一个简短的示例中将所有内容放在一起,您可以执行以下操作:
void free_node (stack_node *n) /* free node helper function */
{
if (n->name)
free (n->name);
free (n);
}
void free_stack (stack *s) /* free stack helper (need not be empty) */
{
while (s->size > 0) {
stack_node *victim = s->top;
s->top = s->top->next;
free_node (victim);
s->size--;
}
free (s);
}
使用/输出示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct stack_node{
int value;
char *name;
struct stack_node * next;
};
typedef struct stack_node stack_node;
struct stack{
stack_node *top;
int size;
};
typedef struct stack stack;
stack *create_stack()
{
stack *s = malloc (sizeof *s); /* size from dereferenced pointer */
if (!s) { /* validate every allocation */
perror ("malloc-s");
return NULL;
}
s->size = 0;
s->top = NULL;
return s;
}
int empty_stack (stack * s) { /* s->size is type 'int' */
return s->size == 0;
}
/* choose return type that can indicate success/failure */
stack_node *push_stack (stack *s, int value, const char *name)
{
stack_node *n = malloc (sizeof *n); /* allocate node only */
if (!n) { /* validate every allocation */
perror ("malloc-n");
return NULL;
}
n->value = value;
n->name = malloc (strlen (name) + 1); /* allocate strlen + 1 chars */
if (!n->name) { /* validate! */
perror ("malloc-name");
return NULL;
}
strcpy (n->name, name); /* you cannot assign strings, use strcpy */
n->next = s->top;
s->top = n;
s->size++;
return n;
}
stack_node *pop_stack (stack *s) /* return the node on pop */
{
if (s->size > 0) {
stack_node *node = s->top;
s->top = s->top->next;
s->size--;
return node; /* caller is responsible for freeing node */
}
return NULL;
}
void free_node (stack_node *n) /* free node helper function */
{
if (n->name)
free (n->name);
free (n);
}
void free_stack (stack *s) /* free stack helper (need not be empty) */
{
while (s->size > 0) {
stack_node *victim = s->top;
s->top = s->top->next;
free_node (victim);
s->size--;
}
free (s);
}
int main (void) {
const char *str[] = { "john", "jack", "jill", "sally", "sue" };
int n = sizeof str/sizeof *str;
stack *s = create_stack();
stack_node *node = NULL;
if (!s) /* validate !!! */
return 1;
for (int i = 0; i < n; i++)
if (push_stack (s, i+1, str[i]) == NULL) {
fprintf (stderr, "error: push node failed '%d'.\n", i);
return 1;
}
while ((node = pop_stack(s)) != NULL) {
printf ("value: %2d name: %s\n", node->value, node->name);
free_node (node); /* free node */
}
free_stack (s); /* free stack */
return 0;
}
内存使用/错误检查
在您编写的任何动态分配内存的代码中,对于任何分配的内存块,您都有2个职责:(1)始终保留指向起始地址的指针因此,(2)当不再需要它时可以释放。
当务之急是使用一个内存错误检查程序来确保您不会尝试访问内存或在已分配的块的边界之外/之外进行写入,不要试图以未初始化的值读取或基于条件跳转,最后,以确认您释放了已分配的所有内存。
对于Linux,$ ./bin/stack_struct
value: 5 name: sue
value: 4 name: sally
value: 3 name: jill
value: 2 name: jack
value: 1 name: john
是正常选择。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行程序即可。
valgrind
始终确认已释放已分配的所有内存,并且没有内存错误。
最后,要避免一开始就遇到大多数此类问题,请始终在启用警告的情况下进行编译,并且在进行干净地编译而不会发出警告之前,不要接受代码< / em>。要启用警告,请在您的$ valgrind ./bin/stack_struct
==4204== Memcheck, a memory error detector
==4204== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==4204== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==4204== Command: ./bin/stack_struct
==4204==
value: 5 name: sue
value: 4 name: sally
value: 3 name: jill
value: 2 name: jack
value: 1 name: john
==4204==
==4204== HEAP SUMMARY:
==4204== in use at exit: 0 bytes in 0 blocks
==4204== total heap usage: 11 allocs, 11 frees, 161 bytes allocated
==4204==
==4204== All heap blocks were freed -- no leaks are possible
==4204==
==4204== For counts of detected and suppressed errors, rerun with: -v
==4204== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
或-Wall -Wextra
编译字符串中添加gcc
。 (添加clang
以获得其他一些警告)。对于-pedantic
,您可以使用clang
。对于 VS (在windoze上为-Weverything
),添加cl.exe
(或使用/W3
,但您会得到很多无关的Windows非代码相关警告)。阅读并理解每个警告。他们将确定任何问题以及发生问题的确切路线。
在大多数教程中,您只需听一下编译器告诉您的内容,就可以学到很多有关编码的知识。
仔细检查一下,如果还有其他问题,请告诉我。
先输入/Wall
,然后再输入value
接受用户输入时,请考虑使用name
来读取名称和值(然后使用fgets
(或sscanf
)转换为strtol
)。您可以避免因int
而陷入许多陷阱,并且避免在匹配失败之后失败清空scanf
(这通常会导致无限循环以及未定义行为 >)
您故意想要连续循环,直到您确认您有所需的输入-并且它满足您的要求(范围等)。您还必须通过生成手册stdin
(例如,在Linux上为 Ctrl + d 或在windoze上为 Ctrl + z )来防止用户取消输入,但请参见:{ {3}})
EOF
(注意:当用户在空白行上按 Enter 而不是寻找/* if you need constants, #define one (or more), or use a global enum */
#define MAXC 1024 /* max chars for input buffer (don't skimp) */
#define MAXNM 64 /* max length for name buffer (ditto) */
...
int main (void) {
stack *s = create_stack();
stack_node *node = NULL;
if (!s) /* validate !!! */
return 1;
for (;;) { /* loop continually taking input until exit conditon */
char buf[MAXC] = "", /* line buffer for fgets */
name[MAXNM] = ""; /* buffer for parsing name from line */
int value = 0; /* int to parse from line */
for (;;) { /* loop continually until you get valid int */
fputs ("\nenter value: ", stdout); /* prompt for value */
if (fgets (buf, MAXC, stdin)) { /* validate line read */
if (sscanf (buf, "%d", &value) == 1) { /* convert to int */
if (value == 0) { /* your exit condition */
fputs (" value == 0 (input done)\n", stderr );
goto inputdone; /* jump out of both loops */
}
break;
}
else /* non-integer (non-numeric) input */
fputs (" error: invalid integer input.\n", stderr);
}
else { /* fgets returned EOF (user canceled) */
fputs ("(user canceled input)\n", stderr);
goto inputdone; /* jump out of both loops */
}
}
for (;;) { /* loop continually until name recieved */
fputs ("enter name : ", stdout); /* prompt name */
if (fgets (name, MAXNM, stdin)) { /* read name */
size_t len = strlen (name); /* get length */
if (len && name[len-1] == '\n') { /* last char \n ? */
name[--len] = 0; /* overwrite with \0 */
if (len) /* check 1 char remains */
break; /* got good name, go push */
else /* user just pressed [Enter] */
fputs (" error: empty-line.\n", stderr);
}
else if (len == MAXNM -1) /* was input too long? */
fputs (" warning: name truncated.\n", stderr);
}
else { /* fgets returned EOF (user canceled) */
fputs ("(user canceled input)\n", stderr);
goto inputdone; /* jump out of both loops */
}
}
push_stack (s, value, name); /* push value and name on stack */
}
inputdone:;
puts ("\nvalues stored in stack\n");
while ((node = pop_stack(s)) != NULL) {
printf ("value: %2d name: %s\n", node->value, node->name);
free_node (node); /* free node */
}
free_stack (s); /* free stack */
return 0;
}
时,最好结束输入。 value == 0
可以为零))
使用/输出示例
int
查看您是否可以通过上面的输入例程,并了解为什么按原样编写它。 (我也有一个可以同时接受$ ./bin/stack_struct_input_simple
enter value: 1
enter name : Frank Zappa
enter value: 2
enter name :
error: empty-line.
enter name : Jimmy Buffet
enter value: 3
enter name : Grahm Nash
enter value: 4
enter name : John Bonham
enter value: 0
value == 0 (input done)
values stored in stack
value: 4 name: John Bonham
value: 3 name: Grahm Nash
value: 2 name: Jimmy Buffet
value: 1 name: Frank Zappa
或value name
然后接受value
的用户。)只要遵循规则并 validate < / strong>一切,防止使用手册name
,并始终知道输入缓冲区中还剩下什么(例如EOF
)。
答案 1 :(得分:0)
是否有任何理由不更改您的stack_node
结构以直接包含名称。
struct stack_node{
int value;
struct stack_node * next;
char name[1];
};
这种特殊形式的优点是,如果您按以下方式致电malloc
:
struct stack_node * new_node = malloc(sizeof *new_node + strlen(name));
您将获得正确的内存量,包括字符串末尾NUL
字节的空间。
Ob警告。如果您的幽默感受损,请跳过最后一段。
还在这里吗?好。如果所有这些都失败了,您可以随时尝试使用Soap界面。但是也许我们永远都不要谈论这个。 ;)
答案 2 :(得分:0)
您的代码中很少有问题:
问题1:
看一下这些函数public static string stopWriteFileHalfWay(string option)
{
string tempPath = Path.GetTempFileName();
using (StreamWriter sw = File.AppendText(tempPath))
{
sw.WriteLine("line 1");
return "after line 3"; // => exit and do not want to create and write any file
sw.WriteLine("line 2");
}
File.Move(tempPath, @"d:\test\test.txt");
return "completed";
}
的语句:
push_stack()
堆栈节点 n->next = s->top;
s->top = n;
的{{1}}指向next
的{{1}},而堆栈n
指向节点top
。创建的节点的stack
应该是top
,因为它将是堆栈的顶部节点。您不需要此语句n
。应该是:
next
问题2:
在NULL
中,您正在做
n->next = s->top;
在功能 n->next = NULL;
s->top = n;
中,您正在执行以下操作:
main()
printf("STACK: %d \n",pop_stack_value(s));
printf("STACK: %s \n", pop_stack_name(s));
指向堆栈的pop_stack_value()
,堆栈 stack_node * sn = s->top;
s->top = s->top->next;
free(sn);
指向节点(就像您在s->top
中所做的那样)。 top
将释放栈顶。现在在功能top
中,您正在做
push_stack()
free(sn)
指向pop_stack_name()
中已释放的内存,并且您正在尝试访问程序不拥有的内存。这是未定义的行为。
您执行弹出操作的方式是完全错误的。在堆栈的弹出操作中,应该弹出整个节点,而不是弹出节点的单个值。弹出节点后,将堆栈的 char * name = s->top->name;
stack_node * sn = s->top;
s->top = s->top->next;
s->size--;
free(sn);
设置为指向上一个节点。为此,您需要遍历整个列表,因为您使用的是单链接列表。或者,您可以具有双链表,其中每个节点都具有其先前的节点地址。通过这种重置,栈顶将不会是一项昂贵的操作。