I was following one of the exercises in the book "The C Programming Language":
http://users.powernet.co.uk/eton/kandr2/krx116.html
In the below program, if the input file contains the line 'h\n', for example, the getline function initializes i and j to 0. Since 'h' is not EOF or '\n', the block in loop executes, the c is assigned to the 0 index of the s array pointer, since j is 0. Then j increments to 1. Then the block ends and the i counter increments to 1. And the loop checks if the next character is a '\n', and it is, so the block exits. At this point, i and j are both equal to 1. Since c is equal to '\n', c is inserted into index 1 of the s pointer, since j is 1. Then j is incremented to 2. Then i is incremented to 2. Then the null terminator '\0' is inserted into index 2 of the s pointer, since j is 2. Then the function returns 2, since i is also 2.
I see no point of the j variable. Because when it increments, so does i. Someone said I was wrong:
"because they're incremented at different rates. The i counter is used to keep track of the length of the string. The j variable, at the end of the processing, stores the point where the null terminator needs to go in the string."
But I say since i and j are the same values (one is just incremented after the other), you could use i to the point where the null terminator needs to go, since i would also be equal to 2 in my above example.
#include <stdio.h>
#define MAXLINE 1000 /* maximum input line size */
int getline(char line[], int maxline);
void copy(char to[], char from[]);
/* print longest input line */
int main(void)
{
int len; /* current line length */
int max; /* maximum length seen so far */
char line[MAXLINE]; /* current input line */
char longest[MAXLINE]; /* longest line saved here */
max = 0;
while((len = getline(line, MAXLINE)) > 0)
{
printf("%d: %s", len, line);
if(len > max)
{
max = len;
copy(longest, line);
}
}
if(max > 0)
{
printf("Longest is %d characters:\n%s", max, longest);
}
printf("\n");
return 0;
}
/* getline: read a line into s, return length */
int getline(char s[], int lim)
{
int c, i, j;
for(i = 0, j = 0; (c = getchar())!=EOF && c != '\n'; ++i)
{
if(i < lim - 1)
{
s[j++] = c;
}
}
if(c == '\n')
{
if(i <= lim - 1)
{
s[j++] = c;
}
++i;
}
s[j] = '\0';
return i;
}
/* copy: copy 'from' into 'to'; assume 'to' is big enough */
void copy(char to[], char from[])
{
int i;
i = 0;
while((to[i] = from[i]) != '\0')
{
++i;
}
}
Am I right or wrong?
You're not quite right. If the input string is longer than the limit, the loop will continue incrementing i in order to count input characters even though they're not going into the result string.
Related
This code reads an input text file, and creates an output file based on its contents.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define OUT 0
#define IN 1
#define MAX 28
#define BLOCK 4000
/* Check whether the character is alphanumeric */
int isAlphanumeric(char c) {
return ('a' <= c && c <= 'z') ||
('A' <= c && c <= 'Z') ||
('0' <= c && c <= '9');
}
int main(int argc, char *argv[]) {
int c, state = OUT, length = 0, i, j, counter[MAX];
char word[30], longest_word[30];
FILE *input, *output; /* FILE pointers to open the file */
/* Initialize the counter */
for (i = state; i < MAX; i++)
counter[i] = 0;
/* Open the file */
input = fopen("complete_shakespeare.txt", "r");
output = fopen("word_length_histogram.txt", "w");
/* Keep reading the character in the file */
while ((c = getc(input)) != EOF) {
/* If the character is alphanumeric, record it */
if (isAlphanumeric(c)) {
strncat(word, &c, 1);
}
/* If the character is not alphanumeric, increment the corresponding counter, and additionally, record longest word. */
else {
length = strlen(word);
if (length == 27) strcpy(longest_word, word);
counter[length] += 1;
memset(word, 0, sizeof(word));
}
}
/* If the file ends with a word, record its length */
if (isAlphanumeric(word[0])){
length = strlen(word);
counter[length] += 1;
}
/* print the longest word to the file */
fprintf(output, "%s\n\n", longest_word);
/* Make the histogram */
for (i = 1; i < MAX; i++) {
int dividend = counter[i] / 4000 + 1;
fprintf(output, "%2d %6d ", i, counter[i]);
for (j = dividend; j >= 1; j--){
if (counter[i] != 0)
fprintf(output, "*");
}
fprintf(output, "\n");
}
/* Don't forget to close the FILEs */
fclose(input);
fclose(output);
return 0;
}
It produces the correct output file, but this error comes up whenever I compile it.
B:\CodeBlocks\Projects\Programming in C\hw_4\Homework_4\main.c|44|warning: passing argument 2 of 'strncat' from incompatible pointer type [-Wincompatible-pointer-types]|
The warning seems to come from the only line with strncat. Does anyone know how this can be remedied?
The variable c is declared as having the type int.
int c, state = OUT, length = 0, i, j, counter[MAX];
^^^^^^
So the expression &c used in this call
strncat(word, &c, 1);
has the type int * instead of the type char *.
There is no sense to call strncat for one character. Moreover the array word has indeterminate values because it was not initialized.
char word[30], longest_word[30];
You could write
char word[30], longest_word[30];
word[0] = '\0';
And then something like the following
size_t n = 0;
while ((c = getc(input)) != EOF) {
/* If the character is alphanumeric, record it */
if (isAlphanumeric(c)) {
word[n] = ( char )c;
word[++n] = '\0';
}
/* If the character is not alphanumeric, increment the corresponding counter, and additionally, record longest word. */
else {
if (n == 27) strcpy(longest_word, word);
counter[n] += 1;
n = 0;
word[n] = '\0';
}
}
That is, the variable n will keep track of the current length of the string stored in the array word.
I'm writing code to find the longest row in the input stream and print it out. However, after I defined an int called max_count = 0, I always found an overflow, which displayed max_count as 1633771873. I've initialized that variable, so I don't know where the problem is. You probably do not need to figure out all of the functions, but each of them has its comment.
Here is my code:
#include <stdio.h>
#define DEFAULT 10
int getline(char line[], int limit);
void copy(char from[], char to[]);
int enlarge(int lim, char s[]);
main()
{
int i;
int max_count = 0;
char line[DEFAULT];
char maxline[DEFAULT];
while ((i = getline(line, DEFAULT)) != 0) {
if (i > max_count) { // where weird thing happend (max_count=1633771873)
max_count = i;
copy(line, maxline);
}
}
if (max_count > 0) {
printf("maxline: %s", maxline);
} else {
printf("No maxline");
}
return 0;
}
/*get a row from input stream and return its length*/
int getline(char s[], int lim)
{
int i, c;
for (i = 0; ((c = getchar()) != EOF) && (c != '\n'); ++i) {
if (i == lim - 1) {
lim = enlarge(lim, s);
}
s[i] = c;
}
if (c == '\n') {
s[i] = c;
++i;
}
if (i == lim) {
enlarge(lim, s);
}
s[i] = '\0';
return i;
}
/*copy an array to another */
void copy(char from[], char to[])
{
int i = 0;
while (from[i] != '\0') {
to[i] = from[i];
++i;
}
}
/*expand an array twice as its capacity*/
int enlarge(int lim, char s[])
{
s[lim - 1] = '\0';
lim *= 2;
char temp[lim];
copy(s, temp);
s = temp;
return lim;
}
This is the console window:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
^Z
maxline:
--------------------------------
Process exited after 15.19 seconds with return value 3221225477
You have a buffer with space for 10 characters:
#define DEFAULT 10
char line[DEFAULT];
You enter 37 characters including a newline:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Your getline function tries to store them all in line (by the way, enlarge doesn't do anything useful).
The first 10 characters fit into line. The other 27 characters and the terminating '\0' overwrite other random variables which come after line in memory.
That is why max_count holds the ASCII codes for aaaa.
Your enlarge function isn't doing what you think it is.
int enlarge(int lim, char s[])
{
s[lim - 1] = '\0';
lim *= 2;
char temp[lim];
copy(s, temp);
s = temp;
return lim;
}
You're creating a new array temp within the scope of the function. You then copy the address of the start of the array to s. Since s is a parameter to the function, modifying s won't be reflected in the calling function. So after this function returns s in getline is unchanged.
Even if you were to fix this by either returning a char * or changing the function to accept a char ** and assigning temp to the dereferenced pointer, you would be returning the address of a variable local to enlarge. That variable goes out of scope when the function returns and so the pointer would be invalid.
The only way you can change the size of an array is if you first allocate it dynamically with malloc and then later use realloc to change its size.
Also, getline is the name of a function on POSIX systems. You should change the name to something else.
What exactly breaks the while loop in this code? This is code from the book The C Programming Language from the C creators. It is a code from section 1.9. I guess int len will always be bigger than 0, but somehow when I compile this code the while loop breaks when I press Ctrl+Z (which is EOF for Windows).
#include <stdio.h>
#define MAXLINE 1000 /* maximum input line length */
int mgetline(char line[], int maxline);
void copy(char to[], char from[]);
/* print the longest input line */
main() {
int len; /* current line length */
int max; /* maximum length seen so far */
char line[MAXLINE]; /* current input line */
char longest[MAXLINE]; /* longest line saved here */
max = 0;
while ((len = mgetline(line, MAXLINE)) > 0)
if (len > max) {
max = len;
if (max == len)
copy(longest, line);
}
if (max > 0) /* there was a line */
printf("%s", longest);
return 0;
}
/* mgetline: read a line into s, return length */
int mgetline(char s[], int lim) {
int c, i;
for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
s[i] = c;
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
/* copy: copy 'from' into 'to'; assume to is big enough */
void copy(char to[], char from[]) {
int i;
i = 0;
while ((to[i] = from[i]) != '\0')
++i;
}
Loop 1: (In copy)
Strings in C are NUL-terminated by convention. NUL is a special char value with the value 0.
The value of the expression to[i] = from[i] is the new value of to[i].
That is 0 when NUL is reached, and the loop exits.
Loop 2: (In main)
Similarly the value of len = mgetline(line, MAXLINE) is the new value of len. That is 0 if mgetline returns 0 which is does when no characters are read. So that loop exits.
I am trying to run the maximum line length program as shown in chapter one of "The c programming language" and the output is always a series of question mark boxes like ⍰.
Here is the code:
#include <stdio.h>
#define MAXLINE 1000 /* maximum input line size */
int getline(char line[], int maxline);
void copy(char to[], char from[]);
/* print longest input line */
int main()
{
int len; /* current line length */
int max; /* maximum length seen so far */
char line[MAXLINE]; /* current input line */
char longest[MAXLINE]; /* longest line saved here */
max =0;
while((len = getline(line, MAXLINE)) > 0)
if (len > max) {
max = len;
copy(longest, line);
}
if (max > 0) /* there was a line */
printf("%s", longest);
return 0;
}
/* getline: read a line into s, return length */
int getline(char s[], int lim)
{
int c, i;
for (i = 0; i < lim - 1 && (c = getchar() != EOF) && c != '\n'; ++i)
s[i] = c;
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
/* copy: copy 'from' into 'to'; assume to is big enough */
void copy(char to[], char from[])
{
int i;
i = 0;
while ((to[i] = from[i]) != '\0')
++i;
}
Is this a technical issue or something obviously wrong with the code?
Thank-you?
You have a tiny, tiny typo. This line in the getline function:
for (i = 0; i < lim - 1 && (c = getchar() != EOF) && c != '\n'; ++i)
should be
for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
The effect of this typo is that you call getchar(), test the returned value to see if it's equal to EOF or not, and assign the 1/0 (true/false) value to the variable c. But what you want to do is assign the returned value from getchar() to c, and then test to see if it's equal to EOF.
So if you type, say, "test", your typo-afflicted getline function was not writing the characters t e s t into line; instead it was writing four 1 values (that is, the character '\001', which is a control-A if you want to think about it that way), and since that's not a printing character, your display system printed it as a little box instead.
I am just simply trying to copy entered line and display it on the screen But still, output includes some additional term.... What is wrong?
#include<stdio.h>
#define MAXLINE 1000
void copy(char to[], char from[]);
main()
{
int length;
int limit;
char saved[MAXLINE];
char len[MAXLINE];
copy(saved,len);
printf("%s", saved);
}
void copy(char to[],char from[])
{
int a,i,c;
i = 0;
a = 0;
while((c = getchar()) != EOF)
{
from[i] = c;
++i;
}
while((to[a] = from[a]) != EOF)
++a;
}
The call to printf() is expecting a null-terminated string, yet the copy() function is not providing one.
Change the first loop in copy() so that the array index i is checked, to avoid buffer overflow, and then add a null-terminator after the loop terminates:
// check array index
while(i < MAXLINE-1 && (c = getchar()) != EOF)
{
from[i] = c;
++i;
}
// add null-terminator to from[]
from[i] = '\0';
Then change the second loop so that it terminates when the null-terminator is encountered, and add a \0 character to the end of to[]:
// change loop termination condition
while((to[a] = from[a]) != '\0')
++a;
// add null-terminator to to[]
to[a] = '\0';
Better, use the saved value of i to terminate the loop, and copy the \0 from from[] to to[]:
// better, use i to terminate loop
for (a = 0; a <= i; a++)
to[a] = from[a];
In my first version of the above loop, I inadvertently used for (a = 0; a < i; a++) {}, caught by #alk. This loop fails to copy the final \0 character, since when a == i, the loop terminates without executing the body. The quick fix above, changing a < i to a <= i works, but the loop is no longer idiomatic (hence my initial trouble; I write a < i in loops by reflex). A possibly better solution would be to increment i after the \0 character is stored in from[], just as has been done for every other character in from[]. This is illustrated in the final code below.
Additionally, note that the function signature for main() should be int main(void), and since copy() is declared as returning void, there should be no value returned at the end of the function. And, to be truly correct, the types of array indices should be size_t, which is an unsigned integer type guaranteed to be able to hold any array index.
#include <stdio.h>
#define MAXLINE 1000
void copy(char to[], char from[]);
int main(void)
{
// int length;
// int limit;
char saved[MAXLINE];
char len[MAXLINE];
copy(saved,len);
printf("%s", saved);
return 0;
}
void copy(char to[],char from[])
{
size_t a,i;
int c;
i = 0;
a = 0;
// check array index
while(i < MAXLINE-1 && (c = getchar()) != EOF)
{
from[i] = c;
++i;
}
// add null-terminator to from[], increment i
from[i] = '\0';
++i;
// use i to terminate copy loop
for (a = 0; a < i; a++)
to[a] = from[a];
}