A question regarding the syntax of loop in C - c

I saw this little problem in the practice book, which is used to print whatever is typed in backward. And here, I just can't understand this loop for (; i >= 0; i--), why doesn't it assign the i to a variable in the first, is this a normal syntax as well? Thanks!
#include <stdio.h>
#include <string.h>
main() {
int i;
char msg[25];
printf("Type up to 25 characters and then press Enter...\n");
for (i = 0; i < 25; i++) {
msg[i] = getchar(); //Outputs a single character
if (msg[i] == '\n') {
i--;
break;
}
}
putchar('\n'); // One line break after the loop is done.
for (; i >= 0; i--) { // <--- this is the line
putchar(msg[i]);
}
putchar('\n');
return (0);
}

why doesn't it assign the i to a variable in the first
Because i already has the value we want to start at, from prior logic.
is this a normal syntax as well?
Yes. Any of the three parts of the for intro may be empty.

The three expressions which appear between the parentheses in a for() loop are all optional, and theoretically arbitrary.
Theoretically, you could write
for(a = b; c < d; e++)
printf("%d\n", f);
This is probably meaningless and useless, but as far as the C language is concerned, there's nothing wrong with it, and I wouldn't expect a compiler to emit any error messages or even warnings about it.
Now, conventionally, the first expression initializes something, the second expression tests whether we should make another trip through the loop, and the third expression increments. But that's all by convention, and again, all three expressions are optional.
For example, you could write
int i = 5;
for(; i < 10; i++)
printf("%d\n", i);
In this case, i gets its initial value when it's declared. This looks weird, to be sure, and it's arguably bad style, but it's not illegal.
The other expressions are optional, too. For example, I could write it like this:
int i = 5;
for(; i < 10; )
printf("%d\n", i++);
Now i gets incremented within the printf call, so there's no need to increment it in the for loop header. But by now, we've basically got a while loop:
int i = 5;
while(i < 10)
printf("%d\n", i++);
Anyway, this explains the loop you saw. The variable i already had the value it needed, from code up above, so there was no reason to set it in the for loop header.

The initial clause of the for loop can be empty: in the case you mention, i already contains the number of characters stored into the array, 25 or less if the user typed fewer characters before the newline. Yet the program has a bug because i should be decremented to avoid accessing msg beyond the last character stored in the array. Decrementing i if the character typed was '\n' is inconsistent and starting the output at msg[i] is incorrect too.
Your practice book is obsolete and confusing. Consider discarding it and using a more up to date book or online tutorial.
Any of the clauses of the for loop can be empty:
the initial clause may not be needed
the test clause can be empty, causing an infinite loop that ends either via a break or a return statement, or via a direct or indirect call to exit().
the update clause can contain any update expression, or nothing if the code already updates the required variables in the body of the loop, if any update is needed at all.
The first for loop is a very idiomatic example: for (i = 0; i < 25; i++) { ... }
Here is another classic for loop: for (;;) { /* repeat some action for ever */ }
Note the following problems in the posted code:
main must be defined with a return type int. The code uses an obsolete syntax where the return type was implicitly int. This syntax is invalid in C99.
the first for loop stores up to 25 bytes to the array, if the user typed more than 24 characters before the newline or signalled an end of file before the newline.
the program has undefined behavior in this latter case because msg[i] points beyond the end of msg if the user typed 25 characters before the newline.
the contents of the array is printed backwards, with potentially some funny characters such as ΓΏ at the beginning if indeed the input was ended prematurely.
there is no need to include <string.h>
the parentheses around the return value (0) are useless.
Here is a modified version:
#include <stdio.h>
int main() {
int i, c;
char msg[25];
printf("Type up to 25 characters and then press Enter...\n");
for (i = 0; i < 25; i++) {
c = getchar();
if (c == EOF || c == '\n')
break;
msg[i] = c;
}
putchar('\n'); // One line break after the loop is done.
while (--i >= 0) { // equivalent to for (; --i >= 0;)
putchar(msg[i]);
}
putchar('\n');
return 0;
}

Related

