Why does valgrind not show the obvious leaks in my program? [closed] - c

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
As I was compiling a program in which I used malloc twice, I realised that I forgot to free the allocated memory at the end of the program.
Having recently encountered the Valgrind tool, I thought it would be a nice occasion to run it to see how it displays memory leaks errors. But to my surprise, Valgrind returned that all heaps blocks were freed...
Valgrind results
The only thing I can think of is that the mallocs were not in the main function but in a subordinate function. But I don't think that Valgrind would be restricted to main, would it
I have tried to find an answer in Valgrind documentation, but to no avail.
Here is the complete code as requested, the aim of which is to cipher a text thanks to a 26 letters key provided by the user as a command line argument.
#include <stdio.h>
#include <cs50.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
void cipher(char *, char *);
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("Usage: ./substitution key\n");
return 1;
}
//INPUT
char *p = get_string("plaintext: ");
//OUTPUT
cipher(argv[1], p);
return 0;
}
void cipher(char *a, char *b)
{
char *low = malloc(27 * sizeof(char));
for (int i = 0; i < 27; i++)
{
if (islower(a[i]))
{
low[i] = a[i];
}
else
{
low[i] = (a[i] + 32);
}
}
char *upp = malloc(27 * sizeof(char));
for (int i = 0; i < 27; i++)
{
if (isupper(a[i]))
{
upp[i] = a[i];
}
else
{
upp[i] = a[i] - 32;
}
}
printf("ciphertext: ");
for (int i = 0, l = strlen(b); i < l; i++)
{
if (islower(b[i]))
{
printf("%c", low[(int)b[i]-97]);
}
else if (isupper(b[i]))
{
printf("%c", upp[(int)b[i]-65]);
}
else
{
printf("%c", b[i]);
}
}
}

After all those comments, the answer is kind of silly...
Your screenshot shows you ran valgrind ./substitution, without passing any command line arguments to your program. When this happens it prints Usage: ./substitution key and exits without doing anything further; in particular without calling cipher() and thus without ever calling malloc(). So of course there is no memory leak in this case.
If you run the program with a key argument on the command line, and give it some input, you should see valgrind report the memory leak:
==15815==
==15815== HEAP SUMMARY:
==15815== in use at exit: 54 bytes in 2 blocks
==15815== total heap usage: 4 allocs, 2 frees, 2,102 bytes allocated
==15815==
==15815== LEAK SUMMARY:
==15815== definitely lost: 54 bytes in 2 blocks
==15815== indirectly lost: 0 bytes in 0 blocks
==15815== possibly lost: 0 bytes in 0 blocks
==15815== still reachable: 0 bytes in 0 blocks
==15815== suppressed: 0 bytes in 0 blocks
==15815== Rerun with --leak-check=full to see details of leaked memory
Although in principle a compiler could optimize out the malloc calls, in my tests with gcc and clang with -O3, I couldn't find any indication of them actually doing so.

Related

Question about freeing malloc()ed memory in C

