LinuxQuestions.org
Welcome to the most active Linux Forum on the web.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 05-18-2013, 06:18 PM   #1
lleb
Senior Member
 
Registered: Dec 2005
Location: Florida
Distribution: CentOS/Fedora/Pop!_OS
Posts: 2,983

Rep: Reputation: 551Reputation: 551Reputation: 551Reputation: 551Reputation: 551Reputation: 551
a C programming question; howto prompt for restart script?


I am in a C class, but we get a week off (only had 1 class and already a day off, Fri morning class) so I got to thinking about the three scripts the Prof. had us write for homework.

The scope is simple. create script that takes input, then prints the desired output to screen and end.

Id like to take it a bit further. Id like to figure out how to get the script to ask would you like to continue. if [Yy] then continue if anything else then exit.

In BASH id just create a simple if/then statement to call a function, but from reading around on google and in The ANSI C Programming Language book the examples they have for functions are the mathematics I am running in my main(void).

As i am still really a newB, i consider myself a newB at least, to BASH scripting I am not wrapping my head around the function(s) in C.

here is one of the programs id like to add the prompt to continue to:

Code:
/* 5. Write a program to convert a temperature in degrees Fahrenheit to degrees Celsius. */
//##########################################################
// Created by Raymond L. Brunkow 
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 or version 3 of the
// license, at your option.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
//##########################################################
#include<stdio.h>

//Problem Input
//int fahrenheit /* temperature in degrees Fahrenheit */
//Problem Output
//double celsius /* temperature in degrees Celsius */
//Relevant Formula celsius = 5/9 ( fahrenheit \u2212 32)

int main(void)

{

	int FAH;
	double CEL;

	/* Get the degree in Fahrenheit */
	printf("Please enter the temperature in Fahrenheit: ");
	scanf("%d", &FAH);

	/* Convert F to C */
	CEL = 5 * (FAH-32) / 9;

	/* Display results */
	printf("%d degrees Fahrenheit converts to %f degrees Celsius.\n", FAH, CEL);
	sleep(10);

	return(0);

}
sweet simple script. *note* the top line is the actual homework problem and as you can see i have met the requirements so I am not asking you to do my homework for me. That would not fly around here anyways ...

What I'm asking is for examples of how I would turn the main(void) section into either a function, or how would I get the script to start back at the top to run again until the user input is != [Yy]???

Thanks.
 
Old 05-18-2013, 07:44 PM   #2
lleb
Senior Member
 
Registered: Dec 2005
Location: Florida
Distribution: CentOS/Fedora/Pop!_OS
Posts: 2,983

Original Poster
Rep: Reputation: 551Reputation: 551Reputation: 551Reputation: 551Reputation: 551Reputation: 551
*grumble*

ive been mucking around with do/while loops and just can not get it to work:

Code:
#include<stdio.h>
#define TRUE  1
#define FALSE  0

//Problem Input
//int fahrenheit /* temperature in degrees Fahrenheit */
//Problem Output
//double celsius /* temperature in degrees Celsius */
//Relevant Formula celsius = 5/9 ( fahrenheit \u2212 32)

int main(void)

{
char ANS, Y;
do	{
	char ANS;
	int FAH;
	double CEL;
	
	/* Get the degree in Fahrenheit */
	printf("\nPlease enter the temperature in Fahrenheit: ");
	scanf("%d", &FAH);

	/* Convert F to C */
	CEL = 5 * (FAH-32) / 9;

	/* Display results */
	printf("\n%d degrees Fahrenheit converts to %.2f degrees Celsius.\n", FAH, CEL);
	//sleep(10);
	
	/* Prompt for new input */
	printf("\nWould you like to enter a new temperature [Y]es or [N]o?  ");
	scanf("%c", &ANS);
	if (ANS == Y)
	{return(TRUE);}
	else
	{return(FALSE);}
	}
	while (1);
	
	return(0);

}
the program stops properly to get the F entry, it then properly displays the output, but never stops to wait for the Y/N for the last question, it just exits the program. the while (FALSE) and while (1) are not working and more importantly why is the program refusing to STOP and wait for an answer?
 
Old 05-18-2013, 08:48 PM   #3
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197
I think the first scanf leaves the newline in the input buffer. So the second scanf gets the newline the user typed for the first.

Anyway, the do and the while(1) are worthless because both sides of the if are return.

Last edited by johnsfine; 05-18-2013 at 08:50 PM.
 
Old 05-18-2013, 09:17 PM   #4
Beryllos
Member
 
Registered: Apr 2013
Location: Massachusetts
Distribution: Debian
Posts: 529