Is it correct to use a do-while loop inside a for loop? Why and why not?

//program to count words
#include<stdio.h>
int main()
{
int i;
int word = 0;
char sent[] = "How are you doing mister"; //character pointer
for (i = 0; sent[i] != '\0'; i++)
{
do
{
word++;
} while (sent[i] == ' ');
}
printf("There are %d words in the sentence\n", word + 1); //words are always 1 more than the no. of spaces.
return 0; //or word=1;
}
This is a code for counting the number of words. Please tell me why cannot we use do-while loops inside a for loop. Or if we can, how to do it.
Nesting the various composite statements such as for and do/while is allowed in C at least up to 127 levels, as specified in 5.2.4.1 Translation limits.
The problem is not a syntax issue, but rather a conceptual one:
your do/while loop iterates on a constant condition as neither i, nor sent is modified in the body or in the condition of the loop, leading to an infinite loop if sent[i] is a space.
counting spaces is not a correct approach to counting words in a string: "" has 0 words, not 1 per your intended code, " " also but you would get 2 and "A B" only has 2 words, not 3.
you should count the number of transitions from space to non space, starting with an implicit space before the beginning of the string.
note also that char sent[] = "..."; is not a character pointer, but a character array.
Here is a modified version:
//program to count words
#include <stdio.h>
int main() {
int i, words, last;
char sent[] = "How are you doing mister?";
words = 0;
last = ' ';
for (i = 0; sent[i] != '\0'; i++) {
if (sent[i] != ' ' && last == ' ')
word++;
last = sent[i];
}
printf("There are %d words in the sentence '%s'\n", words, sent);
return 0;
}
In my experience proof reading code, do/while loops tend to be written incorrectly, especially by beginners, missing test conditions or broken in some other way. I you think a do/while loop solves a given problem, think again, a for loop might be a safer approach. The only place do/while loops are needed is in a macro expansion where you want to combine multiple statements into a single compound statement:
#define swap_ints(a, b) do { a ^= b; b ^= a; a ^= b; } while (0)
Note however that the swapping method in this macro is inefficient and macros are very error prone and should be avoided even more than do/while loops :)
It is completely valid to nest a do-while loop within a for loop. Syntactically, there is nothing wrong with your program.
However, as others have described, your nested loop will never terminate when sent[i] == ' '. Your program has bugs, but they have nothing to do with the validity of nesting loops.

output file .exe is not responding and stopped working in C

It is a simple program to check whether string is palindrome or not. I made following code in the program.
When i compile it, there is no error but when i try to run the .exe file, i always get following message.
The fundamental problem here is that the loop exit condition can never be met, and this leads to an infinite loop:
(i != j || i != j - 1)
This condition is logically equivalent to:
!(i == j && i == j - 1)
which is obviously always true, so the loop continues indefinitely. The loop only needs to continue so long as j > i.
There is another problem here; namely that the dangerous function gets() should never be used. This function was deprecated in C99 and completely removed from the language in C11. One alternative is to use fgets() instead. Note that this function keeps the newline (if there is room in the buffer), so you will need to remove this after getting the input. Also, characters may be left in the input stream if the buffer is too small. For this reason, it would be better to declare a generously sized input buffer to reduce the risk of problems here. There is no reason not to use an input buffer holding 1000 characters, and I usually just use 4096 for something like this. Memory is cheap.
Further, there is a risk of undefined behavior in the posted code, since the input string may be empty. In this case, strlen(str) would be 0, so in the first execution of the loop body j would be decremented to -1. But the array access str[-1] is out of bounds, and leads to undefined behavior.
This problem can be fixed by checking that j is positive before the first decrement. Note that size_t is the correct type for array indices, as it is an unsigned integer type that is guaranteed to be able to hold any array index. Also note that the strlen() function returns a value of type size_t, not int.
Here is a modified version of the posted code. The size_t type is used for array indices. The length of the input string is stored in j, which is then decremented only if it is a positive value; this sets j to the index of the character preceding the null terminator so long as the input string is not an empty string. The loop continues while j is greater than i and the characters indexed by these values match. After the loop terminates, str[i] and str[j] should agree; if they do not, then the input was not a palindrome.
#include <stdio.h>
#include <string.h>
#define BUF_SZ 4096
int main(void)
{
char str[BUF_SZ];
printf("Enter string:\n");
fgets(str, sizeof str, stdin); // Never use gets()
str[strcspn(str, "\r\n")] = '\0'; // remove '\n'
size_t i = 0;
size_t j = strlen(str);
if (j > 0) {
--j;
}
while (i < j && str[i] == str[j]) {
++i;
--j;
}
if (str[i] == str[j]) {
puts("string is palindrome!!");
} else {
puts("string is not palindrome!!");
}
return 0;
}

what is wrong with the while loop here ? C basics

I am trying to count the number of letters of a word before the space. For eg. "Hello how" is my input string and I am trying to count the number of letters in the first word.
#include<stdio.h>
#include<string.h>
int main()
{
char a[30];
int count = 0;
printf("Enter the string.\n"); // Enter hello how as string here.
gets(a);
for ( i = 0; a[i] != '\0'; i++ )
{
while( a[i+1] != ' ' )
count++;
}
printf("%d\n",count);
}
This is a small part of a bigger code, I am actually expecting the value of count to be 5 but it gets into some sort of infinite loop which I am unable to figure out. If I use if instead of while , I get the expected answer. I know gets is not very reliable and I will not use once I get better at programming so it will be kind of you to post your answer about the loop instead of gets. Thank You.
The NUL character is written with a backslash (\), not forward slash (/).
for (i = 0; a[i] != '\0'; i++)
Furthermore, the inner loop will not terminate because you're not incrementing i.
while (a[i] != ' ') {
i++;
count++;
}
Actually, you should not really have two loops. One loop is all you need.
for (i = 0; a[i] != '\0' && a[i] != ' '; i++) {
count++;
}
The expression within the while statement does not depend on count. So with every iteration of the while loop the count gets incremented, but that has no influence on the while conditional, hence it will loop ad infinitum or never, depending on the character at a[i+1].
In addition to that, the conditional statement for the for loop is not written correctly either. The string escape for a NUL character is \0 (backslash). Or you can just compare against a 0 literal, which has exactly the same outcome (though when it comes to the subtleties of C it does not mean exactly the same, but that's splitting hairs).
Use '\0' instead of '/0' in the for loop condition and also the while loop condition will never be false because i remains the same

Segmentation fault in C program with apparently perfect code

I recently recommended K&R to a friend who wanted to learn C. He came across an exercise in the first chapter that gave him various errors. I compiled it on my Ubuntu installation, alternating between the C90 option and the defaults. I've looked at every angle but it seems to be perfect code...yet it consistently gives me a segmentation fault each time I run it. I'm not the sharpest programmer in the shed but this has me pretty frustrated.
What on earth is causing such an error?
Here is the code:
#include <stdio.h>
#define MAXLINE 1000
void reverse(char s[]);
/* A program that reverses its input a line at a time */
main()
{
int c, i;
char line[MAXLINE];
for (i = 0; (c = getchar()) != EOF; ++i) {
line[i] = c;
if (c == '\n') { /* Upon encountering a newline */
line[i] = '\0'; /* replace newline with null terminator */
i = 0;
reverse(line);
printf("\n%s\n", line);
}
}
return 0;
}
/* A function that reverses the character string */
void reverse(char s[])
{
int a, z;
char x;
for (z = 0; s[z]; ++z) /* Figure out where null terminator is */
;
--z;
for (a = 0; a != z; ++a) { /* Reverse array usinng x as placeholder */
x = s[a];
s[a] = s[z];
s[z] = x;
--z;
}
}
You're missing a semi-colon here:
for (z = 0; s[z]; ++z); /* Figure out where null terminator is */
// ^
This loop is supposed to run until it finds the null terminator. If you leave off this semi-colon, then on each iteration it does both ++z and --z, which means it just loops forever. You want the --z to happen after this loop has completed, as that will set z equal to the last character before the string's null terminator.
For an even-length string, a and z will cross each other (they'll never be equal) in the second loop. For example, if z=5 and a=4, then on the next iteration a=5 and z=4. If you check for a<z instead of a!=z then you avoid that problem. Since you're checking for != instead of <, this would cause the loop to run pretty much infinitely. However, you'll end up with a SEGFAULT as a grows too large and z grows too small, since they'll both be used to index into memory outside your buffer.
for (a = 0; a != z; ++a) { /* Reverse array usinng x as placeholder */
// ^ should be <, not !=
Finally, there's also a bug in main. When you find a newline, you set i=0. This works fine when you print the string, but when i is incremented at the end of the loop, you end up with i=1 as you start reading the next string. This means you'll have an extra character at the beginning of your string. You need to do something to properly reset i to make sure that doesn't happen.
K&R has a string reverse function in Section 3.5. In my copy it's on page 62. It looks like your friend decided to iterate over the string rather than calling strlen (which is really all the function call does anyway), and thought that a<z should be equivalent to a!=z.
Does this loop over run?
for (z = 0; s[z]; ++z) /* Figure out where null terminator is */
--z;
for (z = 0; s[z]; ++z) /* Figure out where null terminator is */
--z;
Just after the first iteration, z becomes -1 -- so trying to access s[z] results in segmentation fault.

Cannot figure this out C programming

The program is supposed to remove everything but the letters and create a new string which will have only the letters in upper-case.
However, it is not printing the results.
Here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *remove_up(char input[])
{
char *new_str = (char *) malloc(strlen(input) + 1);
int i=0;
int j=0;
while (i < strlen(input))
{
if (((input[i]) >= 65 && (input[i]<=90)) || ((input[i]>=97) && (input[i]<=122)))
{
new_str[j]= toupper(input[i]);
i++;
j++;
}
else i++;
}
return new_str;
}
int main()
{
char str_1[100];
char str_2[100];
printf("Enter first word: ");
fgets(str_1, sizeof(str_1), stdin);
printf("Enter second word: ");
fgets(str_2, sizeof(str_2), stdin);
char *up_str_1 =(char *) malloc(strlen(str_1) + 1);
char *up_str_2 =(char *) malloc(strlen(str_2) + 1);
up_str_1= remove_up(str_1);
up_str_2= remove_up(str_2);
printf("%s", up_str_1);
printf("\n");
printf("%s", up_str_2);
return 0;
}
There are a few problems, but because this is tagged homework, I'll point them out but not give you the answer.
First of all, this doesn't do what you think:
int i, j = 0;
j will be initialized, but i probably won't start at 0. You need to initialize i to 0 as well.
Next, there's a typo - you missed a closing ] at (input[i<=122).
Finally, based on your answers to the questions, you probably aren't printing the result anyway: look up printf() or cout or whatever you prefer to use for outputting values.
It doesn't print results because you haven't used any print statements to show what comes back from your calls to remove_up.
To understand what is going on in your remove_up function, you need to understand this:
http://www.asciitable.com/
This code:
if (((input[i]) >= 65 && (input[i]<=90)) || ((input[i]>=97) && (input[i<=122)))
Is checking to see if a character is an alphabetic character in the ascii character set between these two ranges. Look at the link above. If it is in this set it's converting it to upper (redundant for half the data) and saving the result in your newly malloc'd string.
Problems:
1. You never set a null terminator in "new_str"
2. You never seem to free anything (though in this code it is trivial, in real code you could create problems, i.e. memory leaks).
3. "i" is redundant in the while loop. It's in both the if and else...
4. Rethink how you're using malloc (you probably don't want to use it this way in your custom functions unless you're going to cleanup after yourself)
There is probably more I'm missing, but that should help you see some problems.
Double check your use of parenths - you have more than needed. You are also missing a ']' in that if statement. Surprised it compiles.
change int i, j = 0; to int i = 0, j = 0;. Your i was initialized with a garbage value greater than strlen(input), and hence never entered the while loop.

Resources