I am in the process of learning C, and I stumbled upon a bug, and I can't seem to get my head around it.
This code is supposed to accept multiple lines of text (followed by an Enter), compare the lines to each other, stop comparing upon hitting EOF (aka. crtl - Z), choose the longest string and print it out to the console.
It does that, but upon printing the text out, it sometimes adds some additional characters to the output.
This is the code:
int get_line(char s[]);
void copy(char from[], char to[]);
main()
{
int len;
char line[1000]; //the array which is used to compare
char longest[1000]; //the array in which the longest line will be stored
int max = 1; //the maximum length of a line
while ((len = get_line(line)) > 0) //checks if the length of a line in more than 0
if (len > max)
{
max = len;
copy(line, longest);
}
if (max > 0)
printf("%s", longest); //prints out the longest line, if it exists
}
int get_line(char s[])
{
int c, i;
for (i = 0; (c = getchar()) != EOF && c != '\n'; ++i) //reads the line until ^Z of Enter
s[i] = c;
return (i);
}
copy(char from[], char to[])
{
for (int i = 0; from[i] != '\n'; ++i)
to[i] = from [i];
}
In C a String is an Array of chars which is terminated with \0, this is called a null terminated String.
So when calling printf("%s",longest), it will print each element in the longest[] array which has length 1000.
To fix this you have to set the length of your char Array to the length of the actual String plus 1, and set longest[length +1] = '\0'.
This will tell the printf() function that the String ends at length + 1.
Related
I do this program which receives input from a string and a substring, and then searches for the substring within the string by determining how often it appears (the number of occurrences) and the locations it is located, then these positions are inserted into an array for example (4 5 8) And they are printed correctly, now what I was trying to do, once I got my array with inside the locations where the substring was found it print it in reverse ie (8 5 4) I tried using this cycle
// reverse output
printf ("%d", count);
for (j = count - 1; j >= 0; j--)
printf("%d", pos[j]);
But if the array positions are 8 5 4 so it prints to me
5 ,4, -311228772
Why does this happen? Here is the code:
// inclusion of libraries
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
Reads a string allocated by the stream.
It stops at newline, not included in string.
Returns NULL to EOF
*/
char *my_getline(FILE *stream) { // statement of function
char *line = NULL; // this is just the pointer initialization
size_t pos = 0; // definition of position variables and init
int c; // a variable to store the temporary character
while ((c = getc(stream)) != EOF) // read every character until the end of the file
{
char *newp = realloc(line, pos + 2); // To dynamically allocate memory, with reference to the number of characters and more '2' is only to compensate for the null character and the character (since it is 0)
if (newp == NULL) { // checks whether memory has been properly associated or not.
free(line); // if the line is not free the blank
return NULL; // interrupts the program and returns NULL
}
line = newp; // if memory is allocated correctly stores the memory allocated to the line pointer
if (c == '\n') // if a new line is detected
break; // interrupts the while cycle
line[pos++] = (char)c; // stores the character in dynamic memory and the new character in the new location.
}
if (line) { // if the line contains something then a null character is added at the end to complete that string.
line[pos] = '\0';
}
return line; // returns the contents of the line.
}
int main(void) { // main statement
char *str, *sub; // character punctuation statement
size_t len1, len2, i, count = 0; // unsigned value statement "size_t is equal to unsigned int" so may also be <0
int pos[count]; // declare a count array to insert the index then print it in reverse
int j;
// Here is the main string
printf("Enter Main String: \n"); // print the entry and enter the main string
str = my_getline(stdin); // inserts the entered string inside the pointer using my_getline function and using getchar analogue stdin to make the entered characters input from the standard input
// here is the substring to look for
printf("Enter substring to search: \ n"); // print the entry and enter the main substring
sub = my_getline(stdin); // inserts the entered string inside the pointer using my_getline function and using getchar analogue stdin to make the entered characters input from the standard input
if (str && sub) { // if string and substring && = and
len1 = strlen(str); // inserts the string length in the len1 variable
len2 = strlen(sub); // inserts the length of the string in the len2 variable
for (i = 0; i + len2 <= len1; i++) { // loop for with the control that the substring is less than or equal to the main string ie len2 <= len1
if (! memcmp(str + i, sub, len2)) { // here uses the memcmp function to compare the string and substring byte bytes
count++; // count variable that is incremented each time the sub is found in p
// here is where it gets in output
// If the substring was found mold the index with the locations it was found
pos[count] = i + 1;
printf( "%d\n", pos[count]);
}
}
// print to get reverse output
printf("number of times%d", count);
// print to get reverse output
printf("%d", count);
for (j = count - 1; j >= 0; j--)
printf("%d", pos[j]);
if (count == 0) { // if count is = 0 ie the substring was not found string string not found
// otherwise if not found
printf("Subtry not found \n");
}
}
// free releases the memory area that was reserved for the string and substrings so that it can be reused in the next run
free(str);
free(sub);
return 0; // exit analog
}
Your code is completely unreadable. Even reformatted and spaced out, the comments make it difficult to see the important stuff.
You should only comment the non obvious: int main(void) {// main statement is a good example of a useless counter productive comment.
After removing all comments, the code shows a few problems:
There is an extra space in printf("Enter substring to search: \ n");
The array pos is defined with a size of 0: int count = 0; int pos[count];. The program has undefined behavior.
count is incremented before storing the offset into the array. Hence the array contents does not start at index 0, hence producing incorrect output when you iterate from count-1 down to 0 in the second loop.
Here is a simplified and corrected version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
Reads a string from the stream allocated with malloc
stops at newline, not included in string.
Returns NULL at EOF
*/
char *my_getline(FILE *stream) {
char *line = NULL;
size_t pos = 0;
int c;
while ((c = getc(stream)) != EOF) {
char *newp = realloc(line, pos + 2);
if (newp == NULL) {
free(line);
return NULL;
}
line = newp;
if (c == '\n')
break;
line[pos++] = (char)c;
}
if (line) {
line[pos] = '\0';
}
return line;
}
int main(void) {
printf("Enter Main String:\n");
char *str = my_getline(stdin);
printf("Enter substring to search:\n");
char *sub = my_getline(stdin);
if (str && sub) {
size_t count = 0;
size_t len1 = strlen(str);
size_t len2 = strlen(sub);
size_t pos[len1 + 1];
for (size_t i = 0; i + len2 <= len1; i++) {
if (!memcmp(str + i, sub, len2)) {
pos[count] = i + 1;
printf("%d\n", (int)pos[count]);
count++;
}
}
if (count != 0) {
printf("number of times: %d\n", (int)count);
for (size_t j = count; j-- > 0;) {
printf(" %d", (int)pos[j]);
}
printf("\n");
} else {
printf("substring not found.\n");
}
}
free(str);
free(sub);
return 0;
}
You declared pos as an array of length 0:
size_t ... count = 0;
int pos [count];
Thus, inside your for-loop you'll access some unitialized memory:
for (j = count-1; j>= 0; j--)
printf ("%d", pos [j]);
My question is concerning some code from section 1.9-character arrays in the Kernighan and Ritchie book. The code is as follows:
#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 */
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;
}
/* get line: 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;
}
My question is about the getline function. Now looking at this following output from my command line as a reference:
me#laptop
$ characterarray.exe
aaaaa
a
aaaaaa
^Z
aaaaaa
me#laptop
$
When I type in the first character which is 'a', does the character 'a' go through the for loop, in the getline function, and initialize s[0] - s[998] = a ? And my second part of the question is once the program leaves the for loop and goes to the
s[i] = '\0'
return i;
wouldn't it be initialized s[998] = '\0' and the return integer is 998? I've spent over an hour staring at this problem and I can't seem to grasp what is going on.
Character 'a' does not go through the for loop. Look here:
for (i=0; i<lim-1 && ((c=getchar()) != EOF) && c != '\n' ; ++i)
c = getchar() is where your program stops and waits for user input -- not just once, but in a loop! This is a condition of continuing the for loop, and each time it is checked, a getchar() is called. The loop goes only one char at a time. Each time a char is added to the current end of an array (indicated by i) and the loop breaks when a newline symbol is entered or when a limit size is reached. This is very compact code but it is kind of oldschool C coding which is not very readable these relaxed days. It can be broken into following parts:
i = 0;
while(i < lim - 1) {
c = getchar();
if (c == EOF || c == '\n') break;
s[i] = c;
++i;
}
s[i] = '\0';
return i;
(also I don't think a newline symbol is needed to be included in the string, so I omitted the
if (c == '\n') {
s[i] = c;
++i;
}
part.)
Here is how the input is working in the for-loop.
Before the for-loop, the array s is empty(ish. It's full of whatever garbage is in that memory). If you type in a single a and hit enter, the for-loop goes through 2 characters 'a' and '\n'. For 'a', the variable c becomes 'a' from getchar() in the for-loop parameters, and that gets saved in the i spot (which is 0) of s. So, the array s is now
s[0] = 'a' and the rest of s has random garbage in it.
Then c becomes '\n' from getchar(). This stops the for-loop because of the c != '\n' check. The if-statement has s[i], where i is 1, become '\n' and i bumps up to 2.
Now s is
s[0] = 'a'
s[1] = '\n'
getline finishes up by making s[2] be '\0', which is the end of string character, and returns i.
Your end result is
s[0] = 'a'
s[1] = '\n'
s[2] = '\0'
and i, your length, is 2.
Here's something that looks like a bug to me, however I am confused that my observation does not seem to pop up anywhere else on the internet, given the age and popularity of the book. Or maybe I am just bad at searching or it is not a bug at all.
I am talking about the "print out the longest input line" program from chapter one. Here's the code:
#include <stdio.h>
#define MAXLINE 1000 /* maximum input line length */
int getline(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 = 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;
}
Now, it seems to me that it should be lim-2 as opposed to lim-1 in the for condition of getline. Otherwise, when the input is exactly of maximum length, that is 999 characters followed by '\n', getline will index into s[MAXLINE], which is out of bounds, and also all kinds of horrible things might happen when copy is called and from[] does not end with a '\0'.
I think you're confused somewhere. This loop condition:
for (i=0; i < lim-1 && (c=getchar())!=EOF && c!='\n'; ++i)
Ensures that i is never greater than lim - 2, so in the maximum-length case, i is lim-1 after the loop exits and the null character is stored into that last position.
In the case of 999 non-\n characters followed by a \n, c never equals \n. When the for loop exits, c is equal to the last non-newline character.
So c != '\n' and doesn't enter the block that does i++ so i never goes out of bounds.
When the input is of length 999 and is followed by \n, value of limit is 1000 and the value of lim -1 will become 999 and the loop test condition i < lim -1 will become false when i becomes 998. For i < 999, c == \n never be true and hence array s will indexed to s[999] and not s[1000]
The tricky line is char longest[] array initialization. If I put it like it is (char longest[lim]), the string returned to me after EOF is longer than lim, so I assume it doesn't work this way. But if I explicitly put 10 there (char longest[10]) — it works as I expect and return only 10 first characters of line.
What makes the difference, in this exact case, between int lim = 10; char longest[lim] and char longest[10]
#include <stdio.h>
#define MAXLINE 1000 //maximum input line size
//#define LIM 10
int getline(char line[], int maxline);
void copy(char to[], char from[]);
//print longest input line
main()
{
int lim=10;
int len; //current line length
int max; //maximum length seen so far
char line[MAXLINE]; //current input line
char longest[lim]; //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("Length: %d\n", max);
printf("First %d characters of line: %s", lim, 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 bigh enough
void copy(char to[], char from[])
{
int i;
i = 0;
while ((to[i] = from[i]) != '\0')
{
++i;
}
}
type name[10] is Fixed array declaration.
type name[exp] is Variable-length array.
Allocation is performed every time you encounter this declaration (in block) is from C99 variable-length arrays. Size is fixed after it is once formed.
So, the behavior of the case, such as access to outside the bounds of the array in code like the example is undefined.
If you write a value that's too long to an array, you get "undefined behaviour". Anything can happen. You program might crash, or it might silently do the wrong thing, or it might even appear to work. Changing anything else in your program can change what happens.
This is also a security vulnerability - if an attacker can control the value that's written, they can probably make your program do anything they want.
Short version: Before you write a string to an array, you MUST make sure it'll fit.
I am writing C program that reads input from the standard input a line of characters.Then output the line of characters in reverse order.
it doesn't print reversed array, instead it prints the regular array.
Can anyone help me?
What am I doing wrong?
main()
{
int count;
int MAX_SIZE = 20;
char c;
char arr[MAX_SIZE];
char revArr[MAX_SIZE];
while(c != EOF)
{
count = 0;
c = getchar();
arr[count++] = c;
getReverse(revArr, arr);
printf("%s", revArr);
if (c == '\n')
{
printf("\n");
count = 0;
}
}
}
void getReverse(char dest[], char src[])
{
int i, j, n = sizeof(src);
for (i = n - 1, j = 0; i >= 0; i--)
{
j = 0;
dest[j] = src[i];
j++;
}
}
You have quite a few problems in there. The first is that there is no prototype in scope for getReverse() when you use it in main(). You should either provide a prototype or just move getReverse() to above main() so that main() knows about it.
The second is the fact that you're trying to reverse the string after every character being entered, and that your input method is not quite right (it checks an indeterminate c before ever getting a character). It would be better as something like this:
count = 0;
c = getchar();
while (c != EOF) {
arr[count++] = c;
c = getchar();
}
arr[count] = '\0';
That will get you a proper C string albeit one with a newline on the end, and even possibly a multi-line string, which doesn't match your specs ("reads input from the standard input a line of characters"). If you want a newline or file-end to terminate input, you can use this instead:
count = 0;
c = getchar();
while ((c != '\n') && (c != EOF)) {
arr[count++] = c;
c = getchar();
}
arr[count] = '\0';
And, on top of that, c should actually be an int, not a char, because it has to be able to store every possible character plus the EOF marker.
Your getReverse() function also has problems, mainly due to the fact it's not putting an end-string marker at the end of the array but also because it uses the wrong size (sizeof rather than strlen) and because it appears to re-initialise j every time through the loop. In any case, it can be greatly simplified:
void getReverse (char *dest, char *src) {
int i = strlen(src) - 1, j = 0;
while (i >= 0) {
dest[j] = src[i];
j++;
i--;
}
dest[j] = '\0';
}
or, once you're a proficient coder:
void getReverse (char *dest, char *src) {
int i = strlen(src) - 1, j = 0;
while (i >= 0)
dest[j++] = src[i--];
dest[j] = '\0';
}
If you need a main program which gives you reversed characters for each line, you can do that with something like this:
int main (void) {
int count;
int MAX_SIZE = 20;
int c;
char arr[MAX_SIZE];
char revArr[MAX_SIZE];
c = getchar();
count = 0;
while(c != EOF) {
if (c != '\n') {
arr[count++] = c;
c = getchar();
continue;
}
arr[count] = '\0';
getReverse(revArr, arr);
printf("'%s' => '%s'\n", arr, revArr);
count = 0;
c = getchar();
}
return 0;
}
which, on a sample run, shows:
pax> ./testprog
hello
'hello' => 'olleh'
goodbye
'goodbye' => 'eybdoog'
a man a plan a canal panama
'a man a plan a canal panama' => 'amanap lanac a nalp a nam a'
Your 'count' variable goes to 0 every time the while loop runs.
Count is initialised to 0 everytime the loop is entered
you are sending the array with each character for reversal which is not a very bright thing to do but won't create problems. Rather, first store all the characters in the array and send it once to the getreverse function after the array is complete.
sizeof(src) will not give the number of characters. How about you send i after the loop was terminated in main as a parameter too. Ofcourse there are many ways and various function but since it seems like you are in the initial stages, you can try up strlen and other such functions.
you have initialised j to 0 in the for loop but again, specifying it INSIDE the loop will initialise the value everytime its run from the top hence j ends up not incrmenting. So remore the j=0 and i=0 from INSIDE the loop since you only need to get it initialised once.
check this out
#include <stdio.h>
#include <ctype.h>
void getReverse(char dest[], char src[], int count);
int main()
{
// *always* initialize variables
int count = 0;
const int MaxLen = 20; // max length string, leave upper case names for MACROS
const int MaxSize = MaxLen + 1; // add one for ending \0
int c = '\0';
char arr[MaxSize] = {0};
char revArr[MaxSize] = {0};
// first collect characters to be reversed
// note that input is buffered so user could enter more than MAX_SIZE
do
{
c = fgetc(stdin);
if ( c != EOF && (isalpha(c) || isdigit(c))) // only consider "proper" characters
{
arr[count++] = (char)c;
}
}
while(c != EOF && c != '\n' && count < MaxLen); // EOF or Newline or MaxLen
getReverse( revArr, arr, count );
printf("%s\n", revArr);
return 0;
}
void getReverse(char dest[], char src[], int count)
{
int i = count - 1;
int j = 0;
while ( i > -1 )
{
dest[j++] = src[i--];
}
}
Dealing with strings is a rich source of bugs in C, because even simple operations like copying and modifying require thinking about issues of allocation and storage. This problem though can be simplified considerably by thinking of the input and output not as strings but as streams of characters, and relying on recursion and local storage to handle all allocation.
The following is a complete program that will read one line of standard input and print its reverse to standard output, with the length of the input limited only by the growth of the stack:
int florb (int c) { return c == '\n' ? c : putchar(florb(getchar())), c; }
main() { florb('-'); }
..or check this
#include <stdio.h>
#include <stdlib.h>
#define MAX 100
char *my_rev(const char *source);
int main(void)
{
char *stringA;
stringA = malloc(MAX); /* memory allocation for 100 characters */
if(stringA == NULL) /* if malloc returns NULL error msg is printed and program exits */
{
fprintf(stdout, "Out of memory error\n");
exit(1);
}
else
{
fprintf(stdout, "Type a string:\n");
fgets(stringA, MAX, stdin);
my_rev(stringA);
}
return 0;
}
char *my_rev(const char *source) /* const makes sure that function does not modify the value pointed to by source pointer */
{
int len = 0; /* first function calculates the length of the string */
while(*source != '\n') /* fgets preserves terminating newline, that's why \n is used instead of \0 */
{
len++;
*source++;
}
len--; /* length calculation includes newline, so length is subtracted by one */
*source--; /* pointer moved to point to last character instead of \n */
int b;
for(b = len; b >= 0; b--) /* for loop prints string in reverse order */
{
fprintf(stdout, "%c", *source);
len--;
*source--;
}
return;
}
Output looks like this:
Type a string:
writing about C programming
gnimmargorp C tuoba gnitirw