Rep: Reputation: 319Reputation: 319Reputation: 319Reputation: 319
[QUOTE=lleb;4954023]
Code:
	scanf("%c", &ANS);
	if (ANS == Y)
I googled "c programming scanf character" and found that this problem is known. I have no first-hand experience with scanf, so I'll take their word for this: The previous scanf function leaves a newline character in the stdin buffer, which somehow feeds into the second scanf so it proceeds without user input. (Sounds a little weird to me, but let's continue...) Apparently it can be fixed by putting a space in the format specifier:
Code:
	scanf(" %c", &ANS);
The space, we are told, stands for any whitespace, including newlines.

By the way, I am not sure if your Y/N conditional is correctly written. I see that Y is declared as a character, but I don't see where it is assigned a value. I would write the conditional as:
Code:
	if (ANS == 'Y')
There is some difference between single and double quotes. You need single quotes in this context.
 
1 members found this post helpful.
Old 05-18-2013, 10:08 PM   #5
Erik_FL
Member
 
Registered: Sep 2005
Location: Boynton Beach, FL
Distribution: Slackware
Posts: 821

Rep: Reputation: 258Reputation: 258Reputation: 258
Talking

The problem is that "scanf( )" does not read the new line "\n" at the end of a line. In fact, "scanf" is not guaranteed to read an entire line. It only reads what is necessary. For example, if you typed this in at the first prompt.

100y

Then BOTH of your "scanf( )" functions would succeed and return immediately. The first one reads "100" and stops because it sees a non-numeric character. the second one reads the "y" since there is another character that can be read.

Typing in a number followed by a new-line character reads only the number, and not the new-line after the number. So, "scanf("%d", &FAH)" leaves the new-line character waiting to be read. The next "scanf("%c", &ANS)" reads ANY character. It reads the new-line character of the first input line that you typed.

There are a few different ways around this problem. One way around the problem is to skip white-space in the "scanf" before reading the character.

Code:
scanf(" %c", &ANS);
White-space in the scanf specifier causes all white-space to be skipped up to the first non-white-space character.

Another way to solve the problem is to read a string instead of a character. Reading a string skips leading white-space. A string also ends at the first white-space. With "scanf" a string is really more like a token.

Code:
char ANS[2];
scanf( "%1s", ANS );
The best way to read input from the user is one line at a time. Then, use "sscanf" to parse each line.

Code:
char BUFFER[81];
char ANS[2];

fgets( BUFFER, sizeof(BUFFER), stdin );
sscanf( BUFFER, "%d", &FAH);
fgets( BUFFER, sizeof(BUFFER), stdin );
sscanf( BUFFER, "%1s", ANS );
This allows you to do a lot more error checking, because multiple items typed on the same line will not remain in the input buffer to be read by the next "scanf( )". Also, "sscanf( )" returns the number of items processed. One can check to see if exactly the correct number of items was typed on each line.

Code:
do
{
    printf("\nWould you like to enter a new temperature [Y]es or [N]o?  ");
    fgets( BUFFER, sizeof(BUFFER), stdin );
}
while ( sscanf( BUFFER, "%1s%*1s", ANS ) != 1 || strchr( "YyNn", *ANS ) == NULL  );
Using "fgets" instead of "gets" limits the number of characters to the size of the character buffer. The "sscanf" function tries to read two character strings (tokens) of a single character. If just a "Y" or "N" is entered, then only one character string is returned and "sscanf" returns 1. If extra "garbage" is typed, then "sscanf" reads two strings and returns 2. Note that using "*" in a "sscanf" specifier throws away the data instead of storing it. The program doesn't care what is the extra "garbage".

Reading numbers can work much the same way.

Code:
do
{
    printf("\nPlease enter the temperature in Fahrenheit: ");
    fgets( BUFFER, sizeof(BUFFER), stdin );
}
while ( sscanf( BUFFER, "%d%*1s", ANS ) != 1 );
You may be trying to write a program to read a Y or N response without requiring a new-line. Reading input without waiting for a new-line is difficult. Some input devices won't allow your program to "read" any input until a new-line is typed. That's because lines can be edited before they are entered. Even using "getchar( )" may not return any character until a new-line is entered. So, reading individual key-presses requires special functions that are dependent on the operating system.
 
2 members found this post helpful.
Old 05-18-2013, 11:23 PM   #6
lleb
Senior Member
 
Registered: Dec 2005
Location: Florida
Distribution: CentOS/Fedora/Pop!_OS
Posts: 2,983

Original Poster
Rep: Reputation: 551Reputation: 551Reputation: 551Reputation: 551Reputation: 551Reputation: 551
Erik FL, you are way over my head, but i think i understand some of what you are saying. I attempted to incorporate the code you have for the Y/N response, but I am getting the following error when compiling:

