[3rd update]
I made some changes to David's code. I changed: n->next = s->top;s->top = n; to n->next = NULL;s->top = n; like H.S. suggested; and also added different features to the main function. I'm trying to get the char name and int value as input from the user while the program is running. The program should keep taking data from the user, storing it in the stack and allocating memory accordingly. The program only ends when the user types 'end' (char) or 0 (int). The code gets compiled, but that's it, after that, any input and the program stops. Also, am I wasting memory using fgets(str,20,stdin)? What if I don't use all that size. Any feedback is very much appreciated!
///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);
Well.. It's obvious you have put effort into your code, but it is also obvious you are still struggling to wrap your head around storing a struct as a node within the stack (and with basic string handling issues).
First, there is no need to cast the return of malloc, see See: Do I cast the result of malloc?. Next, always size your allocation for an object based on the derefernced pointer to the object. For example stack *s declares s as a pointer to type stack. If s is a pointer to stack, then *s is type stack. Sizing your allocations with the actual pointer itself with eliminate any chance of an incorrect size. Further, it is your responsibility to validate every allocation by checking the return is not NULL. With that, your create_stack() could be:
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;
}
Your empty_stack is unused, but otherwise OK.
Your push_stack function is where a number of problems lie. First you cannot allocate for the node and the string at the same time! (even if you could, you would be one-byte too small as you have forgotten space for the nul-terminating character at the end of the string). You declare:
struct stack_node{
int value;
char *name;
struct stack_node * next;
};
You must allocate first for your node, then allocate storage for your pointer name -- remembering to validate the allocation as done in create_stack(), e.g.
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;
}
Next, you cannot assign strings in C (aside from initialization of a pointer to point to a string-literal, e.g. char *name = "foo";) In C, you must copy your string to the valid storage your create for n->name. For example:
strcpy (n->name, name); /* you cannot assign strings, use strcpy */
Further, how are you going to indicate to the caller, whether your allocations succeeded or failed if you declare push_stack as type void??
Always select a return type that can indicate success/failure where required. With that in mind, you can simply return a pointer to the new node (which is convenient for immediate use anyway), or return NULL on failure, e.g.
/* 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;
}
You do not want two-separate pop function. Instead of pop_stack_value and pop_stack_name, you simply want a pop_stack that returns a pointer to the top node. You can then access either the value or name (e.g. node->value or node->name) as required.
It also makes sense to declare a couple of simple helper functions to free the memory allocated when it is no longer needed. You are dealing with nodes and the stack, so a free_node helper and a free_stack helper make sense. (and remember, you may want to free the stack before it is empty, so code the free_stack accordingly), e.g.
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);
}
Putting all the pieces together in a short example, you could do something similar to the following:
#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;
}
Example Use/Output
$ ./bin/stack_struct
value: 5 name: sue
value: 4 name: sally
value: 3 name: jill
value: 2 name: jack
value: 1 name: john
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ 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)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Finally, to avoid most of these problems to begin with, always compile with warnings enabled, and do not accept code until it compiles cleanly without warning. To enable warnings add -Wall -Wextra to your gcc or clang compile string. (add -pedantic for several additional warnings). For clang, instead you can use -Weverything. For VS (cl.exe on windoze), add /W3 (or use /Wall but you will get quite a few extraneous windows non-code related warnings). Read and understand each warning. They will identify any problems, and the exact line on which they occur.
You can learn as much about coding by simply listening to what your compiler is telling you as you can from most tutorials.
Look things over and let me know if you have further questions.
Taking value Input, then name
When you take user input, consider using fgets for reading both the name and value (and then use sscanf (or strtol) to convert to int). You avoid many pitfalls with scanf and failure to empty stdin after a matching failure (which will generally lead to an infinite loop as well as Undefined Behavior)
You purposely want to loop continually until you validate your have the input you ask for -- and that it meets your requirements (range, etc...). You also must protect against the user canceling input by generating a manual EOF (e.g. Ctrl+d on Linux or Ctrl+z on windoze, but see: CTRL+Z does not generate EOF in Windows 10)
/* 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;
}
(note: it would probably be better to end input when the user presses Enter on an empty line rather than looking for a value == 0 (because an int can be zero))
Example Use/Output
$ ./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
See if you can go though the input routine above and understand why it is written the way it is. (I also have one that will accept both value name or value then name) You can be as flexible as you want with input as long as you follow the rules and validate everything, protect against a manual EOF and always know what remains in your input buffer (e.g. stdin).
Is there any reason not to alter your stack_node struct to directly include the name.
struct stack_node{
int value;
struct stack_node * next;
char name[1];
};
This particular form has the advantage that if you call malloc as follows:
struct stack_node * new_node = malloc(sizeof *new_node + strlen(name));
you get exactly the correct amount of memory, including space for the trailing NUL byte on the string.
Ob warning. Skip the last paragraph if you are humor impaired.
Still here? Good. Failing all this, you could always try a Soap interface. But then perhaps we should never talk about that. ;)
Few of problems in your code:
Problem 1:
Look at these statement of function push_stack():
n->next = s->top;
s->top = n;
The next of stack node n is pointing to top of stack and top of stack is pointing back to node n. The next of the node created should be NULL as it will be top node of stack. You don't need this statement n->next = s->top;. It should be:
n->next = NULL;
s->top = n;
Problem 2:
In main(), you are doing:
printf("STACK: %d \n",pop_stack_value(s));
printf("STACK: %s \n", pop_stack_name(s));
and in function pop_stack_value(), you are doing:
stack_node * sn = s->top;
s->top = s->top->next;
free(sn);
s->top is pointing to the top of stack and the top of stack is pointing back to node (as you did in push_stack()). The free(sn) will free the top of stack. Now in function pop_stack_name(), you are doing
char * name = s->top->name;
stack_node * sn = s->top;
s->top = s->top->next;
s->size--;
free(sn);
s->top is pointing to memory which has been already freed in pop_stack_value() and you are trying to access memory which your program doesn't own. This is undefined behavior.
The way you are doing pop operation is completely wrong. In the pop operation of stack, you should pop the whole node instead of popping individual value of node. Once the node is popped, set the top of stack to point to the previous node. For that you need to traverse the whole list because you are using singly linked list. As an alternative, you can have doubly linked list in which every node is having its previous node address. With this resetting top of stack would not be an expensive operation.
Related
I just started learning C recently, and am having issues figuring out memory allocation. I have spent about the last 2~3 days in my extra time trying to figure this out, but have not found a solution yet. So first, I have two structs:
struct _list {
// arr is an array of string arrays
char **arr;
// recs tracks how many records are in the list
size_t recs;
// arrSizes records the size of each string array in arr
size_t *arrSizes;
};
typedef struct _list list_t;
and
struct _string {
char *string;
// size is used to store strlen
size_t size;
};
typedef struct _string string_t;
I initialize the above structs respectively in the following ways.
list_t:
list_t *NewList() {
list_t *List = NULL;
List = malloc(sizeof(*List));
if (List == NULL) {
fprintf(stderr, "Failed to allocate memory to list structure.\n");
return NULL;
}
List->arr = malloc(sizeof(List->arr));
if (List->arr == NULL) {
free(List);
fprintf(stderr, "Failed to allocate memory to list array.\n");
return NULL;
}
List->arrSizes = malloc(sizeof(List->arrSizes));
if (List->arr == NULL) {
free(List);
fprintf(stderr, "Failed to allocate memory to size array.\n");
return NULL;
}
List->recs = 0;
return List;
}
string_t:
// a string array read in by the program is passed with "char* record"
string_t *NewString(char *record)
{
string_t *String = NULL;
String = malloc(sizeof * String);
if (String == NULL) {
fprintf(stderr, "Failed to allocate memory to string structure.\n");
return NULL;
}
String->size = strlen(record) + 1;
String->string = malloc(String->size);
if (String->string == NULL) {
free(String);
fprintf(stderr, "Failed to allocate memory to string array.\n");
return NULL;
}
strcpy(String->string, record);
return String;
}
I read lines from a file and load them into a "matching results" buffer using something like the following code. Please ignore exits and the fact that I don't have null handling after struct initialization is complete; I will add something more useful later. Also, sorry about the length. I edited quite a bit to produce the smallest example I could think of that reproduces the issue.
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
// Check if File exists
void FileExists(FILE *FilePath) {
if (FilePath == NULL) {
fprintf(stderr, "Error: File not found.\n");
exit(1);
}
}
// Delete a string_t struct
int delString(string_t *Structure)
{
if (Structure != NULL) {
free(Structure->string);
free(Structure);
return 0;
}
return 1;
}
// Allocate memory for additional elements added to members of list_t struct
void AllocList(list_t *List, size_t StrLen)
{
char **ArrStrArr_tmp;
size_t *SizeArr_tmp;
char *StrArr_tmp;
ArrStrArr_tmp = realloc(*List->arr, sizeof(**ArrStrArr_tmp) * List->recs);
SizeArr_tmp = realloc(List->arrSizes, sizeof(*SizeArr_tmp) * List->recs);
StrArr_tmp = malloc(sizeof(*StrArr_tmp) * StrLen);
if ((ArrStrArr_tmp == NULL) || (SizeArr_tmp == NULL)
|| (StrArr_tmp == NULL)) {
fprintf(stderr, "Failed to allocate memory.\n");
exit(1);
}
else {
List->arr = ArrStrArr_tmp;
List->arrSizes = SizeArr_tmp;
(List->arr)[List->recs-1]= StrArr_tmp;
}
}
// Add a record to a buffer
int AddRecord(list_t *List, char *AppendRecord)
{
string_t *line = NewString(AppendRecord);
List->recs++;
AllocList(List, line->size);
(List->arr)[List->recs - 1] = line->string;
(List->arrSizes)[List->recs - 1] = line->size;
delString(line);
return 0;
}
// Sends entire string array to lowercase
void tolowerString(char *UpperString, size_t StrLen)
{
int i;
for (i = 0; i < (int)StrLen; i++) {
UpperString[i] = (char)tolower(UpperString[i]);
}
}
// Attempt to match string in lines from a file; lines with matches are read into a buffer
int main()
{
char line[80];
int PrintedLines = 0;
list_t *ResultList = NewList();
char *MyString = "theme";
char *Filename = "List.txt";
FILE *in = fopen(Filename, "r");
// Check if file exists
FileExists(in);
while (fscanf(in, "%79[^\n]\n", line) == 1)
{
char LookString[80];
strcpy(LookString, line);
LookString[strlen(LookString) - 1] = '\0';
// send lookstring to lowercase
tolowerString(LookString, strlen(LookString));
// add line to buffer ResultList if it contains MyString
if (strstr(LookString, MyString)) {
AddRecord(ResultList, line);
PrintedLines++;
}
}
// If PrintedLines is at zero after the while statement terminates, return in abnormal state
if (PrintedLines == 0) {
fprintf(stderr, "No matches found. Please check your input if you are sure there is a match.\n");
return 1;
}
fclose(in);
return 0;
}
When trying to read the 5th matching record into my buffer, my program crashes at this line in the AllocList function:
ArrStrArr_tmp = realloc(*List->arr, sizeof(**ArrStrArr_tmp) * List->recs);
I get the following message on the version I have posted above:
realloc(): invalid old size
aborted (core dumped)
My guess is that I'm running into an error after some default amount of memory from my initial malloc is used, but I have no clue what is actually causing this. In my actual code I'm printing all sorts of things (pointer sizes, etc.), but I still can't spot anything. What's strange is, before writing this post, I was actually seeing the error:
realloc(): invalid next size
aborted (core dumped)
But I can't reproduce it now for some reason...
I have also read that I should reallocating memory for my list_t struct whenever I add an element to one of it's members, but reallocating it actually doesn't change where or how this program crashes. In any case, I'm not sure how I should be reallocating memory for my struct. To clarify, my questions are:
What is causing this memory issue?
Should I be reallocating memory for my list struct, and how much should I be reallocating given that I'm adding an extra element to the arr and arrSizes members?
As the crash suggests, the line
ArrStrArr_tmp = realloc(*List->arr, sizeof(**ArrStrArr_tmp) * List->recs);
is wrong.
This have it read an uninitialized buffer allocated via malloc(), whose value is indeterminate.
The intension of this line is to re-allocate the array pointed at by List->arr, which is an array of char*.
Therefore, the line should be
ArrStrArr_tmp = realloc(List->arr, sizeof(*ArrStrArr_tmp) * List->recs);
just like the following line, which is re-allocating an array of size_t.
SizeArr_tmp = realloc(List->arrSizes, sizeof(*SizeArr_tmp) * List->recs);
Also I found 2 more points for improvement:
Firstly, the usage of some malloc() in the function NewList are not good.
The function is creating zero-element array, so you won't need space for List->arr and List->arrSizes.
Also note that realloc() accepts NULL as the buffer to re-allocate.
list_t *NewList() {
list_t *List = NULL;
List = malloc(sizeof(*List));
if (List == NULL) {
fprintf(stderr, "Failed to allocate memory to list structure.\n");
return NULL;
}
List->arr = NULL;
List->arrSizes = NULL;
List->recs = 0;
return List;
}
Secondly, you are copying pointer instead of string in AddRecord,
so you have problems of memory leak and potential use-after-free.
It seems the string should be copied:
(List->arr)[List->recs - 1] = line->string;
should be
strcpy((List->arr)[List->recs - 1], line->string);
Create a function that receives two Lists (L1,L2), initialize L2, and insert in L2 the elements of L1 excluding the ones in odd postion (Assume that the first element of the list is in position zero). Then print L2.
My prof gave us the solution (below), but I need to create a main() that calls this function and returns L2, and since I'm a newbie nothing seems to work.
I tried initializing L1 and then calling the function, but all I got was a huge amount of errors.
That's the final function:
struct list{
int value;
struct list * nextPtr;
};
void createSubList(struct list * l1Ptr, struct list ** l2PtrPtr) {
init(l2PtrPtr);
while(l1Ptr!=NULL) {
pre_insert(l2PtrPtr, l1Ptr>value);
l1Ptr = l1Ptr>nextPtr;
if (l1Ptr != NULL)
l1Ptr = l1Ptr>nextPtr;
}
}
I expect to see L2 printed after calling the function.
That's my final file:
#include <stdlib.h>
#include <string.h>
struct list {
int value;
struct list *nextPtr;
};
void init( struct list **ptrptr){
*ptrptr!=NULL;
}
void prn (struct list * lptr) {
while (lptr) {
printf (" %d", lptr->value);
lptr = lptr->nextPtr;
}
putchar ('\n'); }
void pre_insert(struct list ** ptrptr, int value){
struct list * tmp_ptr;
tmp_ptr=*ptrptr;
*ptrptr=(struct list *)malloc(sizeof(struct list));
(*ptrptr)->value=value;
(*ptrptr)->nextPtr=tmp_ptr;
}
void createSubList(struct list* l1Ptr, struct list** l2PtrPtr) {
init(l2PtrPtr);
while(l1Ptr!=NULL) {
pre_insert(l2PtrPtr, l1Ptr->value);
l1Ptr = l1Ptr->nextPtr;
if (l1Ptr != NULL)
l1Ptr = l1Ptr->nextPtr;
}
prn(l1Ptr);
}
void main(){
struct list* l1Ptr;
init(&l1Ptr);
struct list* l2ptr;
init(&l2ptr);
pre_insert(&l1Ptr , 1);
pre_insert(&l1Ptr , 2);
pre_insert(&l1Ptr , 3);
pre_insert(&l1Ptr , 4);
pre_insert(&l1Ptr , 5);
pre_insert(&l1Ptr , 6);
createSubList(l1Ptr,&l2ptr);
}
Errors I get:
[Finished in 0.1s with exit code -11]
[shell_cmd: gcc "/home/vittorio/Scrivania/CProjects/new.c" -o "/home/vittorio/Scrivania/CProjects/new" && "/home/vittorio/Scrivania/CProjects/new"]
[dir: /home/vittorio/Scrivania/CProjects]
[path: /home/vittorio/bin:/home/vittorio/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin]
Once you get the non-ASCII chars squared away. Take your implementation piece-by-piece. For starters, unless you are on a non-conforming system, the proper declarations for main are int main (void) and int main (int argc, char **argv) (which you will see written with the equivalent char *argv[]). note: main is a function of type int and it returns a value. See: C11 Standard §5.1.2.2.1 Program startup p1 (draft n1570). See also: What should main() return in C and C++?.
So, for conforming implementation, main() without arguments should be:
int main (void) {
struct list *l1Ptr = NULL;
struct list *l2ptr = NULL;
...
(note: just ditch your init() function, you don't need to function call overhead just to set a pointer NULL)
Next issue along the way is your pre_insert must distinguish between adding the 1st node to the list, and adding all others. For the 1st node, just set your *ptrptr to your initialize tmp_ptr to establish the head of the list. For the remaining nodes, you are using chaining where you set tmp_ptr->nextPtr = *ptrptr; to make the next pointer in the new node point to the old-start of your list, then set *ptrptr = tmp_ptr; to make it your new start of the list, e.g.
void pre_insert (struct list **ptrptr, int value)
{
struct list *tmp_ptr = malloc (sizeof *tmp_ptr); /* don't cast malloc */
if (tmp_ptr == NULL) { /* validate EVERY allocation */
perror ("malloc-tmp_ptr");
exit (EXIT_FAILURE);
}
tmp_ptr->value = value; /* initialize struct members */
tmp_ptr->nextPtr = NULL;
if (!*ptrptr) /* if 1st node, simply assign */
*ptrptr = tmp_ptr;
else {
tmp_ptr->nextPtr = *ptrptr; /* otherwise, set tmp->next to 1st */
*ptrptr = tmp_ptr; /* now set list to point to tmp */
}
}
Your createSubList had similar redundant logic, showing your were struggling. All you need is a simply 1/0 toggle to add or skip nodes from list1. For example:
void createSubList (struct list *l1Ptr, struct list **l2PtrPtr)
{
int i = 0;
while (l1Ptr != NULL) {
if (i == 0) { /* only store even nodes */
pre_insert (l2PtrPtr, l1Ptr->value);
i = 1;
}
else
i = 0;
l1Ptr = l1Ptr->nextPtr;
}
}
As discussed in the comments, you need a way to print your lists, and equally important, a way to free the memory allocated to the nodes where you are done with them. Simple functions are all you need, e.g.
void prnlist (struct list *lptr)
{
while (lptr) {
printf (" %d", lptr->value);
lptr = lptr->nextPtr;
}
putchar ('\n');
}
void freelist (struct list *lptr)
{
while (lptr) {
struct list *victim = lptr;
lptr = lptr->nextPtr;
free (victim);
}
}
(note: do you see why you have to save a pointer to the current node, and then advance the node before calling free on your victim?)
That's it, aside from my additional comments in-line. Putting it altogether you could do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct list {
int value;
struct list *nextPtr;
};
void pre_insert (struct list **ptrptr, int value)
{
struct list *tmp_ptr = malloc (sizeof *tmp_ptr); /* don't cast malloc */
if (tmp_ptr == NULL) { /* validate EVERY allocation */
perror ("malloc-tmp_ptr");
exit (EXIT_FAILURE);
}
tmp_ptr->value = value; /* initialize struct members */
tmp_ptr->nextPtr = NULL;
if (!*ptrptr) /* if 1st node, simply assign */
*ptrptr = tmp_ptr;
else {
tmp_ptr->nextPtr = *ptrptr; /* otherwise, set tmp->next to 1st */
*ptrptr = tmp_ptr; /* now set list to point to tmp */
}
}
void createSubList (struct list *l1Ptr, struct list **l2PtrPtr)
{
int i = 0;
while (l1Ptr != NULL) {
if (i == 0) { /* only store even nodes */
pre_insert (l2PtrPtr, l1Ptr->value);
i = 1;
}
else
i = 0;
l1Ptr = l1Ptr->nextPtr;
}
}
void prnlist (struct list *lptr)
{
while (lptr) {
printf (" %d", lptr->value);
lptr = lptr->nextPtr;
}
putchar ('\n');
}
void freelist (struct list *lptr)
{
while (lptr) {
struct list *victim = lptr;
lptr = lptr->nextPtr;
free (victim);
}
}
int main (void) {
struct list *l1Ptr = NULL;
struct list *l2ptr = NULL;
for (int i = 1; i < 10; i++)
pre_insert (&l1Ptr , i);
createSubList (l1Ptr, &l2ptr);
prnlist (l2ptr); /* print list 2 */
freelist (l1Ptr); /* don't forget to free what you allocate */
freelist (l2ptr);
}
Example Use/Output
$ ./bin/llcreatesublist
1 3 5 7 9
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/llcreatesublist
==23324== Memcheck, a memory error detector
==23324== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==23324== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==23324== Command: ./bin/llcreatesublist
==23324==
1 3 5 7 9
==23324==
==23324== HEAP SUMMARY:
==23324== in use at exit: 0 bytes in 0 blocks
==23324== total heap usage: 14 allocs, 14 frees, 224 bytes allocated
==23324==
==23324== All heap blocks were freed -- no leaks are possible
==23324==
==23324== For counts of detected and suppressed errors, rerun with: -v
==23324== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
You need to add #include <stdio.h>
In the init function the code *ptrptr!=NULL; shall be *ptrptr=NULL;, i.e. no !
Then change void main(){ to int main(){
That should solve the warnings.
Then you need to do the actual printing. So in main do:
prn(l1Ptr);
createSubList(l1Ptr,&l2ptr);
prn(l2ptr);
BTW: Notice that the prn(l1Ptr); inside createSubList will not print anything as l1Ptr is already NULL when you call prn. So you should simply delete that line.
With the above change your output should be:
6 5 4 3 2 1
2 4 6
The created sub-list is reverted compared to the original list due to use of pre_insert that adds new nodes in the front.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
int CurrentCnt = 0;
#define MAX_ID_LEN 30
#define MAX_NAME_LEN 30
#define MAX_PRICE_LEN 30
#define MAX_DISCOUNT_LEN 30
typedef struct {
char goods_id[MAX_ID_LEN];
char goods_name[MAX_NAME_LEN];
int goods_price;
char goods_discount[MAX_DISCOUNT_LEN];
int goods_amount;
int goods_remain;
} GoodsInfo;
//--------------------------------------------------------------------
//define node
//--------------------------------------------------------------------
typedef struct node
{
GoodsInfo data;
struct node *next;
} GoodsList;
bool check_nullfile(void)
{
FILE *fp = fopen("goodsinfo.txt", "r");
//file not exist
if (!fp) {
printf("no files found.\n");
FILE *fp = fopen("goodsinfo.txt", "w");
fclose(fp);
return false;
}
//file already exist
else {
int temp;
//res for try to read file if file null feof() can't determine
whether file is null or not
int res = fscanf(fp, "%d", &temp);
fclose(fp);
if (res <= 0)
return false;
else
return true;
}
}
void info_init(GoodsList **L) {
if(check_nullfile())
{
FILE * fp;
fp=fopen("goodsinfo.txt", "r");
GoodsList *listptr;
while (1)
{
if (feof(fp)) break;
listptr=(GoodsList*)malloc(sizeof(GoodsList));
listptr->next=(*L)->next;
(*L)->next=listptr;
fscanf(fp,"%4s\t",listptr->data.goods_id);
fscanf(fp,"%4s\t",listptr->data.goods_name);
fscanf(fp,"%d\t",&(listptr->data.goods_price));
fscanf(fp,"%s\t",listptr->data.goods_discount);
fscanf(fp,"%d\t",&(listptr->data.goods_amount));
fscanf(fp,"%d",&(listptr->data.goods_remain));
/* printf("%c%c%c%c\n",listptr->data.goods_id[0],listptr-
>data.goods_id[1],listptr->data.goods_id[2],listptr->data.goods_id[3]);
printf("%c%c%c%c\n",listptr->data.goods_name[0],listptr-
>data.goods_name[1],listptr->data.goods_name[2],listptr-
>data.goods_name[3]);
printf("%d\n",listptr->data.goods_price);
printf("%c%c%c%c\n",listptr->data.goods_discount[0],listptr-
>data.goods_discount[1],listptr->data.goods_discount[2],listptr-
>data.goods_discount[3]);
printf("%d\n",listptr->data.goods_amount);
printf("%d\n",listptr->data.goods_remain); these are my
testing*/
CurrentCnt++;
if (feof(fp)) break;
}
fclose(fp);
}
printf("%d\n", CurrentCnt);
}
int main (void)
{
GoodsList **L;
L=(GoodsList**)malloc(sizeof(GoodsList*));
info_init(L);
return 0;
}
I have a testing file which includes five groups of files. When I run this program, the fifth group of data can't be output correctly. My testing data is
1000 new1 90 0.9 90 80
1001 new2 80 0.9 80 80
1002 new3 70 0.8 10 10
1003 new4 88 0.8 70 80
1004 new5 100 0.8 70 80
Why the position4 works but others can't?Position1 2 3will make CurrentCnt become 6 but 5.In the last loop the program get nothing but why doesn't it jump out the loop?
My new poor program:
void info_init(GoodsList **L) {
if(check_nullfile())
{
FILE * fp;
fp=fopen("goodsinfo.txt", "r");
GoodsList *listptr;
while (1/*feof(fp) position1*/)
{
//if (feof(fp)) break; //position2
listptr=malloc(sizeof(*listptr));
listptr->next=*L;
*L=listptr;
//if (feof(fp)) break;//position3
fscanf(fp,"%s\t",listptr->data.goods_id);
fscanf(fp,"%s\t",listptr->data.goods_name);
fscanf(fp,"%d\t",&(listptr->data.goods_price));
fscanf(fp,"%s\t",listptr->data.goods_discount);
fscanf(fp,"%d\t",&(listptr->data.goods_amount));
fscanf(fp,"%d",&(listptr->data.goods_remain));
//if (feof(fp)) break;//position4
CurrentCnt++;
}
fclose(fp);
}
printf("%d\n", CurrentCnt);
}
The way you have factored your code (1) to handle the list; and (2) to add data to the list, it so confused, and so lacking in validation that it is no wonder you are having difficulty sorting it out.
The read of the data is flawed from the beginning. See Why is while ( !feof (file) ) always wrong?. Further, you fail to validate a single return of fscanf. If one read fails, you invoke Undefined Behavior by blindly using the indeterminate value (and likely every value from that point forward will be indeterminate). All bets are over at that point.
However, you are to be commended for your use of #define to define constants you need, but you then fail to protect your array bounds by including field-width modifiers with all char* conversion specifiers. While you #define constants, you then turn around and hard-code your file name. Don't do that. Pass your filename as an argument to your program or prompt for its entry.
Whenever processing a "line-of-data" at a time, you should use a line-oriented input function such as fgets or POSIX getline and then parse the values you need from the line of data. This provides the benefit of allowing separate validations of the (1) read of the data from the file; and (2) the parse of the values from the resulting buffer. If for some reason there is an error in the format, your parse will fail, and you can simply continue the read loop and read the next line -- without the risk of Undefined Behavior.
When you are creating a list, all you need is a single append() function which will create the list if it does not exist, and allocate and add each additional node to the list as needed. Your code looks to attempt a simple forward-chaining for adding nodes to your list (which is fine, but without more will result in the list being held in memory in reverse order)
Don't mix reading data with list operations. Instead read and pass the data required to your append() function. While this largely up to you, failure to separate your read/parse and append will just lead to list functions at are not reusable.
For example, instead of trying to read and parse data within your list function, open your file in main() and parse the data to a temporary goodsinfo1 struct and pass the list address and pointer to your temporary data to your append function. You could do something similar to the following for your read and parse of data and passing the needed values to your function:
int main (int argc, char **argv)
{
char buf[MAXC]; /* read buffer */
size_t linecnt = 0; /* line counter */
goodslist *list = NULL; /* linked list pointer */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("fopen-file");
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line of data */
goodsinfo tmp = { .goods_id = "" }; /* temp data struct */
/* parse and validate data in buf (can be separate function) */
if (sscanf (buf, "%29s %29s %d %29s %d %d", tmp.goods_id,
tmp.goods_name, &tmp.goods_price, tmp.goods_discount,
&tmp.goods_amount, &tmp.goods_remain) != 6) {
fprintf (stderr, "error: invalid format line %zu.\n", linecnt+1);
continue;
}
if (!append (&list, &tmp)) /* append to list/validate */
break;
}
if (fp != stdin) /* close file if not stding */
fclose (fp);
prn_list (list); /* print list */
free_list (list); /* free list data */
return 0;
}
(note: the program takes the filename to read data from as the first argument, or reads from stdin by default if no filename is provided. Also note, you declare your list as a pointer to goodslist, not a pointer-to-pointer-to goodslist)
With your data read and parsed, your append() function simply has to allocate storage for data and allocate storage for a new list node. It only has two cases to handle (1) is the list empty? -- leave node->next = NULL; otherwise (2) set node->next equal to the current list address before assigning the address for your new node as the new list address to chain your nodes together, e.g.
/* function to allocate goodslist node and append allocated goodsinfo
* data to list. Takes address of list pointer and pointer to goodsinfo data
* to append to list. Returns pointer new node on success, NULL otherwise.
*/
goodsinfo *append (goodslist **l, goodsinfo *tmp)
{
goodsinfo *data = malloc (sizeof *data); /* allocate/validate data */
if (!data) {
perror ("malloc-data");
return NULL;
}
*data = *tmp; /* fill allocated data block with tmp values */
/* allocate/validate list node */
goodslist *node = malloc (sizeof *node);
if (!node) {
perror ("malloc-node");
free (data);
return NULL;
}
node->data = data; /* initialize data and set next NULL */
node->next = NULL;
if (*l) /* if list exists, chain next to list */
node->next = *l;
return ((*l = node)->data); /* assign new node as list, return data */
}
Putting it altogether you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
#define MAX_ID_LEN 30
#define MAX_NAME_LEN MAX_ID_LEN
#define MAX_PRICE_LEN MAX_NAME_LEN
#define MAX_DISCOUNT_LEN MAX_PRICE_LEN
#define MAXC 1024 /* read buffer size (don't skimp) */
typedef struct {
char goods_id[MAX_ID_LEN];
char goods_name[MAX_NAME_LEN];
int goods_price;
char goods_discount[MAX_DISCOUNT_LEN];
int goods_amount;
int goods_remain;
} goodsinfo;
typedef struct goodslist {
goodsinfo *data; /* make data a pointer and allocate */
struct goodslist *next;
} goodslist;
/* bool check_nullfile(void)
* (poor test, if first char not 0-9, test fails)
*/
/* function to allocate goodslist node and append allocated goodsinfo
* data to list. Takes address of list pointer and pointer to goodsinfo data
* to append to list. Returns pointer new node on success, NULL otherwise.
*/
goodsinfo *append (goodslist **l, goodsinfo *tmp)
{
goodsinfo *data = malloc (sizeof *data); /* allocate/validate data */
if (!data) {
perror ("malloc-data");
return NULL;
}
*data = *tmp; /* fill allocated data block with tmp values */
/* allocate/validate list node */
goodslist *node = malloc (sizeof *node);
if (!node) {
perror ("malloc-node");
free (data);
return NULL;
}
node->data = data; /* initialize data and set next NULL */
node->next = NULL;
if (*l) /* if list exists, chain next to list */
node->next = *l;
return ((*l = node)->data); /* assign new node as list, return data */
}
/* simple print list function */
void prn_list (goodslist *l)
{
if (!l)
return;
while (l) {
printf (" %-8s %-8s %8d %-8s %8d %9d\n", l->data->goods_id,
l->data->goods_name, l->data->goods_price,
l->data->goods_discount, l->data->goods_amount,
l->data->goods_remain);
l = l->next;
}
}
/* simple free list function */
void free_list (goodslist *l)
{
if (!l)
return;
goodslist *iter = l;
while (iter) {
goodslist *victim = iter;
free (iter->data);
iter = iter->next;
free (victim);
}
}
int main (int argc, char **argv)
{
char buf[MAXC]; /* read buffer */
size_t linecnt = 0; /* line counter */
goodslist *list = NULL; /* linked list pointer */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("fopen-file");
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line of data */
goodsinfo tmp = { .goods_id = "" }; /* temp data struct */
/* parse and validate data in buf (can be separate function) */
if (sscanf (buf, "%29s %29s %d %29s %d %d", tmp.goods_id,
tmp.goods_name, &tmp.goods_price, tmp.goods_discount,
&tmp.goods_amount, &tmp.goods_remain) != 6) {
fprintf (stderr, "error: invalid format line %zu.\n", linecnt+1);
continue;
}
if (!append (&list, &tmp)) /* append to list/validate */
break;
}
if (fp != stdin) /* close file if not stding */
fclose (fp);
prn_list (list); /* print list */
free_list (list); /* free list data */
return 0;
}
(note: your bool check_nullfile(void) does more harm than good and will fail if the first non-whitespace character isn't a digit)
Example Use/Output
(note: when using chaining without keeping a "last" pointer results in the list nodes stored in reverse order)
$ ./bin/ll_goodslist dat/goodsinfo.txt
1004 new5 100 0.8 70 80
1003 new4 88 0.8 70 80
1002 new3 70 0.8 10 10
1001 new2 80 0.9 80 80
1000 new1 90 0.9 90 80
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/ll_goodslist dat/goodsinfo.txt
==3493== Memcheck, a memory error detector
==3493== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3493== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==3493== Command: ./bin/ll_goodslist dat/goodsinfo.txt
==3493==
1004 new5 100 0.8 70 80
1003 new4 88 0.8 70 80
1002 new3 70 0.8 10 10
1001 new2 80 0.9 80 80
1000 new1 90 0.9 90 80
==3493==
==3493== HEAP SUMMARY:
==3493== in use at exit: 0 bytes in 0 blocks
==3493== total heap usage: 11 allocs, 11 frees, 1,152 bytes allocated
==3493==
==3493== All heap blocks were freed -- no leaks are possible
==3493==
==3493== For counts of detected and suppressed errors, rerun with: -v
==3493== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
footnotes
While not an error, C generally avoids the use of camelCase or MixedCase variable names in favor of all lower-case while reserving upper-case names for use with macros and constants. It is a matter of style -- so it is completely up to you, but failing to follow it can lead to the wrong first impression in some circles.
There are two problems. The main one is that you're creating the linked list incorrectly.
For the first record you read, the value of *L is undefined - you've only created L. So accessing (*L)->next will cause your program to crash.
listptr->next=(*L)->next;
But this is due to you having a misunderstanding about what L is in the first place. Your info_init function is being passed a GoodList ** because it is using that argument to pass back the newly created list. You're not meant to pass in a variable of GoodList **, you're meant to pass in a pointer to a GoodList *. This variable should be initialised as NULL to start with.
int main (void)
{
GoodsList *L=NULL;
info_init(&L);
return 0;
}
and then instead of
listptr->next=(*L)->next;
(*L)->next=listptr;
you have
listptr->next=*L;
*L=listptr;
This means that the newly created list node will point to the previous node which is stored in *L. As in main it is initially NULL, this means the first node will point next to NULL. And then *L will be updated to point to the first node. And then the next time around, the second node will point to the first etc...
The other issue isn't quite so bad - you'll always read one extra empty node, because you're extending the list and then trying to read something in. Your check for EOF is too early as it'll only register EOF after you've tried to read something in.
A more robust method of reading in each record is to read in each line using fgets and using sscanf to read each field. You can also remove the need for the check_nullfile function by checking the return value of fopen. If the file is empty, you'll have an empty list as nothing will be read or allocated.
You also don't need to cast the return value of malloc and it's safer to use sizeof(*listptr) to work out the amount of memory listptr needs as it'll work even if you change the type of listptr.
void info_init(GoodsList **L) {
FILE * fp;
fp=fopen("goodsinfo.txt", "r");
if(fp)
{
char line[512];
GoodsList *listptr;
while (fgets(line,512,fp))
{
listptr=malloc(sizeof(*listptr));
listptr->next=(*L);
(*L)=listptr;
sscanf(line,"%4s\t%4s\t%d\t%s\t%d\t%d",listptr->data.goods_id,
listptr->data.goods_name,
&(listptr->data.goods_price),
listptr->data.goods_discount,
&(listptr->data.goods_amount),
&(listptr->data.goods_remain));
CurrentCnt++;
}
fclose(fp);
}
printf("%d\n", CurrentCnt);
}
I have an Array of Strings inside a Struct that looks like this
#define ID_LEN 20
struct Person{
char name[ID_LEN];
int num_items;
char **items;
};
int main(){
int num_persons = 10;
struct Person *ptr[num_persons];
I start with an array of 10 persons. Initially all persons have 0 items so their list of items is an malloc(0).
for(int i = 0; i < num_persons; i++){
ptr[i] = (struct Person *) malloc(sizeof(struct Person) + 1);
ptr[i]->num_items = 0;
ptr[i]->items = malloc(0 * sizeof(char*));
}
At some point I want to name those persons and add them some items like this.
strcpy(ptr[0]->name, "John");
ptr[0]->num_items = 1;
ptr[0]->items = (char **)realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*));
strcpy(ptr[0]->items[0], "pencil");
printf("Name: %s\n", ptr[0]->name);
printf("Number of items: %d\n", ptr[0]->num_items);
for(int i = 0; i < ptr[0]->num_items; i++){
printf("Item %d is %s\n", i, ptr[0]->items[i]);
}
I'm getting segmentation fault: 11. I'm not sure if I have done realloc correctly or not.
You have several problems here
First your ID_LEN
nowhere do you check if by copying to name you surpass the ID_LEN
so instead of
strcpy(ptr[0]->name, "John");
use
strcpy_s(ptr[0]->name,sizeof(ptr[0]->name),"John or whatever"); // C11
You allocate
ptr[0]->items = (char **)realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*))
but
you should not cast return value of malloc/realloc (search on web for
explanation, it has been described ad nauseum)
when using realloc, you should first check the return value, it may fail.
char** tmp = realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*))
if (tmp != NULL)
{
ptr[0]->items = tmp;
}
else
{
abort();
}
The memory realloc returns is partly uninitialized (old pointers remain but
new ones are uninitialized. In your case you did not have any previous
pointers so that one items[0] is uninitialized.
so when you do
strcpy(ptr[0]->items[0], "pencil");
it will fail since items[0] is pointing to some arbitrary memory location.
after you realloc pointers you need initialize them to point to a
memory large enough to accommodate your string
E.g.
ptr[0]->items[0] = strdup("pencil"); // does malloc then copies string
It is not so efficient to use realloc everytime you need to add one new
item, instead allocate a bunch of items but set the ones you are not using
to NULL then keep track of how many are left, once they run out allocate
another bunch
All the problem in your code revolves around using memory which is not allocated.
Consider
ptr[0]->items = (char **)realloc(ptr[0]->items, ptr[0]->num_items * sizeof(char*));//line 1
strcpy(ptr[0]->items[0], "pencil");//line 2
In line 1 you have allocated memory to hold ptr[0]->num_items number of pointers to c strings.But you have not actually allocated memory to store c strings i.e. those pointers are not pointing to actual memory.
When in line 2 you try to access ptr[0]->items[0] its just char* there is no memory allocated to it.
I added the below line before line 2 and your code worked fine.
for(int i=0;i<ptr[0]->num_items;i++)
ptr[0]->items[i]=malloc(sizeof(char)*10);
In addition to what the other answers provide, there are several other considerations relevant to what you are doing. The first, don't 0 allocate anything. That is a holdover from days gone by and not necessary.
Next, following your static declaration of a variable length array of num_persons pointers to type struct Person, you must decide whether you will allocate storage for all your pointers at once, or whether you will allocate storage for the struct, only when adding a person. That will have implications when you split your code up into functions.
Since the remainder of your data structure will be allocated dynamically, you must validate each allocation succeeds before copying/initializing, whatever.
When you allocate your empty structure, take a close look at calloc instead of malloc. This is one instance where the default initialization provided by calloc can help.
The remainder of your task is just an accounting problem -- that is accounting for which members require allocation, the current state and size of each allocation, and then finally to free the memory you have allocated when it is no longer needed.
Take for example the code you might want for adding a person with an item to your array of pointers. To validate the addition, you will need to know that ptr is a valid pointer and whether you have reached your num_persons limit before you begin to allocate memory. You next must allocate storage for a struct Person and assign the address for that block to ptx[X] (for whatever index X you are working with).
Once allocated, you can initialize defaults (or if calloc was used, know that all values were initialized to 0/NULL). Following that you can copy name (limited to ID_LEN) and allocate pointers and storage for each item you wish to hold in your pointer-to-pointer-to-char* items (which you will handle similar to ptr accept that the pointers for items are allocated as well) Note there is no need to realloc (items,... at this point as this is the first time items is allocated.
If you put all of that together, you can come up with a function to add a person together with their first item similar to the following, where max is the maximum number of persons allowed, nm is the name, itm is the item, and idx is a pointer to the current index for that person:
/* add person with item */
struct person *addpwitem (struct person **ptr, int max, char *nm,
char *itm, int *idx)
{
if (!ptr || *idx + 1 == max) return NULL;
int i = *idx;
/* allocate storage for struct */
if (!(ptr[i] = calloc (1, sizeof **ptr))) {
fprintf (stderr, "error: ptr[%d], virtual memory exhausted.\n", i);
return NULL;
}
strncpy (ptr[i]->name, nm, ID_LEN); /* copy name */
/* allocate/validate pointer to char* ptr[i]->items */
if (!(ptr[i]->items =
malloc (ptr[i]->num_items + 1 * sizeof *(ptr[i]->items)))) {
fprintf (stderr, "error: items*, virtual memory exhausted.\n");
return NULL;
}
/* allocate/validate memory for ptr[num_items]->items[num_items] */
if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) {
fprintf (stderr, "error: items, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->num_items++;
(*idx)++;
return ptr[i];
}
(note: you must assign the return of the function to ptr[X] in the calling function. Also note, C-style recommends all lower-case names, which is what you see above, leave CamelCase names to C++)
Once you have a person added, you will want the ability to add items to the list of items associated with that person. This is where realloc comes in to allow you to resize the number of pointers-to-items for that person. You can do something similar to add person, but the index you pass no longer needs to be a pointer as it will not be updated within the function. e.g.
/* add item to person at index 'i' */
struct person *additem (struct person **ptr, int i, char *itm)
{
if (!ptr) return NULL;
void *tmp;
/* allocate/realloc/validate pointer to char* ptr[i]->items */
if (!(tmp = realloc (ptr[i]->items,
(ptr[i]->num_items + 1) * sizeof *(ptr[i]->items)))) {
fprintf (stderr, "error: items*, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->items = tmp; /* assign tmp on successful realloc */
/* allocate/validate memory for ptr[num_items]->items[num_items] */
if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) {
fprintf (stderr, "error: items, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->num_items++;
return ptr[i];
}
You are now able to add both persons and items to the list, but need a way to iterate over all values in the list if you are going to make use of them. Whether searching, printing, or freeing memory, all iteration functions will have similar form. To print all persons and items you could do something similar to the following:
/* print the list of persons and items */
void prndetail (struct person **ptr, int idx)
{
if (!ptr || !*ptr) return;
int i, p;
for (p = 0; p < idx; p++) {
printf (" %-20s %2d", ptr[p]->name, ptr[p]->num_items);
for (i = 0; i < ptr[p]->num_items; i++)
printf ("%s%s", i ? ", " : " ", ptr[p]->items[i]);
putchar ('\n');
}
}
In any code your write that dynamically allocates memory, you have 2 responsibilites regarding any block of memory allocated: (1) always preserves a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed. When you are done with your list, you will need to free it. Similar to printing, it can be done as follows:
/* free all allocated memory */
void deletelist (struct person **ptr, int idx)
{
if (!ptr || !*ptr) return;
int i, p;
for (p = 0; p < idx; p++) {
for (i = 0; i < ptr[p]->num_items; i++)
free (ptr[p]->items[i]);
free (ptr[p]->items);
free (ptr[p]);
}
// free (ptr); /* if array of pointers allocated, not static */
}
That's really all there is to the accounting lesson. If you keep track of each part of the data structure you are using, managing the memory is straight forward. Putting all the pieces together in a short example, you could do something like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { NPER = 10, ID_LEN = 20 };
struct person {
char name[ID_LEN];
int num_items;
char **items;
};
/* add person + item - to add both person and item at once */
struct person *addpwitem (struct person **ptr, int max, char *nm,
char *itm, int *idx);
/* add item to existing person */
struct person *additem (struct person **ptr, int i, char *itm);
void prndetail (struct person **ptr, int idx);
void deletelist (struct person **ptr, int idx);
int main (void) {
int idx = 0;
struct person *ptr[NPER];
/* allocate storage for struct, add person + item */
if (!(ptr[idx] = addpwitem (ptr, NPER, "John", "pencils", &idx))) {
fprintf (stderr, "error: adding ptr[%d] failed.\n", idx);
return 1;
}
printf ("\nadded John:\n");
prndetail (ptr, idx); /* print contents of persons & items */
additem (ptr, idx - 1, "pens"); /* add next item */
printf ("\nadded item 'pens' for John:\n");
prndetail (ptr, idx); /* print contents of persons & items */
/* add next person + item */
if (!(ptr[idx] = addpwitem (ptr, NPER, "Martha", "paper", &idx))) {
fprintf (stderr, "error: adding ptr[%d] failed.\n", idx);
return 1;
}
printf ("\nadded Martha:\n");
prndetail (ptr, idx); /* print contents of persons & items */
deletelist (ptr, idx); /* free all allocated memory */
return 0;
}
/* add person with item */
struct person *addpwitem (struct person **ptr, int max, char *nm,
char *itm, int *idx)
{
if (!ptr || *idx + 1 == max) return NULL;
int i = *idx;
/* allocate storage for struct */
if (!(ptr[i] = calloc (1, sizeof **ptr))) {
fprintf (stderr, "error: ptr[%d], virtual memory exhausted.\n", i);
return NULL;
}
strncpy (ptr[i]->name, nm, ID_LEN); /* copy name */
/* allocate/validate pointer to char* ptr[i]->items */
if (!(ptr[i]->items =
malloc (ptr[i]->num_items + 1 * sizeof *(ptr[i]->items)))) {
fprintf (stderr, "error: items*, virtual memory exhausted.\n");
return NULL;
}
/* allocate/validate memory for ptr[num_items]->items[num_items] */
if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) {
fprintf (stderr, "error: items, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->num_items++;
(*idx)++;
return ptr[i];
}
/* add item to person at index 'i' */
struct person *additem (struct person **ptr, int i, char *itm)
{
if (!ptr) return NULL;
void *tmp;
/* allocate/realloc/validate pointer to char* ptr[i]->items */
if (!(tmp = realloc (ptr[i]->items,
(ptr[i]->num_items + 1) * sizeof *(ptr[i]->items)))) {
fprintf (stderr, "error: items*, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->items = tmp; /* assign tmp on successful realloc */
/* allocate/validate memory for ptr[num_items]->items[num_items] */
if (!(ptr[i]->items[ptr[i]->num_items] = strdup (itm))) {
fprintf (stderr, "error: items, virtual memory exhausted.\n");
return NULL;
}
ptr[i]->num_items++;
return ptr[i];
}
/* print the list of persons and items */
void prndetail (struct person **ptr, int idx)
{
if (!ptr || !*ptr) return;
int i, p;
for (p = 0; p < idx; p++) {
printf (" %-20s %2d", ptr[p]->name, ptr[p]->num_items);
for (i = 0; i < ptr[p]->num_items; i++)
printf ("%s%s", i ? ", " : " ", ptr[p]->items[i]);
putchar ('\n');
}
}
/* free all allocated memory */
void deletelist (struct person **ptr, int idx)
{
if (!ptr || !*ptr) return;
int i, p;
for (p = 0; p < idx; p++) {
for (i = 0; i < ptr[p]->num_items; i++)
free (ptr[p]->items[i]);
free (ptr[p]->items);
free (ptr[p]);
}
// free (ptr); /* if array of pointers allocated */
}
Example Use/Output
$ ./bin/struct_p2p2c
added John:
John 1 pencils
added item 'pens' for John:
John 2 pencils, pens
added Martha:
John 2 pencils, pens
Martha 1 paper
Memory Error Check
It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an unintitialized value and finally to confirm that you have freed all the memory you have allocated.
It is simple to do:
$ valgrind ./bin/struct_p2p2c
==7618== Memcheck, a memory error detector
==7618== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==7618== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==7618== Command: ./bin/struct_p2p2c
==7618==
added John:
John 1 pencils
added item 'pens' for John:
John 2 pencils, pens
added Martha:
John 2 pencils, pens
Martha 1 paper
==7618==
==7618== HEAP SUMMARY:
==7618== in use at exit: 0 bytes in 0 blocks
==7618== total heap usage: 8 allocs, 8 frees, 115 bytes allocated
==7618==
==7618== All heap blocks were freed -- no leaks are possible
==7618==
==7618== For counts of detected and suppressed errors, rerun with: -v
==7618== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
Always confirm All heap blocks were freed -- no leaks are possible and equally important ERROR SUMMARY: 0 errors from 0 contexts.
Look over this and the rest of the answers. There are many good suggestions between them. Let us know if you have further questions.
You are allocating for the list of items, but you are not allocating space for each individual item string. Your
strcpy(ptr[0]->items[0], "pencil");
will fail.
I'm having trouble with what should be a simple program.
I've written a single linked list implementation in C using void* pointers. However, I have a problem, as there is a possible memory leak somewhere, however I checked the code using valgrind and it detected no such errors.
But when all the memory is free'd there is still some memory un-freed (see comments)... I tried passing everything to the add function by reference too, but this didn't fix the issue either.
I just wondered if anyone here had any comments from looking at the code. (This should be simple!, right?)
/*
Wrapping up singley linked list inside a struct
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* Needed for: memcpy */
void waitKey(){
printf("Press any key to continue...");
getchar();
}
/* Define a structure for a list entry */
struct ListEntry {
void* data;
struct ListEntry* pNext;
};
/* Struct for list properties */
struct ListProperties {
struct ListEntry* g_pLast;
struct ListEntry* g_pHead;
struct ListEntry* pCurrent;
unsigned int size;
int getHead;
};
/* Add:
args: list, data, dyn (0 if not, else size of dynamic data)
*/
void add(struct ListProperties* l, void* d, unsigned long dyn) {
struct ListEntry* pNew = malloc(sizeof(struct ListEntry));
/* Set the data */
if (dyn > 0){
/* Allocate and copy array */
pNew->data = malloc(dyn);
pNew->data = memcpy(pNew->data,d,dyn);
} else {
pNew->data = d;
}
/* Set last element to point to new element */
if (l->g_pLast != NULL){
l->g_pLast->pNext = pNew;
/* Get head of list */
if (l->g_pHead == NULL && l->getHead == 0){
l->g_pHead = l->g_pLast;
l->getHead = 1;
}
} else {
/* 1 elem case */
l->g_pHead = pNew;
l->pCurrent = pNew;
}
/* New element points to NULL */
pNew->pNext = NULL;
/* Save last element for setting
pointer to next element */
l->g_pLast = pNew;
/* Inc size */
l->size++;
}
/* Create new list and return a pointer to it */
struct ListProperties* newList(){
struct ListProperties* nList = malloc (sizeof(struct ListProperties));
nList->g_pHead = NULL;
nList->g_pLast = NULL;
nList->getHead = 0;
nList->size = 0;
return nList;
}
/* Reset pointer */
int reset(struct ListProperties *l){
if (l->g_pHead != NULL){
l->pCurrent = l->g_pHead;
return 0;
}
return -1;
}
/* Get element at pointer */
void* get(struct ListProperties *l) {
if (l->size > 0){
if (l->pCurrent != NULL){
return l->pCurrent->data;
}
}
return NULL;
}
/* Increment pointer */
int next(struct ListProperties *l){
if (l->pCurrent->pNext != NULL){
l->pCurrent = l->pCurrent->pNext;
return 1;
}
return 0;
}
/* Get element at n */
void* getatn(struct ListProperties *l, int n) {
if (l->size > 0){
int count = 0;
reset(l);
while (count <= n){
if (count == n){
return l->pCurrent->data;
break;
}
next(l);
count++;
}
}
return NULL;
}
/* Free list contents */
void freeList(struct ListProperties *l){
struct ListEntry* tmp;
/* Reset pointer */
if (l->size > 0){
if (reset(l) == 0){
/* Free list if elements remain */
while (l->pCurrent != NULL){
if (l->pCurrent->data != NULL)
free(l->pCurrent->data);
tmp = l->pCurrent->pNext;
free(l->pCurrent);
l->pCurrent = tmp;
}
}
}
l->g_pHead = NULL;
l->g_pLast = NULL;
l->size = 0;
l->getHead = 0;
free(l);
}
void deleteElem(struct ListProperties *l, int index){
struct ListEntry* tmp;
int count = 0;
if (index != 0)
index--;
reset(l);
while (count <= index){
if (count == index){ // Prev element
if (l->pCurrent != NULL){
if (l->pCurrent->pNext != NULL){
free(l->pCurrent->pNext->data); // Free payload
tmp = l->pCurrent->pNext;
l->pCurrent->pNext = l->pCurrent->pNext->pNext;
free(tmp);
if (l->size > 0)
l->size--;
} else {
// Last element
free(l->pCurrent->data);
free(l->pCurrent);
l->g_pHead = NULL;
l->g_pLast = NULL;
l->getHead = 0;
l->size = 0;
}
}
break;
}
if (next(l) != 1)
break;
count++;
}
}
int size(struct ListProperties *l){
return l->size;
}
int main( int argc, char* argv )
{
int j = 0;
unsigned long sz = 0;
/*=====| Test 1: Dynamic strings |=====*/
/* Create new list */
struct ListProperties* list = newList();
if (list == NULL)
return 1;
char *str;
str = malloc(2);
str = strncat(str,"A",1);
sz = 2;
printf("Dynamic Strings\n===============\n");
/* Check memory usage here (pre-allocation) */
waitKey();
/* Add to list */
for (j = 0; j < 10000; j++){
add(list,(char*)str, sz);
str = realloc(str, sz+2);
if (str != NULL){
str = strncat(str,"a",1);
sz++;
}
}
/* Allocated strings */
waitKey();
/* TESTING */
freeList(list);
free(str);
/* Check memory usage here (Not original size!?) */
waitKey();
return 0;
}
Thanks!
You don't say how you are checking memory usage, but I'm going to guess that you are using ps or something similar to see how much memory the OS has given the process.
Depending on your memory allocator, calling free may or may not return the memory to the OS. So even though you are calling free, you will not see the memory footprint decrease from the OS's point of view.
The allocator may keep a cache of memory that is given to it by the OS. A call to malloc will first look in this cache to see if it can find a big enough block and if so, malloc can return without asking the OS for more memory. If it can't find a big enough block, malloc will ask the OS for more memory and add it to it's cache.
But free may simply add the memory back to the cache and never return it to the OS.
So, what you may be doing is seeing the allocators cache and not any memory leak.
As was mentioned, I would not trust the memory usage reported by the task manager as there are other factors beyond your control that impact it (how malloc/free are implemented, etc).
One way you can test for memory leaks is by writing your own wrapper functions around the existing malloc and free functions similar to:
void* my_malloc(size_t len) {
void* ptr = malloc(len);
printf("Allocated %u bytes at %p\n", len, ptr);
return ptr;
}
void my_free(void* ptr) {
printf("Freeing memory at %p\n", ptr);
free(ptr);
}
Now, you will get a log of all memory that is dynamically allocated or freed. From here, it should be fairly obvious if you leak a block of memory (the more complex your program is, the longer your log will be and the more difficult this task will be).
Your program contains incorrect argv in main, incorrect usage of strncat, and strange memory allocation. Some of these should of shown up as warnings. The argv is a non-issue, but if the others showed up as warning, you needed to heed them. Don't ignore warnings.
These changes clean it up. The biggest thing was that you don't seem to have a good grasp on the NUL ('\0') character (different than NULL pointer) used to terminate C strings, and how that effects str(n)cat.
The mixed usage of str* functions with memory functions (*alloc/free) was likely part of the confusion. Be careful.
#include <assert.h>
...
int main( int argc, char* argv[] ) /* or int main(void) */
...
sz = 2;
str = (char*) malloc(sz); /* allocate 2 bytes, shortest non-trivial C string */
assert(str != NULL);
strncpy(str, "A", sz); /* copy 'A' and '\0' into the memory that str points to */
...
/* Add to list */
for (j = 0; j < 10000; j++){
add(list, str, sz);
str = realloc(str, ++sz); /* realloc str to be one (1) byte larger */
assert(str != NULL);
strncat(str, "a", sz - strlen(str)); /* now insert an 'a' between last 'A' or 'a' and '\0' */
assert(str != NULL);
}