473,395 Members | 1,666 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,395 software developers and data experts.

trouble with double free

Hello, I'm working on the exercises from k&r, exercise 5-13 is to
implement a simple replacement for the unix tail command. The brief
says it should be able to cope no matter how unreasonable the input and
should make best use of availbale storage (I suppose it means dont just
declare a big char array).

I have made a solution that seems to be basically working (tail -1
doesnt work, I'll try to fix that later), but I'm having trouble
free'ing allocated memory, if i add the call to free() where i thought
it should go my c library kills it (gnu c library) as it detects an
error.

If I just dont free the memory, it works, but I suppose that is bad
form.

I have added a comment where I thought I should be using free(), please
help me understand why this happens, as I am having trouble visualising
why it doesnt work.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define CHUNK_SIZE 256

char *getline(void);
void writeline(const char *line);

int main(int argc, char **argv)
{
int nlines = 10, end = 0, start = 1;
int i, cnt = 0;
char **linev, *line;

/* find number of lines required */
if (argc > 1 && *argv[1] == '-')
nlines = strtol(argv[1]+1, NULL, 10);

/* allocate space for nlines pointers */
if ((linev = malloc(sizeof (char *) * nlines)) == NULL)
return 1;

/* put each line into the last element of linev */
while ((line = getline()) != NULL) {
linev[end = (end + 1 == nlines) ? 0 : (end + 1)] = line;

if (++cnt >= nlines) {
/* this is where i would like to free(linev[start]); */
start = (start + 1 == nlines) ? 0 : (start + 1);
}
}

/* EOF received, print lines */

/* for nlines, or number of lines received if < nlines */
for (i = (cnt < nlines) ? cnt : nlines; i; i--) {
writeline(linev[start]);
free(linev[start]);
start = (start + 1 == nlines) ? 0 : (start + 1);
}

free (linev);

return 0;
}

char *getline(void)
{
char *line;
int c, n = 0;

if ((line = (char *) malloc(CHUNK_SIZE)) == NULL)
return line;

while ((c = getchar()) != EOF) {
if ((line[n++] = c) == '\n')
break;
if (CHUNK_SIZE - (n % CHUNK_SIZE) <= 2)
if ((line = (char *) realloc(line, ((n / CHUNK_SIZE + 1) *
CHUNK_SIZE) * sizeof (char))) == NULL)
return line;
}

line[n+1] = '\0';

if (n == 0)
free(line);

return (n) ? line : NULL;
}

void writeline(const char *line)
{
int i = 0;

for (i = strlen(line); i > 0; i--)
putchar (*(line++));
}

Nov 15 '05 #1
7 2125
sl****************@hotmail.com wrote:
Hello, I'm working on the exercises from k&r, exercise 5-13 is to
implement a simple replacement for the unix tail command. The brief
says it should be able to cope no matter how unreasonable the input and
should make best use of availbale storage (I suppose it means dont just
declare a big char array).

I have made a solution that seems to be basically working (tail -1
doesnt work, I'll try to fix that later), but I'm having trouble
free'ing allocated memory, if i add the call to free() where i thought
it should go my c library kills it (gnu c library) as it detects an
error.

If I just dont free the memory, it works, but I suppose that is bad
form.

I have added a comment where I thought I should be using free(), please
help me understand why this happens, as I am having trouble visualising
why it doesnt work.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define CHUNK_SIZE 256

char *getline(void);
When I see this, I instantly wonder who owns the memory afterwards. Is it
some static/global string or is it perhaps allocated with malloc() and the
caller must free it?
void writeline(const char *line);

int main(int argc, char **argv)
{
int nlines = 10, end = 0, start = 1;
int i, cnt = 0;
char **linev, *line;

/* find number of lines required */
if (argc > 1 && *argv[1] == '-')
nlines = strtol(argv[1]+1, NULL, 10);
This lacks error checking. Also, in order to tail a file called '-10' it is
customary to use 'tail -- -10', i.e. use '--' to separate the arguments
from the options.
/* allocate space for nlines pointers */
if ((linev = malloc(sizeof (char *) * nlines)) == NULL)
return 1;

/* put each line into the last element of linev */
while ((line = getline()) != NULL) {
linev[end = (end + 1 == nlines) ? 0 : (end + 1)] = line;
IMHO too many expressions in this single line, I'd split it up for clarity:
linev[end] = line; // insert new line
end = (end+1)%nlines; // increment or wraparound (modulo operation!)
if (++cnt >= nlines) {
/* this is where i would like to free(linev[start]); */
start = (start + 1 == nlines) ? 0 : (start + 1);
}
Hmmm. I'd do it differently. Firstly, init linev with zeros. Then, before
entering an element into it, see if the place is already occupied and if
so, free() the former occupant. Also, while printing, check if an element
is occupied in the same way, that way you even eliminate the 'cnt'
fill-counter.
/* for nlines, or number of lines received if < nlines */
for (i = (cnt < nlines) ? cnt : nlines; i; i--) {
writeline(linev[start]);
free(linev[start]);
start = (start + 1 == nlines) ? 0 : (start + 1);
}

free (linev);
This last line is futile, just as releasing the lines after printing them
in the loop - some leak detectors might complain, but the program is
terminating so there's no need to free stuff, the OS will reclaim it
automatically.
char *getline(void)
{
char *line;
int c, n = 0;
if ((line = (char *) malloc(CHUNK_SIZE)) == NULL)
return line;
Oh, nono - never cast the returnvalue of malloc()(or realloc(), free(),
calloc() etc), it only serves to hide errors.
while ((c = getchar()) != EOF) {
if ((line[n++] = c) == '\n')
break;
if (CHUNK_SIZE - (n % CHUNK_SIZE) <= 2)
if ((line = (char *) realloc(line, ((n / CHUNK_SIZE + 1) *
CHUNK_SIZE) * sizeof (char))) == NULL)
return line;
It could be that it is the linebreaking newsreader, but I consider this
utterly unreadable. Remove the cast (see above) and the 'sizeof (char)'
which - by definition - is 1. Also, I'd have made CHUNK_SIZE a constant
inside that function, as it is only used there.
if (n == 0)
free(line);

return (n) ? line : NULL;


Hmm, obfuscating. You twice check for n==0 or n!=0 and act thereupon. Why
not a single one:
if(n==0)
{
free(line);
return NULL;
}
return line;

Uli

Nov 15 '05 #2
sl****************@hotmail.com wrote:
Hello, I'm working on the exercises from k&r, exercise 5-13 is to
implement a simple replacement for the unix tail command. The brief
says it should be able to cope no matter how unreasonable the input and
should make best use of availbale storage (I suppose it means dont just
declare a big char array).

I have made a solution that seems to be basically working (tail -1
doesnt work, I'll try to fix that later), but I'm having trouble
free'ing allocated memory, if i add the call to free() where i thought
it should go my c library kills it (gnu c library) as it detects an
error.

With all due regard to BWK and the New Testament, tail is perhaps better
implemented another way:

Usage: tail [-n] <text>

Print the last n lines of <text> to stdout. Assume n defaults to 5.

It is not necessary to read lines at all. We read <text> with fgetc()
looking for '\n' and with ftell() determine the position in the file of
the next line until EOF.

Store the ftell() returns in an array n+1 of long configured as a ring.
At EOF, determine where EOF-n is, fseek() it and then fputc() until EOF
again.

--
Joe Wright
"Everything should be made as simple as possible, but not simpler."
--- Albert Einstein ---
Nov 15 '05 #3
In article <Zf********************@comcast.com>,
Joe Wright <jw*****@comcast.net> wrote:
With all due regard to BWK and the New Testament, tail is perhaps better
implemented another way: Store the ftell() returns in an array n+1 of long configured as a ring.
At EOF, determine where EOF-n is, fseek() it and then fputc() until EOF
again.


That presumes that the input is seekable. Regular files are normally
seekable, but interactive terminals (or pipes or sockets) rarely are.
--
"Who Leads?" / "The men who must... driven men, compelled men."
"Freak men."
"You're all freaks, sir. But you always have been freaks.
Life is a freak. That's its hope and glory." -- Alfred Bester, TSMD
Nov 15 '05 #4
Walter Roberson wrote:
In article <Zf********************@comcast.com>,
Joe Wright <jw*****@comcast.net> wrote:
With all due regard to BWK and the New Testament, tail is perhaps better
implemented another way:


Store the ftell() returns in an array n+1 of long configured as a ring.
At EOF, determine where EOF-n is, fseek() it and then fputc() until EOF
again.

That presumes that the input is seekable. Regular files are normally
seekable, but interactive terminals (or pipes or sockets) rarely are.


Thank you Walter. We don't usually concern ourselves here with pipes or
sockets. The C language has no such concepts. Do you suppose 'head' and
'tail' are meant to deal with other than files?

--
Joe Wright
"Everything should be made as simple as possible, but not simpler."
--- Albert Einstein ---
Nov 15 '05 #5
Joe Wright wrote:
Walter Roberson wrote:
Joe Wright <jw*****@comcast.net> wrote:
Store the ftell() returns in an array n+1 of long configured as a
ring. At EOF, determine where EOF-n is, fseek() it and then fputc()
until EOF again.
That presumes that the input is seekable. Regular files are normally
seekable, but interactive terminals (or pipes or sockets) rarely are.


Thank you Walter. We don't usually concern ourselves here with pipes or
sockets. The C language has no such concepts


That's true, but we usually don't concern ourselves with writing "head"
or "tail" commands, either. It all gets murky if you start talking about
implementing Unix tools but not implementing them 'properly'.
Do you suppose 'head' and
'tail' are meant to deal with other than files?


Absolutely, I use head and tail in pipe-lines all the time.

--
Simon.
Nov 15 '05 #6
Joe Wright <jw*****@comcast.net> wrote:
Walter Roberson wrote:
In article <Zf********************@comcast.com>,
Joe Wright <jw*****@comcast.net> wrote:
With all due regard to BWK and the New Testament, tail is perhaps better
implemented another way:
Store the ftell() returns in an array n+1 of long configured as a ring.
At EOF, determine where EOF-n is, fseek() it and then fputc() until EOF
again.


That presumes that the input is seekable. Regular files are normally
seekable, but interactive terminals (or pipes or sockets) rarely are.


Thank you Walter. We don't usually concern ourselves here with pipes or
sockets. The C language has no such concepts.


No, but we do concern ourselves with streams on which fseek() may fail;
a contingency for which the C Standard definitely does have a provision.
Do you suppose 'head' and 'tail' are meant to deal with other than files?


Definitely yes; I make use of this regularly, in fact more often than I
use it on a normal file.

Richard
Nov 15 '05 #7
sl****************@hotmail.com wrote:

Hello, I'm working on the exercises from k&r, exercise 5-13 is to
implement a simple replacement for the unix tail command. The brief
says it should be able to cope no matter how unreasonable the input and
should make best use of availbale storage (I suppose it means dont just
declare a big char array).

I have made a solution that seems to be basically working (tail -1
doesnt work, I'll try to fix that later), but I'm having trouble
free'ing allocated memory, if i add the call to free() where i thought
it should go my c library kills it (gnu c library) as it detects an
error.

If I just dont free the memory, it works, but I suppose that is bad
form.

I have added a comment where I thought I should be using free(), please
help me understand why this happens, as I am having trouble visualising
why it doesnt work.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define CHUNK_SIZE 256

char *getline(void);
void writeline(const char *line);

int main(int argc, char **argv)
{
int nlines = 10, end = 0, start = 1;
int i, cnt = 0;
char **linev, *line;

/* find number of lines required */
if (argc > 1 && *argv[1] == '-')
nlines = strtol(argv[1]+1, NULL, 10);

/* allocate space for nlines pointers */
if ((linev = malloc(sizeof (char *) * nlines)) == NULL)
return 1;

/* put each line into the last element of linev */
while ((line = getline()) != NULL) {
linev[end = (end + 1 == nlines) ? 0 : (end + 1)] = line;

if (++cnt >= nlines) {
/* this is where i would like to free(linev[start]); */
Here you would be freeing a line which you still want to output later.
For example nlines is 2, cnt is 2, end is 0, start is 1, stdin has 2
lines: you would free linev[1] and below call writeline with linev[1].
Suggestion: Drop the variable 'start' and free linev[end] at the
beginning of the loop after you incremented 'end' and before you assign
a /new/ line to it.
start = (start + 1 == nlines) ? 0 : (start + 1);
}
}

/* EOF received, print lines */

/* for nlines, or number of lines received if < nlines */
for (i = (cnt < nlines) ? cnt : nlines; i; i--) {
writeline(linev[start]);
free(linev[start]);
start = (start + 1 == nlines) ? 0 : (start + 1);
}

free (linev);

return 0;
}

char *getline(void)
{
char *line;
int c, n = 0;

if ((line = (char *) malloc(CHUNK_SIZE)) == NULL)
return line;

while ((c = getchar()) != EOF) {
if ((line[n++] = c) == '\n')
break;
if (CHUNK_SIZE - (n % CHUNK_SIZE) <= 2)
if ((line = (char *) realloc(line, ((n / CHUNK_SIZE + 1) *
CHUNK_SIZE) * sizeof (char))) == NULL)
return line;
}

line[n+1] = '\0';

if (n == 0)
free(line);

return (n) ? line : NULL;
}

void writeline(const char *line)
{
int i = 0;

for (i = strlen(line); i > 0; i--)
putchar (*(line++));
}

Nov 15 '05 #8

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

13
by: Martijn van den Branden | last post by:
Hi, sorry if this message gets posted twice, i seem to have some problem posting to newsgroups. i've rewritten some loop in matlab using the mex interface method in c, and i've achieved a...
31
by: Bjørn Augestad | last post by:
Below is a program which converts a double to an integer in two different ways, giving me two different values for the int. The basic expression is 1.0 / (1.0 * 365.0) which should be 365, but one...
4
by: JS | last post by:
I have a file called test.c. There I create a pointer to a pcb struct: struct pcb {   void *(*start_routine) (void *);   void *arg;   jmp_buf state;   int    stack; }; ...
1
by: khaleel.alyasini | last post by:
anyone could point me out where did i do wrong? it seems that i can't get back the original Lena image after the IDCT(inverse discrete cosine transform) process. the output raw image is nothing...
9
by: edware | last post by:
I read a document about c++ programming and memory allocation, and I came across a new term I've never heard before, double free. I tried googling for it but I found no explanation, can someone...
4
by: Alan | last post by:
I`m having trouble figuring out the correct syntax for accessing an element of a struct variable (t_list) passed by reference to a function. The compiler does not like the code below when I added...
1
by: yucikala | last post by:
Hello, I'm a "expert of beginner" in C#. I have a dll - in C. And in this dll is this struct: typedef struct msg_s { /* please make duplicates of strings before next call to emi_read() ! */ ...
6
by: amarapreet | last post by:
hi gcc 3.4 says in a warning that %lf is not recognized as a scanf format specifier is this right if so how to read in a double using scanf thanks
9
Steel546
by: Steel546 | last post by:
This program is used to calculate GPA. I'm having trouble actually getting an output. Alright, I KNOW that I don't have an output statement, but I don't know where to put it... heh. Netbeans keeps...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.