Code:
ssma-imac:ENG-3211 ssma$ gcc -o hw_1_5.v2 hw_1_5.v2.c 
hw_1_5.v2.c: In function ‘main’:
hw_1_5.v2.c:51: warning: passing argument 1 of ‘fgets’ makes pointer from integer without a cast
hw_1_5.v2.c:54: warning: incompatible implicit declaration of built-in function ‘strchr’
here is the modified script:

Code:
#include<stdio.h>
#define TRUE  1
#define FALSE  0

//Problem Input
//int fahrenheit /* temperature in degrees Fahrenheit */
//Problem Output
//double celsius /* temperature in degrees Celsius */
//Relevant Formula celsius = 5/9 ( fahrenheit \u2212 32)

int main(void)

{
char ANS[2], Y, BUFFER[81];
do	{
	char ANS, BUFFER;
	int FAH;
	double CEL;
	
	/* Get the degree in Fahrenheit */
	printf("\nPlease enter the temperature in Fahrenheit: ");
	scanf("%d", &FAH);

	/* Convert F to C */
	CEL = 5 * (FAH-32) / 9;

	/* Display results */
	printf("\n%d degrees Fahrenheit converts to %.2f degrees Celsius.\n", FAH, CEL);
	//sleep(10);
	
	/* Prompt for new input */
	printf("\nWould you like to enter a new temperature [Y]es or [N]o?  ");
	fgets( BUFFER, sizeof(BUFFER), stdin );	
	
	}
	while ( sscanf( BUFFER, "%1s%*1s", ANS ) != 1 || strchr( "YyNn", *ANS ) == NULL  );
	return(0);

}
thanks all for helping me understand more and get ahead in class.
 
Old 05-19-2013, 04:11 PM   #7
Erik_FL
Member
 
Registered: Sep 2005
Location: Boynton Beach, FL
Distribution: Slackware
Posts: 821

Rep: Reputation: 258Reputation: 258Reputation: 258
Quote:
Originally Posted by lleb View Post
I am getting the following error when compiling:

Code:
ssma-imac:ENG-3211 ssma$ gcc -o hw_1_5.v2 hw_1_5.v2.c 
hw_1_5.v2.c: In function ‘main’:
hw_1_5.v2.c:51: warning: passing argument 1 of ‘fgets’ makes pointer from integer without a cast
hw_1_5.v2.c:54: warning: incompatible implicit declaration of built-in function ‘strchr’
The error on line 51 is because you need to use character strings (character arrays) rather than single character variables.

Code:
char ANS[2], BUFFER[81];
What the above does is define "ANS" as an array of two characters. For example, "ANS[0]" and ANS[1]". To refer to the whole array, you can use "ANS" and it is the same as a pointer to a character "&ANS[0]". When you read the 1 character string into ANS then ANS[0] will have the 'Y' or 'N' and ANS[1] will have the zero terminator '\0'.

The input buffer "BUFFER" is 81 characters, so the first 80 characters can be text, leaving the last character for the zero terminator. Of course if the user enters less text, the zero terminator will be somewhere else. For example typing in "test" will result in BUFFER[0] through BUFFER[3] being "test" and BUFFER[4] will be 0.

The error on line 54 is because you need to include a header file to define the "strchr" function.

Code:
#include <string.h>
The "string.h" header file defines the "strchr" function. The "strchr" function returns a "char *". If you don't include the header file, the default (implicit) definition of the "strchr" function is to return an integer. Since "strchr" is also a built-in function the compiler warns you that you are trying to redefine an already defined built-in function.

The other thing you may be wondering about is the "sizeof( )" function. It is actually a compiler built-in function. It returns the size of a variable in bytes. For a character array, the size in bytes is always equal to the size in characters. The zero terminator takes up a byte, so an array of 81 characters can hold a character string that has 80 characters. If you use "sizeof( )" for other kinds of data, keep in mind that they may be more than one byte each. For example, sizeof(int) is usually 4 and sizeof(short) is usually 2.

Code:
int numbers[3];

printf( "The array is %d bytes and can hold %d numbers\n", sizeof(numbers), sizeof(numbers)/sizeof(int) );
The above prints a message saying that the array is 12 bytes and can hold 3 numbers.

In cases where the type of the data in the array might be changed later, you can divide by the total size of one array element.

Code:
printf( "The array is %d bytes and can hold %d numbers\n", sizeof(numbers), sizeof(numbers)/sizeof(numbers[0]) );
The above will work no matter what type of data is in the array. It could be an array of double-precision floating point numbers that are 8 bytes each.