I'm doing the CS50 course (so please don't give me the exact right answer, but point me in the right direction!
I got my program (below) to work (although I'm not sure if I did it 'the right way'); it prints the 8 license plates from plates.txt. However, valgrind still tells me I'm losing some bytes. I know for sure that it has to do with my 'temp' thing allocating memory in the loop. I just don't know how to fix it. If anyone could point me in the right direction, that would be wonderful!
Valgrind:
==18649== HEAP SUMMARY:
==18649== in use at exit: 49 bytes in 7 blocks
==18649== total heap usage: 10 allocs, 3 frees, 4,624 bytes allocated
==18649==
==18649== 49 bytes in 7 blocks are definitely lost in loss record 1 of 1
==18649== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==18649== by 0x109257: main (license.c:39)
==18649==
==18649== LEAK SUMMARY:
==18649== definitely lost: 49 bytes in 7 blocks
==18649== indirectly lost: 0 bytes in 0 blocks
==18649== possibly lost: 0 bytes in 0 blocks
==18649== still reachable: 0 bytes in 0 blocks
==18649== suppressed: 0 bytes in 0 blocks
==18649==
==18649== For lists of detected and suppressed errors, rerun with: -s
==18649== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Program code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
// Check for command line args
if (argc != 2)
{
printf("Usage: ./read infile\n");
return 1;
}
// Create buffer to read into
char buffer[7];
// Create array to store plate numbers
char *plates[8];
// Create a pointer that will later point to a place in memory on the heap for strcpy
char *temp = NULL;
FILE *infile = fopen(argv[1], "r");
if (infile == NULL)
{
printf("File not found.\n");
return 1;
}
int idx = 0;
while (fread(buffer, 1, 7, infile) == 7)
{
// Replace '\n' with '\0'
buffer[6] = '\0';
// Allocate memory to temporarily store buffer contents
temp = malloc(sizeof(buffer));
// Copy buffer contents to temp
strcpy(temp, buffer);
// Save plate number in array
plates[idx] = temp;
idx++;
}
fclose(infile);
for (int i = 0; i < 8; i++)
{
printf("%s\n", plates[i]);
}
free(temp);
return 0;
}
I fclosed the file and freed my 'temp' location in the heap. However, I malloc() temp multiple times, but I can't free(temp) multiple times?
free(temp); is wrong, it just deletes the last item allocated.
Since you called malloc in a loop, you'll also have to call free in a loop. The rule of thumb is that every malloccall must be matched by a freecall.
So you have to make a for loop iterating across char *plates[8]; and free everything that each plates pointer points at.
You need to free all allocated pointers.
for(i = 0; i < idx; i++)
free(plates[i]);
You also can very easily fo beyond your array bounds as you do not check the index
while (idx < 8 && fread(buffer, 1, 7, infile) == 7)
Printing is also wrong as you assume that you have read all 8 chunks.
for (int i = 0; i < idx; i++)
{
printf("%s\n", plates[i]);
}

Using Dynamic Memory get every second element in a char array into another

I've been tasked with getting writing a function that uses dynamic memory and will take a string s and pull out every second element of the string, and then return a new string with those elements. So far my code is:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
char* skipping(const char* s)
{
int inc = 0; //new list incrementer
int len = strlen(s);
char* new_s = malloc(len + 1);
for (int i = 0; i < len - 1; i+=2) {
new_s[inc] = s[i];
inc++;
}
return new_s;
}
int main(void)
{
char* s = skipping("0123456789");
printf("%s\n", s);
free(s);
return 0;
}
This works, however when I run it using Valgrind I get told I have an error, which comes from using strlen, but I can't seem to fix it. Any help would be awesome!
Error messages: (in valgrind)
==4596==
==4596== Conditional jump or move depends on uninitialised value(s)
==4596== at 0x4C32D08: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4596== by 0x4EBC9D1: puts (ioputs.c:35)
==4596== by 0x1087B4: main (in /home/ryan/ENCE260/lab6)
==4596==
02468 //this is the expected output
==4596==
==4596== HEAP SUMMARY:
==4596== in use at exit: 0 bytes in 0 blocks
==4596== total heap usage: 2 allocs, 2 frees, 1,035 bytes allocated
==4596==
==4596== All heap blocks were freed -- no leaks are possible
==4596==
==4596== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Why is Valgrind reporting this error?
From the Valgrind online manual on the use of uninitialised or unaddressable values in system calls:
Sources of uninitialised data tend to be:
- Local variables in procedures which have not been initialised.
- The contents of heap blocks (allocated with malloc, new, or a similar function) before you (or a constructor) write something there.
Valgrind will complain if:
the program has written uninitialised junk from the heap block to the standard output.
Since you have used s in printf without null-terminating it, it caused the error.

I get segmentation fault (core dumped) when I tried to run program with struct

I need to read a file and store the data from the file into a structure. The first line of the file contains the size of the array of structs that i have to dynamically allocate.
4
12/04/2010
Interview went well I think, though was told to wear shoes.
18/04/2010
Doc advised me to concentrate on something... I forget.
03/05/2010
Was asked today if I was an art exhibit.
19/05/2010
Apparently mudcakes not made of mud, or angry wasps.
I am to run my code perfectly in Windows but when I run in Unix environment it shows me segmentation fault (core dumped). I did use valgrind to check for the memory leak and that is the results
==4344== Invalid read of size 1
==4344== at 0x407F842: ____strtol_l_internal (strtol_l.c:298)
==4344== by 0x407F606: strtol (strtol.c:108)
==4344== by 0x407C87E: atoi (atoi.c:27)
==4344== by 0x8048837: main (in /home/admininistrator/ucp/p6/gg)
==4344== Address 0x0 is not stack'd, malloc'd or (recently) free'd
==4344==
==4344==
==4344== Process terminating with default action of signal 11 (SIGSEGV)
==4344== Access not within mapped region at address 0x0
==4344== at 0x407F842: ____strtol_l_internal (strtol_l.c:298)
==4344== by 0x407F606: strtol (strtol.c:108)
==4344== by 0x407C87E: atoi (atoi.c:27)
==4344== by 0x8048837: main (in /home/admininistrator/ucp/p6/gg)
==4344== If you believe this happened as a result of a stack
==4344== overflow in your program's main thread (unlikely but
==4344== possible), you can try to increase the size of the
==4344== main thread stack using the --main-stacksize= flag.
==4344== The main thread stack size used in this run was 8388608.
==4344==
==4344== HEAP SUMMARY:
==4344== in use at exit: 1,396 bytes in 3 blocks
==4344== total heap usage: 3 allocs, 0 frees, 1,396 bytes allocated
==4344==
==4344== LEAK SUMMARY:
==4344== definitely lost: 0 bytes in 0 blocks
==4344== indirectly lost: 0 bytes in 0 blocks
==4344== possibly lost: 0 bytes in 0 blocks
==4344== still reachable: 1,396 bytes in 3 blocks
==4344== suppressed: 0 bytes in 0 blocks
==4344== Rerun with --leak-check=full to see details of leaked memory
==4344==
==4344== For counts of detected and suppressed errors, rerun with: -v
==4344== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)
Here is my code attached
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include"struct.h"
int main(int argc, char* argv[])
{
if (argc < 2)
{
printf("You have enter less arguments.\n");
}
else if (argc > 2)
{
printf("You have enter too many arguments.");
}
else
{
FILE *file;
Diary *res;
Diary *res2;
char line[102];
int i, size, k, l, choice;
int day, month, year;
/* int d[10],m[10],y[10];*/
char as[102];
char* oken;
char* yoken;
char* coken;
oken = NULL;
yoken = NULL;
coken = NULL;
i = 0;
file = fopen("struct.txt", "r");
if (file == NULL)
{
perror("Error opening file\n.");
}
else
{
fscanf(file, "%d", &size);
res = (Diary*) malloc(size * sizeof(Diary));
res2 = (Diary*) calloc((5), sizeof(Diary));
while (fgets(line, sizeof(line), file) != NULL)
{
oken = strtok(line, "/");
if (oken != NULL)
{
res2[i].day= atoi(oken);
coken = strtok(NULL, "/");
if (oken != NULL)
{
res2[i].month = atoi(coken);
yoken = strtok(NULL, "\n ");
if (coken != NULL)
{
/*printf("%s",yoken);*/
res2[i].year = atoi(yoken);
fgets(as, 102, file);
strncpy(res2[i].entry, as, 102);
}
}
}
i++;
}
k = 1;
l = 0;
while (l < size)
{
res[l].day = res2[k].day;
res[l].month = res2[k].month;
res[l].year = res2[k].year;
strncpy(res[l].entry, res2[k].entry, 102);
k++;
l++;
}
choice = atoi(argv[1]);
printf("%d-%02d-%02d:%s",res[choice].year, res[choice].month,res[choice].day,res[choice].entry);
free(res2);
free(res);
}
fclose(file);
}
return 0;
}
I need to read all the data from the file to the struct and print it out whenever user want the entry. I tried to debug part by part, and I found out it is the part while( fgets( line, sizeof( line ), file) != NULL) that loop, gives the problem. But I have no idea how to fix it.
My struct.h is given as below:
typedef struct journal{
int day;
int month;
int year;
char entry[1024];
} Diary;
I don't quite understand all of what you are trying to achieve, but here are a few issues.
You probably don't need the day month year variables.
This line
day = atoi(oken);
should probably be
res2[i].day = atoi(oken);
There is a problem with the line that reads the size
fscanf(file, "%d", &size)
This reads an integer but it does not read the trailing newline
You need to change this to be something like
fscanf(file, "%d\n", &size)
or use fgets.
Because of the trailing newline, the next time you call gets you obtain a string containing just the newline.
Your strtok calls and NULL checks are out of phase. The first one, for oken is OK. But then you do a strtok returning coken but a NULL check on oken and lastly a strtok returning yoken and a NULL check on coken. In all 3 cases, the call to strtok should be followed by a NULL check on the returned value (as is the case for oken).
I don't understand the purpose of the while (l < size) loop (maybe because of the mishandling of the newline as described above?). You allocate 5 structs, read 4 from structs.txt into res2 (elements 0 to 3) then you copy elements 1 to 4 of res2 into elements 0 to 3 of res. This means that element 0 of res2 doesn't get copied and element 4 which is all zeroes does get copied.
The cause of the crash is a combination of points 3 and 4.
I would advise against using atoi as it does no error checking. It is unsafe to use unless you are certain that the string contains a well-formed integer. If you want your code to be robust, you need to add more error checking e.g., check the return values of malloc and calloc.

Memory leak in C, most likely due to realloc

I have a program that is supposed to receive input, remember the longest string and print it out at EOF. My code works, but when run through a debugger there is a memory leak detected. I am compiling in Windows and do not have a proper debugger like Valgrind so I do not get much information about the error. The only thing I can imagine can cause this leak is the realloc() or free() function. However, I am not skilled enough in C to understand what the problem is.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
int main(void)
{
char *p;
char *line;
int sc;
p = (char*) malloc ( sizeof(char) );
line = (char*) malloc ( sizeof(char) );
int count = 0;
int max = 0;
p[count] = 0;
while ( ( sc = getchar()) != EOF ) {
if ( p == NULL ) {
p = (char*) realloc ( p, sizeof(char) );
}
if ( isalpha(sc) ) {
p[count] = sc;
count++;
p = (char*) realloc( p, (count+1)*sizeof(char) );
p[count] = 0;
} else if ( sc == '\n' || sc == ' ' ) {
if ( count > max ) {
line = (char*) realloc( line, (count+1)*sizeof(char) );
strcpy( line, p );
max = count;
} else if ( count == 0) {
printf("%d characters in longest word: %s\n", max, line);
free(line);
free(p);
break;
}
count = 0;
}
}
return 0;
}
You state in the comments that you "have tried moving both free() to the end of the program", but I don't believe you. The problem appears to be that this is a badly designed program which does not always reach the free() statements because when sc is a space or newline, count is unlikely to be 0, and after count is reset to 0, the next character is read into sc (unlikely to be a space or newline).
Simply moving the calls to free() to the end of the program fixes the memory leak, which was reported by Valgrind:
λ> valgrind --tool=memcheck ./a.out
==2967== Memcheck, a memory error detector
==2967== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==2967== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==2967== Command: ./a.out
==2967==
this is a test
==2967==
==2967== HEAP SUMMARY:
==2967== in use at exit: 10 bytes in 2 blocks
==2967== total heap usage: 14 allocs, 12 frees, 42 bytes allocated
==2967==
==2967== LEAK SUMMARY:
==2967== definitely lost: 10 bytes in 2 blocks
==2967== indirectly lost: 0 bytes in 0 blocks
==2967== possibly lost: 0 bytes in 0 blocks
==2967== still reachable: 0 bytes in 0 blocks
==2967== suppressed: 0 bytes in 0 blocks
==2967== Rerun with --leak-check=full to see details of leaked memory
==2967==
==2967== For counts of detected and suppressed errors, rerun with: -v
==2967== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
But there are many other issues with the code. First, never store the result of a call to realloc() in the only copy of a pointer you have to the memory being reallocated; a null pointer may be returned, and if so you then have a memory leak and lost data. Instead, use a temporary variable to hold the result, and assign this value to the original pointer only after checking for a null pointer. If a null pointer has been returned, the simplest solution is probably to terminate the program with an error message, as is done below; the error could be handled in other ways, so long as it is handled.
The posted code is more complex than it needs to be, with multiple calls to malloc() and realloc(). Instead, initialize p and line to NULL, and reallocate only as needed. There is no need to begin by allocating space for 1 char; this can be done when the first character needs to be stored. Also, there is no need to cast the result of malloc() in C (a different story in C++); and, sizeof char is always 1, so this is redundant and only clutters the code.
The fundamental problem in the posted code seems to be that when a character is read, count is incremented. Then if this character is a space or a newline, count may not be 0, so the exit with deallocation condition may not be met. Instead of complicated conditions, rethink the flow of the program.
After reading a character (unless EOF is encountered), if that character is alphabetic, count should be incremented, and p should be reallocated. If this step is successful, the character should be stored in p[], which should then be null-terminated.
Otherwise, if the character is a \n or a space, max should be compared with count. If count is larger, then line should be reallocated. If this step is successful, the string pointed to by p should be copied to line[]. Then max is given the value of count, and count is reset to 0.
After the loop has terminated, the results are printed only if there were words in the input. Then deallocation can occur before the program terminates.
The isalpha() function and similar functions from ctype.h expect an int value in the range of an unsigned char (or EOF). Often you need to cast the value of arguments to these functions to unsigned char to avoid undefined behavior. But, in this case the cast is unnecessary since getchar() returns an int value in the range of an unsigned char (or EOF).
You might also consider using the isspace() function instead of sc == '\n' || sc == ' '. This would allow other whitespace characters, such as '\t', to separate words in the input. As written in the OP, an input of "one\tword" (where '\t' is a tab character) would result in an output of:
7 characters in longest word: oneword
Here is a modified version of the posted code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
int main(void)
{
char *p = NULL;
char *line = NULL;
int sc;
int count = 0;
int max = 0;
while ((sc = getchar()) != EOF) {
if (isalpha(sc)) {
++count; // read a letter
char *temp = realloc(p, count + 1); // +1 for '\0'
if (temp == NULL) { // check allocation
perror("Failure to reallocate p");
exit(EXIT_FAILURE);
}
p = temp; // OK to reassign p
p[count-1] = sc; // store character
p[count] = 0; // add null-terminator
} else if (isspace(sc)) {
if (count > max) {
char *temp = realloc(line, count + 1); // +1 for '\0'
if (temp == NULL) { // check allocation
perror("Failure to reallocate line");
exit(EXIT_FAILURE);
}
line = temp; // OK to reassign line
strcpy(line, p);
max = count;
}
count = 0;
}
}
if (max > 0) {
printf("%d characters in longest word: %s\n", max, line);
} else {
puts("No words in input");
}
free(line);
free(p);
return 0;
}
And here is a clean bill of health from Valgrind:
λ> valgrind --tool=memcheck ./a.out
==4753== Memcheck, a memory error detector
==4753== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==4753== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==4753== Command: ./a.out
==4753==
this is a testrun
7 characters in longest word: testrun
==4753==
==4753== HEAP SUMMARY:
==4753== in use at exit: 0 bytes in 0 blocks
==4753== total heap usage: 16 allocs, 16 frees, 69 bytes allocated
==4753==
==4753== All heap blocks were freed -- no leaks are possible
==4753==
==4753== For counts of detected and suppressed errors, rerun with: -v
==4753== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
I think during debugging after the free() function call; you checked the value of the pointer p and line, and you still see the value(string) it is pointing to. If so that is not memory leak because free() does not change the value of pointer or assign 0 or '\0' character in it. free() just free the block of memory so that next time when you call any memory allocation function it will get that memory as available memory, and allocate it. Therefore after calling free(), we always assign NULL to the pointer like p = NULL;.
Correct the code as below and try:-
free(line);
free(p);
line = NULL;
p = NULL;
break;
Based on your coding approach I'll use calloc (you need) instead of malloc to get rid of that:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
int main(void)
{
char *p = NULL;
char *line = NULL;
int sc;
p = calloc ( sizeof(char), 1);
line = calloc ( sizeof(char),1 );
size_t count = 0;
size_t max = 0;
p[count] = 0;
while ( ( sc = getchar()) != EOF ) {
if ( p == NULL ) {
p = realloc ( p, sizeof(char) );
}
if ( isalpha(sc) ) {
p[count] = (char)sc;
count++;
p = realloc( p, (count+1) * sizeof(char) );
p[count] = 0;
} else if ( sc == '\n' || sc == ' ' ) {
if ( count > max ) {
line = realloc( line, (count+1)*sizeof(char) );
strcpy( line, p );
max = count;
} else if ( count == 0) {
printf("%zu characters in longest word: %s\n", max, line);
free(line);
free(p);
break;
}
count = 0;
}
}
return 0;
}
Output with Valgrind:
==4362== Memcheck, a memory error detector
==4362== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==4362== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==4362== Command: ./program
==4362==
Hello World
5 characters in longest word: Hello
==4362==
==4362== HEAP SUMMARY:
==4362== in use at exit: 0 bytes in 0 blocks
==4362== total heap usage: 15 allocs, 15 frees, 2,096 bytes allocated
==4362==
==4362== All heap blocks were freed -- no leaks are possible
==4362==
==4362== For counts of detected and suppressed errors, rerun with: -v
==4362== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Now try to understand, why is not working with malloc and why is working with calloc.
EDIT
Are you sure that is all about a leak? and is not about an uninitialised value?
I'm saying because if you type a letter works fine, the problem comes with Numbers, hence the calloc sugestion.
This is the Output of Valgrind using your code with no modification:
==5042== Memcheck, a memory error detector
==5042== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==5042== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==5042== Command: ./program
==5042==
1
==5042== Conditional jump or move depends on uninitialised value(s)
==5042== at 0x4E88CC0: vfprintf (vfprintf.c:1632)
==5042== by 0x4E8F898: printf (printf.c:33)
==5042== by 0x40082B: main (in /home/michael/program)
==5042== Uninitialised value was created by a heap allocation
==5042== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5042== by 0x400715: main (in /home/michael/program)
==5042==
0 characters in longest word:
==5042==
==5042== HEAP SUMMARY:
==5042== in use at exit: 0 bytes in 0 blocks
==5042== total heap usage: 4 allocs, 4 frees, 2,050 bytes allocated
==5042==
==5042== All heap blocks were freed -- no leaks are possible
==5042==
==5042== For counts of detected and suppressed errors, rerun with: -v
==5042== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Pointing to an uninitialised pointer vs Pointing to it after allocating memory for it

