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++));
} 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 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 ---
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
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 ---
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.
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 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++)); } This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics
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...
|
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...
|
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;
};
...
|
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...
|
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...
|
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...
|
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() ! */
...
|
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
|
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...
|
by: Charles Arthur |
last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
|
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...
|
by: nemocccc |
last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
|
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...
|
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...
|
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,...
|
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...
|
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...
|
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...
| |