One of the hardest concepts to understand in C is the difference between a character, a character array, and a string. A character is a single byte (8-bits). A character array is a sequence of characters, with a fixed size. A string is a variable-length sequence of characters ending with a 0. The zero at the end of a string is a normal character, and takes up a byte. You can store a string in a character array if the array is large enough for the characters and the terminating 0 byte. There may be other characters after the 0 byte if the array is larger than the string.

Adding to the confusion, C does not have a separate "string" data type. A "char *" could be a pointer to a single character, a pointer to all or part of a character array, or a pointer to a string. That is determined by how the program and functions use the pointer.

A string constant is really just a convenient way of declaring a constant array of characters, and then using the pointer to that array.

Code:
const char msg[] = { 'T', 'e', 's', 't', '\n', 0 };
const char *msg2 = "Test\n";

puts(msg);
puts(msg2);
puts("Test\n");
puts("Test\n");
Although "puts" outputs the same text for each call, the character pointer (memory address) is usually different each time. Some really smart C compilers may actually figure out that all four (or maybe three) of the strings are identical constants. In that case, some of the pointers (memory addresses) might be exactly the same. Also note that "msg2" does not contain a string! Since "msg2" is a pointer to a string, it contains the address of the string constant that is on the same line. If the program were to assign a different pointer, then "msg2" could refer to a totally different string.

Code:
msg2 = "New text string";
puts(msg2);
So, what happened to the text string ""Test\n"" that was on the same line where "msg2" was originally defined? That text string may still exist somewhere in memory, but the program is no longer able to obtain the address of that string. For all practical purposes, the string can no longer be used in the program. It's really no different than using a string constant as a parameter in a function call. Smarter C compilers will remember if a string constant exists in memory and use the exact same constant where it appears again.

Also, "msg2" is not a constant, the characters that "msg2" points to are constant. Otherwise, how could the program change the pointer? The following would make msg2 a constant pointer to constant characters.

Code:
const char *const msg2 = "Test\n";
Now it is not possible to do this.

Code:
msg2 = "New text string";
Some people find it easier to understand if it is written a different way.

Code:
const char *(const msg2) = "Test\n";
const char* const msg2 = "Test\n";
const char* (const msg2) = "Test\n";
(const char*) (const msg2) = "Test\n";
Why does any of that matter? Strictly speaking, you can do anything to a non-constant variable than you can to a constant variable. The advantage is for the compiler to warn you if you do something unintended. By saying that something is a constant, you are telling the compiler that you don't ever intend to change it. Perhaps you used "msg2" in a lot of places and just defined it to avoid having lots of copies of the exact same string constant. In that case you really don't want to allow the program to accidentally change "msg2".
 
1 members found this post helpful.
Old 05-19-2013, 05:44 PM   #8
lleb
Senior Member
 
Registered: Dec 2005
Location: Florida
Distribution: CentOS/Fedora/Pop!_OS
Posts: 2,983

Original Poster
Rep: Reputation: 551Reputation: 551Reputation: 551Reputation: 551Reputation: 551Reputation: 551
ok. much information.

i added the new include, but i am still getting an error with the fget line. reading around there seem to be 2 standards. the one you provided above and one that has a -1 for the 2nd part...

Code:
fgets( BUFFER, sizeof(BUFFER), stdin );
and
Code:
fgets( BUFFER, sizeof(BUFFER)-1, stdin );
neither seem to make much of a difference in the error im getting...

Code:
$ gcc -o hw_1_5.v2 hw_1_5.v2.c 
hw_1_5.v2.c: In function ‘main’:
hw_1_5.v2.c:52: warning: passing argument 1 of ‘fgets’ makes pointer from integer without a cast
from what little i can follow in your above post, not you just me being so new to C, the stdin is passing as an integer even though we have BUFFER defined as a character array. that is confusing. if it is defined as a character array(or string im sure im using the wrong term here), then why does the compiler think its an integer?
 
Old 05-19-2013, 06:29 PM   #9
Erik_FL
Member
 
Registered: Sep 2005
Location: Boynton Beach, FL
Distribution: Slackware
Posts: 821

Rep: Reputation: 258Reputation: 258Reputation: 258
Quote:
Originally Posted by lleb View Post
ok. much information.

i added the new include, but i am still getting an error with the fget line. reading around there seem to be 2 standards. the one you provided above and one that has a -1 for the 2nd part...
Make sure that you have "BUFFER" declared as an array of characters.

Code:
char BUFFER[81];
That should eliminate the error. Note the error message refers to parameter 1 of "fgets" and that is "BUFFER".

