I'm learning C with The C Programming Language (K&R).
Since I don't particularly want to bob back and forth between a text editor and running gcc, I've decided to use xcode as an IDE. So far, I've been able to follow the book's examples without a problem up until section 1.5.2.
When given the valid (?) program...
#include <stdio.h>
void main()
{
long nc;
nc = 0;
while (getchar() != EOF)
++nc;
printf("%ld\n", nc);
}
...I receive no final output telling me how many characters were in my input. I am entering my input via the xcode console window.
Upon some debugging, it looks like my program gets stuck in the while loop, and never encounters the EOF token. To accommodate for this, I've instead substituted a newline as the new condition, by replacing EOF with "\n", which also does nothing and gives me a int to pointer comparison warning.
What am I doing wrong here?
Will I be able to follow K&R using xcode?
Type ^D (control-d) to send an EOF.
If you want to break on a newline, you need to compare the return value of getchar to '\n', not "\n". The former is the actual char value of a newline; the latter is a pointer to a char with that value. If that doesn't make sense to you yet, don't worry, it will once you've read more.
Related
Exercise 1-9 in The C Programming Language by Denis Ritchie and Brian Kernighan, second edition:
Write a program to copy its input to its output, replacing each string of one or more blanks by a single blank.
Given that I'm using the book as a reference, I know only the C principles which have been discussed in the book up to Exercise 1-9, that is, variable assignments, while- and for-loops, if-statements, symbolic constants, character I/O via getchar() and putchar(), escape sequences and the printf() function. Maybe I'm forgetting something, but that's most of it. (I'm at page 20, where the exercise is at.)
Here's my (not working) code:
#include <stdio.h>
main()
{
int c;
while ((c = getchar()) != EOF) { // As long as EOF is not met, repeat the following…
if(c == ' ') { // If the input character is a blank, proceed (otherwise skip)…
putchar(c); // Output the blank space which was just inputed…
while(c == ' ') { // As long as more spaces keep coming in, don't do anything (proceed when another character comes along)…
;
}
}
else { // When a character other than a blank is inputed, output that character…
putchar(c);
} // Now retest the master while-loop condition (EOF not met) and proceed…
}
}
What I'm getting as a result is a working input-to-output program, that keeps on inputting and stops outputting the moment a blank is typed. (An exception to this is if the blank is removed with a backspace before entering a new line in the console.)
For example, the input abcde\nabcde abcde\nabcde will yield the output abcde, omitting the second and third lines, given that a blank is contained in the former. Here I am obviously using \n to represent an inputted new line (normally using the Enter key).
What have I done wrong, and what could I do to fix this issue? I know there are several working models of this program spread all over the internet, but I'm wondering why this one (which is my creation) in particular doesn't work. Again, do note that my knowledge of C is mostly limited to the first twenty pages of the book whose details are provided below.
Specs:
I'm running Eclipse version 2021-12 (4.22.0) on Debian GNU/Linux 11 (bullseye). I downloaded the pre-compiled Eclipse version from the official Eclipse.org website.
References:
Kernighan, B.W. and Ritchie, D.M. (1988). The C programming language / ANSI C Version. Englewood Cliffs, N.J.: Prentice Hall.
while(c == ' ') is forever loop.
you should try to remember previous character and if prevous character is whitespace and current character is also whitespace, skip it.
I am a beginner of C language, and now trying to count the number of characters which is input.
#include <stdio.h>
main()
{
long nc;
nc = 0;
while (getchar() != EOF)
++nc;
printf("%1d\n", nc);
}
This is what I wrote just as my textbook writes, but printf function doesn't seem to work.
In addition, this program doesn't seem to finish, because the prompt is not coming up.
I have no idea if the contents of this book are old enough.
Could you tell me what is wrong with this code?
This looks like code from K&R
The C Programming Language, 2nd Edn (1988), Chapter 1, p18.
The problem is that your transcription of the code is misinterpreting %ld as %1d. Given that nc is of type long, you need %ld (letter ell) and not %1d (digit one). The book contains an ell and not a one.
With suitable options, compilers like GCC and Clang will give warnings about type mismatches in the format strings. Use -Wall -Werror to get errors when the code is malformed (or -Wformat if you can't work with -Wall — but I use -Wall -Wextra -Werror plus a few extra fussier options for all my compilations; I won't risk making mistakes that the compiler can tell me about).
The use of main() shows that the book is dated. C99 requires a return type and prefers void in the argument list — int main(void) — when you don't make use of the command line arguments.
As to the program not finishing, when you're typing at the terminal, you indicate EOF (end of file) to the program by typing Control-D on most Unix-like systems (though it is configurable), and Control-Z on Windows systems. (If you want to indicate EOF without typing a newline immediately beforehand, you need to type the EOF indicator twice instead of just once.) Or you can feed it a file from the shell: counter < data-file (assuming the program is called counter and you want to count the characters in the file data-file).
I build a small program that simply copy the text from an input.txt file to an output.txt file. It works with no apparent problem with the command:
./myCopier.txt < rand.txt > randout.txt
on a GCC89 compiler, used for didactic purposes.
#include<stdio.h>
int main(void){
int c;
while ((c = getchar()) != EOF)
putchar(c);
/*The text in printf() does not appear anywhere*/
printf("My theories and toughts!\n");
}
Later, I add printf() in order to print my thoughts about how the program worked. It compiles without errors or warnings even with restrictive parameters (-Wall), but this text does not appear in any place. By any place, I mean both output.txt and the standard output.
I am thinking that:
The command ./myCopier.exe alone clearly create an endless loop. Without text input there is no EOL character, hence the print() command is never reached.
So, why when the input is provided, the printf() command has no apparent effect? I'd expect the text from printf()to appear on the standard output, just when the loop closes and output.txt is created. Why does it not occur?
My theory is that ./myCopier.txt < rand.txt > randout.txt does not allow it. For some reason, it makes the program only transfer the input to the output, "ignoring" everything does not come from the input. Am I correct? If so, why?
If you are interested in the problem with deeper detail, here is the assembly code:
http://text-share.com/view/79f31f38
Omitting the return statement in main() causes undefined behavior in C89/C90.
Normally, returning from main() forces the output buffer to be flushed. If you omit the return statement, anything can happen.
Try to add return 0 at the end of main() as was required in C90. GCC in c90/gnu90 mode will warn you otherwise: "control reaches end of non-void function".
Try to add fflush(stdout);.
I'm new to programming and I can't seem to get my head around why the following happens in my code, which is:
#include <stdio.h>
/*copy input to output; 1st version */
main()
{
int c;
c = getchar();
while (c != EOF) {
putchar(c);
c = getchar();
}
}
So after doing some reading, I've gathered the following:
Nothing executes until I hit Enter as getchar() is a holding function.
Before I hit Enter, all my keystrokes are stored in a buffer
When getchar() is called upon, it simply goes looks at the first value in the buffer, becomes that value, and then removes that value from the buffer.
My question is that when I remove the first c = getchar() the resulting piece of code has exactly the same functionality as the original code, albeit before I type anything a smiley face symbol immediately appears on the screen. Why does this happen? Is it because putchar(c) doesn't hold up the code, and tries to display c, which isn't yet defined, hence it outputs some random symbol? I'm using Code::Blocks if that helps.
The function you listed will simply echo back to you every character you type at it. It is true that the I/O is "buffered". It is the keyboard input driver of the operating system that is doing this buffering. While it's buffering keys you press, it echoes each key back at you. When you press a newline the driver passes the buffered characters along to your program and getchar then sees them.
As written, the function should work fine:
c = getchar(); // get (buffer) the first char
while (c != EOF) { // while the user has not typed ^D (EOF)
putchar(c); // put the character retrieved
c = getchar(); // get the next character
}
Because of the keyboard driver buffering, it will only echo back every time you press a newline or you exit with ^D (EOF).
The smiley face is coming from what #YuHao described: you might be missing the first getchar in what you ran, so putchar is echoing junk. Probably a 0, which looks like a smiley on your screen.
If you ommit the first getchar(), the code will look like this:
int c;
while (c != EOF) {
putchar(c);
c = getchar();
}
Here, c is uninitialized, so calling putchar(c) the first time will output a garbage value, that's where you get the smiley face.
"I'm new to programming"
You're not advised to learn programming using C (and the difficulties you're going through are because of the C language). For example, my first computer science classes were in pascal. Other univesities may use scheme or lisp, or even structured natural languages to teach programming. MIT's online classes are given in python.
C is not a language you would want to use in the first months of programming. The specific reason in your case is due to the fact that the language allowed you to use the value of an uninitialized value.
When you declare the integer variable "c", it gets an implicitly reserved space on the program stack, but without have any meaningful value: it's "trash", the value is whatever value was already on memory at that time. The C language requires that the programmer implicitly knows that he needs to assign some value before using a variable. Removing the first getchar results in uses before assignment in the while condition (c != EOF) and putchar(c), both before c has any meaningful value.
Consider the same code rewritten in python:
import sys
c = sys.stdin.read(1)
while c != '':
c = sys.stdin.read(1)
sys.stdout.write(c)
Remove the initial read and you get the following error:
hdante#aielwaste:/tmp$ python3 1.py
Traceback (most recent call last):
File "1.py", line 3, in <module>
while c != '':
NameError: name 'c' is not defined
That's a NameError: you used the value without assigned to it resulted in a language error.
For more information, try an online course, for example:
http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-00-introduction-to-computer-science-and-programming-fall-2008/video-lectures/
About uninitialized values:
http://en.wikipedia.org/wiki/Uninitialized_variable
Why does the following have the effect it does - it prints a terminal full of random characters and then exits leaving a command prompt that produces garbage when you type in it. (I tried it because I thought it would produce a seg fault).
#include <stdio.h>
int main(){
char* s = "lololololololol";
while(1){
printf("%c", *s);
s++;
}
}
it was compiled with:
gcc -std=c99 hello.c
It will eventually seg fault, but before that it'll print out whatever bytes are in the same page. That's why you see random chars on the screen.
Those may well include escape sequences to change (say) the character encoding of the console. That's why you end up with gibberish when you type on the console after it's exited, too.
Because you have an infinite loop (while(1)), and you keep getting the current value of pointer (*s), and then moving the pointer one char forward (s++). This has the effect of marching well past the end of the string into "garbage" (uninitialized memory), which gets printed to the console as a result.
In addition to what everyone else said in regards to you ignoring the string terminal character and just printing willy-nilly what's in memory past the string, the reason why your command prompt is also "garbage" is that by printing a particular "unprintable" character, your terminal session was left in a strange character mode. (I don't know which character it is or what mode change it does, but maybe someone else can pipe in about it that knows better than I.)
You are just printing out what is in memory because your loop doesn't stop at the end of the string. Each random byte is interpreted as a character. It will seg fault when you reach the end of the memory page (and get into unreadable territory).
Expanding ever so slightly on the answers given here (which are all excellent) ... I ran into this more than once myself when I was just beginning with C, and it's an easy mistake to make.
A quick tweak to your while loop will fix it. Everyone else has given you the why, I'll hook you up with the how:
#include <stdio.h>
int main() {
char *s = "lolololololololol";
while (*s != '\0') {
printf("%c", *s);
s++;
}
}
Note that instead of an infinite loop (while(1)), we're doing a loop check to ensure that the pointer we're pulling isn't the null-terminator for the string, thus avoiding the overrun you're encountering.
If you're stuck absolutely needing while(1) (for example, if this is homework and the instructor wants you to use it), use the break keyword to exit the loop. The following code smells, at least to me, but it works:
#include <stdio.h>
int main() {
char *s = "lolololololololol";
while (1) {
if (*s == '\0')
break;
printf("%c", *s);
s++;
}
}
Both produce the same console output, with no line break at the end:
lolololololololol
Your loop doesn't terminate, so println prints whatever is in the memory after the text you write; eventually it will access memory it is not allowed to read, causing it to segfault.
You can change the loop as the others suggested, or you can take advantage of fact that in c, zero is false and null (which terminates all strings) is also zero, so you can construct the loop as:
while (*s) {
Rather than:
while (*s != '\0')
The first one may be more difficult to understand, but it does have the advantage of brevity so it is often used to save a bit of typing.
Also, you can usually get back to your command prompt by using the 'reset' command, typing blindly of course. (type Enter, reset, Enter)