This doubt is very specific , consider the below code
The two lines in the block, are the lines I am confused with. ie. when I exchange those two lines I get a segmentation fault , but this code runs.So my question is
what is happening when I interchange the two lines?
#include<stdio.h>
#include<stdlib.h>
typedef struct scale_node_s {
char note[4];
struct scale_node_s *linkp;
} scale_node_t;
int main(){
scale_node_t *scalep, *prevp, *newp,*walker;
int i;
scalep = (scale_node_t *)malloc(sizeof (scale_node_t));
scanf("%s", scalep->note);
prevp = scalep;
for(i = 0; i < 7; ++i) {
//---------------------------------------
newp = (scale_node_t *)malloc(sizeof (scale_node_t));
prevp->linkp = newp;
//---------------------------------------
scanf("%s", newp->note);
prevp = newp;
}
walker = scalep;
for(i = 0 ; i < 7 ; i++){
printf("%s",walker->note);
walker = walker->linkp;
}
}
The line newp = (scale_node_t *)malloc(sizeof (scale_node_t)); allocates a piece of memory needed to hold an instance of scale_node_t and makes newp to hold that address. On the next line, you pass newp to a struct to be the value of linkp.Since on the first run of the loop newp is defined, but its value is not determined, it can hold several values depending on OS (and maybe on compiler too): memory waste, or 0 (so newp even can be null pointer there), segmentation fault occures hence.It is not allowed to use any variable before initialization (pointers are actually variables, holding a memory address as a number), however some editor/environment/compiler may not warn you about it at compile time.
In addition to the other answer, you have several additional issues that are making your list logic very confused and brittle. First, you are hardcoding loop iterations that may or may not match your input. The entire purpose of a linked-list is to provide a flexible data structure that allows you to store an unknown number of nodes. for(i = 0; i < 7; ++i) defeats that purpose entirely.
What is it you want to store? What is your input? (the note strings). Why not condition creation of additional nodes on input of a valid note? It can be as simple as:
char tmp[MAXC] = "";
...
while (scanf ("%3s", tmp) == 1) {
...
strcpy (newp->note, tmp); /* set note and linkp */
...
}
You are also leaving yourself wide open to processing invalid values throughout your code. Why? You fail to validate your user input and memory allocations. If either fail, you continue to blindly use undefined values from the point of failure forward. ALWAYS validate ALL user input and memory allocations. It is simple to do, e.g.
if (!(scalep = malloc (sizeof *scalep))) { /* allocate/validate */
fprintf (stderr, "error: virtual memory exhausted, scalep.\n");
return 1;
}
if (scanf ("%3s", scalep->note) != 1) { /* validate ALL input */
fprintf (stderr, "error: invalid input, scalep->note\n");
return 1;
}
Finally, there is no need for a prevp. That will only be required when removing or swapping nodes (you have to rewire the prev pointer to point to next (your linkp) after you remove or swap a node. You do neither in your code. There is also no need for an int value. You iterate current node = next node; to iterate though your list. (there are several variations of how to do this). Putting all the pieces together, and attempting to lay the code out in a slightly more logical way, you could do something similar to the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 4
typedef struct scale_node_s {
char note[MAXC];
struct scale_node_s *linkp;
} scale_node_t;
int main (void)
{
scale_node_t *scalep, *newp, *walker;
char tmp[MAXC] = "";
if (!(scalep = malloc (sizeof *scalep))) { /* allocate/validate */
fprintf (stderr, "error: virtual memory exhausted, scalep.\n");
return 1;
}
if (scanf ("%3s", scalep->note) != 1) { /* validate ALL input */
fprintf (stderr, "error: invalid input, scalep->note\n");
return 1;
}
scalep->linkp = NULL;
while (scanf ("%3s", tmp) == 1) {
if (!(newp = malloc (sizeof *newp))) { /* allocate/validate */
fprintf (stderr, "error: virtual memory exhausted, newp.\n");
break;
}
strcpy (newp->note, tmp); /* set note and linkp */
newp->linkp = NULL;
walker = scalep; /* set walker to scalep */
while (walker->linkp) /* find last node */
walker = walker->linkp; /* linkp !NULL move to next node */
walker->linkp = newp;
}
walker = scalep; /* output list */
while (walker) {
printf ("%s\n", walker->note);
walker = walker->linkp;
}
walker = scalep; /* free list memory */
while (walker) {
scale_node_t *victim = walker; /* save victim address */
walker = walker->linkp;
free (victim); /* free victim */
}
return 0;
}
Example Use/Output
$ echo "a b c d e" | ./bin/llhelp
a
b
c
d
e
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 write beyond/outside the bounds of your allocated block of memory, 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.
$ echo "a b c d e" | valgrind ./bin/llhelp
==25758== Memcheck, a memory error detector
==25758== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==25758== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==25758== Command: ./bin/llhelp
==25758==
a
b
c
d
e
==25758==
==25758== HEAP SUMMARY:
==25758== in use at exit: 0 bytes in 0 blocks
==25758== total heap usage: 5 allocs, 5 frees, 80 bytes allocated
==25758==
==25758== All heap blocks were freed -- no leaks are possible
==25758==
==25758== For counts of detected and suppressed errors, rerun with: -v
==25758== 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 any additional questions.

Resources