Using the "-1" isn't necessary since "fgets" already subtracts one from the length.

Last edited by Erik_FL; 05-19-2013 at 06:35 PM.
 
Old 05-19-2013, 06:39 PM   #10
chrism01
LQ Guru
 
Registered: Aug 2004
Location: Sydney
Distribution: Rocky 9.2
Posts: 18,359

Rep: Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751
Read the definition of fgets() http://linux.die.net/man/3/fgets.
1st arg must be ptr-to-char, not char.
 
Old 05-19-2013, 07:27 PM   #11
lleb
Senior Member
 
Registered: Dec 2005
Location: Florida
Distribution: CentOS/Fedora/Pop!_OS
Posts: 2,983

Original Poster
Rep: Reputation: 551Reputation: 551Reputation: 551Reputation: 551Reputation: 551Reputation: 551
thanks found the compiling error issue. i was redefining both ANS and BUFFER as char instead of strings. i deleted that line and it was then able to compile. Now i need to figure out how to get the script to wait for user input before it starts over. now it just prints the question, does not wait, then restarts the script. so it is at least restarting, just want it to wait for the Y/N response to it will exit properly too.
 
Old 05-19-2013, 08:14 PM   #12
Erik_FL
Member
 
Registered: Sep 2005
Location: Boynton Beach, FL
Distribution: Slackware
Posts: 821

Rep: Reputation: 258Reputation: 258Reputation: 258
You need to make sure that the first new-line character (following the number) is read in before the program reads the Y/N answer. One way to do that is to use "fgets" to read the entire input line.

Code:
fgets( BUFFER, sizeof(BUFFER), stdin );
sscanf( BUFFER, "%d", &FAH );
Another way to handle the problem is to read and discard the characters up to the first new-line.

Code:
scanf( "%d", &FAH );
while ( getchar( ) != '\n' );
 
Old 05-19-2013, 08:19 PM   #13
lleb
Senior Member
 
Registered: Dec 2005
Location: Florida
Distribution: CentOS/Fedora/Pop!_OS
Posts: 2,983

Original Poster
Rep: Reputation: 551Reputation: 551Reputation: 551Reputation: 551Reputation: 551Reputation: 551
Quote:
Originally Posted by Erik_FL View Post
You need to make sure that the first new-line character (following the number) is read in before the program reads the Y/N answer. One way to do that is to use "fgets" to read the entire input line.

Code:
fgets( BUFFER, sizeof(BUFFER), stdin );
sscanf( BUFFER, "%d", &FAH );
question about this. isnt %d the place holder for integer? isnt %c that place holder? if the answer is going to be a char, why would we be looking for an integer? is that due to ANS[2] or BUFFER[81]?
Quote:

Another way to handle the problem is to read and discard the characters up to the first new-line.

Code:
scanf( "%d", &FAH );
while ( getchar( ) != '\n' );
 
Old 05-19-2013, 08:39 PM   #14
chrism01
LQ Guru
 
Registered: Aug 2004
Location: Sydney
Distribution: Rocky 9.2
Posts: 18,359

Rep: Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751
In C, a single 'char' fits exactly into the space for an int, therefore you can (under certain circumstances) treat them either way, depending on context.
See the ASCII table http://www.asciitable.com/.

Its been while since I've written any C, but as an example you can check for eg a lowercase single alpha char using arithmetic methods; something like (off the top of my head)
Code:
if ( x >= 'a' && x <= 'z' )
{
    /* x is lowercase char */
.
.
}
UNTESTED

I used to like this book a lot when i was learning C http://www.amazon.com/Book-Programmi.../dp/0201183994 (A Book on C; Auth by Kelley & Pohl).
Extremely clear teaching, inc line by line analysis of examples.
I highly recommend you lookup each new C (library) fn you come across and become familiar with its args, & rtn value (if any).
Details, Details.... a very powerful lang, but takes some focus to pick up.
 
Old 05-19-2013, 09:29 PM   #15
lleb
Senior Member
 
Registered: Dec 2005
Location: Florida
Distribution: CentOS/Fedora/Pop!_OS
Posts: 2,983

Original Poster
Rep: Reputation: 551Reputation: 551Reputation: 551Reputation: 551Reputation: 551Reputation: 551
thanks.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
[SOLVED] howto restart hal daemon Fred Caro SUSE / openSUSE 3 03-27-2011 12:04 PM
bash programming question - hand over variables to another script ppr:kut Linux - General 5 02-09-2008 10:00 AM
question about shell-script programming GSX Programming 11 11-22-2005 04:58 PM
Restart apache service without password prompt? Phaethar Linux - Software 2 07-06-2004 01:28 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 06:40 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration