Solid user input check - Newbie Learning C - input

So I'm learning C and got this exercise to do with functions. Not gonna ask how to do it.
So I created this function:
int menu(void) {
char user;
do {
printf("Choise: ");
user = getchar();
if (user == '1') {
/* code and no return here */
}
else if (user == '2') {
/* code */
return 2;
}
else if (user == '3') {
/* code */
return 3;
}
} while (user != '3');
Got others controls flows like isdigit(user) and !isdigit(user).
The problem here is that if the user input "fw" (for example), the program prints 2 times "Choise: " or more if there's more input of course.
I tried several others controls flows and changing the variable user to an array and force user[0] == ... && user[1] == '\n' but to no avail.
What I'm trying to do is, if the user don't enter one of the 3 options the program just stop reading after the 1st input and waits for another input.
Already checked several questions at StackOverflow but it doesn't answer to my question :/
I'm all ears to advises! Thank in advance!

The underlying cause here is that getchar() in C gets one single character; it's not like, for example, input() in Python, which gets an entire string of as many characters as you like. The usual technique in C to get a string consisting of more than one character is with pointers: You declare a variable, for instance, as char* a; To obtain user input, you use scanf() and store the input under that pointer address: scanf(&a); Technically, a is the pointer pointing at the memory address of the first character in your string, but the compiler stores the individual characters of the string in a contiguous block of memory until a null character is reached to mark the end of the string.
To avoid the risk of seg faults, you might want to reserve as much memory as you need to hold the longest string you think you'll need: a = malloc((sizeof(char)*n);, with n being the number of characters you want to set memory aside for.

Apologies for not posting in a while, I've been travelling. I thought about the problem again, and I think the root of the problem is that getchar() will return the character cast into an integer, namely, the ASCII value of the character. So if the user keys in "1", you can't run an if statement that tests if (user == '1') (C doesn't natively support direct string comparison). Instead, you should test if (user == 49) - 49 is the ASCII value of the digit "1" ("2" is 50, "3" is 51). If you write your if statements and the loop condition accordingly, that should work.

Related

Do-While Loop in C doesn't repeat

This code doesn't repeat if I answer a negative number like "-1.01". How can I make it loop so that it will ask again for c?
#include <stdio.h>
main()
{
float c;
do {
printf("O hai! How much change is owed? ");
scanf("%.2f", &c);
} while (c < 0.0);
return(0);
}
The format strings for scanf are subtly different than those for printf. You are only allowed to have (as per C11 7.21.6.2 The fscanf function /3):
an optional assignment-suppressing character *.
an optional decimal integer greater than zero that specifies the maximum field width (in characters).
an optional length modifier that specifies the size of the receiving object.
a conversion specifier character that specifies the type of conversion to be applied.
Hence your format specifier becomes illegal the instant it finds the . character, which is not one of the valid options. As per /13 of that C11 section listed above:
If a conversion specification is invalid, the behaviour is undefined.
For input, you're better off using the most basic format strings so that the format is not too restrictive. A good rule of thumb in I/O is:
Be liberal in what you accept, specific in what you generate.
So, the code is better written as follows, including what a lot of people ignore, the possibility that the scanf itself may fail, resulting in an infinite loop:
#include <stdio.h>
int main (void) {
float c;
do {
printf ("O hai! How much change is owed? ");
if (scanf ("%f", &c) != 1) {
puts ("Error getting a float.");
break;
}
} while (c < 0.0f);
return 0;
}
If you're after a more general purpose input solution, where you want to allow the user to input anything, take care of buffer overflow, handle prompting and so on, every C developer eventually comes up with the idea that the standard ways of getting input all have deficiencies. So they generally go write their own so as to get more control.
For example, here's one that provides all that functionality and more.
Once you have the user's input as a string, you can examine and play with it as much as you like, including doing anything you would have done with scanf, by using sscanf instead (and being able to go back and do it again and again if initial passes over the data are unsuccessful).
scanf("%.2f", &c );
// ^^ <- This seems unnecessary here.
Please stick with the basics.
scanf("%f", &c);
If you want to limit your input to 2 digits,
scanf("%2f", &c);

I keep getting stuck in an infinite loop java

I am writing a battleship program. Right now I am testing a couple lines of code to see if it will place the boat going in the up direction. How my program is set up is that if, for example, the user clicks on the aircraft carrier button to set his aircraft carrier, the program should also set the ai's aircraft carrier. The boats are placed on a button array, called tlba. aifirstclicki is set by a random generator so that it will choose a random row. aifirstclickj chooses a random column, in conjunction the two pinpoint a spot on the button array (which is 10x10). I wrote the following code to try to make it so that if the program has an outofboundsexception error,or in other words if the program chooses a first spot that will eventually cause an outofbounds exception error because the for loop will keep adding spots until aiclickcount = 5, it should start over and pick a different spot until it finds a spot that will allow it to place all 5 spots. I keep getting stuck in an infinite loop though.
int aiclickcount = 0;
while (directiondecider == 0)
{//up
aifirstclicki = generator.nextInt(10);
aifirstclickj = generator.nextInt(10);
while (aifirstclicki != 3 &&
aifirstclicki != 2 &&
aifirstclicki != 1 &&
aifirstclicki != 0)
{
for(int k=0; k<shiplength; k++)
{
tlba[aifirstclicki - k][aifirstclickj].setBackground(Color.RED);
aistringarray[aifirstclicki - k][aifirstclickj] = "aircraftcarrier";
aioccupied2d[aifirstclicki - k][aifirstclickj] = true;
aiclickcount++;
}
if (aiclickcount == 5)
{
shipset = true;
break;
}
}
System.out.println(shipset);
}
Does anyone know what's wrong or have a different solution to my problem?
You never have aiclickcount == 5 if your shiplength is not 5. Put if into your for loop. You don't need the second while at all, you don't break out of it as well. Just generate number greater than 3 by nextInt(6) + 4.
Your code does not tell us, which value the variable shiplength has. If it's 0 the for-loop will never be entered thus aiclickcount will remain 0 and your break statement is never reached (under the premise that the random value of aifirstclicki is greater than 3).
Try to step through your code with a debugger and let it display the values for the variables to you to find out what's going on.
Your break; is only going to get you out of the second while loop, not the first as it only works on the inner-most loop that it is part of.
Java allows you to specify multi-level breaks, rather than having to complicate your loop conditions:
Breaking out of nested loops in Java

One identifier for multiple values. Is it possible?

i would like to have one identifier responsible for several values for one time. if i had one this is what i could do:
if (myVariable == IDENTIFIER)//instead of if(myVariable == 5 || myVariable == 7) if i need A LOT of values
[myObject doSomething];
Is there a possibility to implement it somehow?
I think the closest you can come is by using bitmasks, so that you represent the set of allowable values with a mask that has all of the values set:
const int ALL_VALUES = (1 << 5) | (1 << 7);
if ((1 << myVariable) & ALL_VALUES)
[myObject doSomething];
Above, bit-wise AND is used to compute the intersection between the current value (seen as a 1-bit mask) and the mask of all allowed values. Note that this will only work if the number of values (and their actual values) is less than the number of bits in an int.
You could have a NSSet of possible values:
NSSet *possibleValues = [NSSet setWithObjects:#"Value1", #"Value2", #"Value3", nil];
if ([possibleValues containsObject:myVariable])
If you need something that works with a raw integer, let me know.
This is what methods are for:
- (BOOL)isFoo(int identifier) {
return identifier == 5 || identifier == 7;
}
Combine the answers. First use a function (variant of grahamparks):
BOOL isFoo(int identifier)
{
...
return ...;
}
For something this simple a function is probably better than a method - calling is a lot quicker and there is no need to ever override. Further if the function is only ever required in the current file declare it static BOOL isFoo... to limit the visibility of isFoo to just the file.
Now pick the body which suits the data - a couple of values, comparisons (grahamparks); more than a few values but all within 0-31 (uint32_t) or 0-63 (uint64_t) consider the bit-mask (unwind); many values all over the range consider sets (Richard J. Ross III); or roll your own. The important point which ever algorithm you choose is isolated within the function and can be changed easily if needed without affecting the rest of your code.
As existing similar examples consider isDigit() et al in the standard C library. Some implementations of these use a pre-allocated arrays of booleans (256 elements as the argument is a character) so testing for membership of the set is just an array index operation.

DBI::sql_type_cast: DBIstcf_DISCARD_STRING - question

My hope was, that DBI::sql_type_cast with the DBIstcf_DISCARD_STRING-flag would modify $sv from '4.8g' to 4.8.
(DBIstcf_DISCARD_STRING:
"If this flag is specified then when the driver successfully casts the bound perl scalar to a non-string type then the string portion of the scalar will be discarded.")
What does the return-value sv could not be case and DBIstcf_STRICT was not used mean?
#!/usr/bin/env perl
use warnings;
use 5.012;
use DBI qw(:sql_types);
my $dsn = "DBI:Proxy:hostname=horst;port=2000;dsn=DBI:ODBC:db1.mdb";
my $dbh = DBI->connect( $dsn, undef, undef, { RaiseError => 1, PrintError => 0 } )
or die $DBI::errstr;
my $sv = '4.8g';
my $sql_type = SQL_DOUBLE;
my $flags = DBIstcf_DISCARD_STRING;
my $sts = DBI::sql_type_cast( $sv, $sql_type, $flags );
say $sts; # 1 (sv could not be case and DBIstcf_STRICT was not used)
say $sv;
# Argument "4.8b" isn't numeric in subroutine entry at ./perl6.pl line 14.
# 1
# 4.8b
The documentation contains a typo -- the description for $sts == 1 should be "sv could not be cast" -- i.e. a cast to SQL_DOUBLE wasn't possible for the value you provided and so nothing was done.
DBIstcf_DISCARD_STRING means something different from what you want. In Perl internal terms it means that if you pass an SV with POK and NOK and PV part "1.23" and NV part 1.23, you will get back an SV with !POK and NOK and NV part 1.23 -- that is, the stored string part of the scalar will be invalidated, leaving the numeric part intact, so any future attempt to use the scalar as a string will force it to be re-converted from a number to a string. But note that it says that this will only happen if the cast is successful, and a cast to SQL_DOUBLE isn't successful if the value isn't a valid number to begin with. "4.8g" doesn't pass the test.
You can clean up the string part of the value almost as effectively as DBI on your own just by doing $sv = 0 + $sv; which will clear POK and force a reconversion to string in the same way. The difference between this and what DBI does is that it's not actually clearing the PV in the way that DBI would, only marking it invalid. To force the value to be cleared immediately in the same way as DBI, you need to do something like
$sv = do { my $tmp = 0 + $sv; undef $sv; $tmp };
but unless you have some really good explanation for why you need that, you don't -- so don't use it. :)
After reading through the documentation and the code in DBI.xs (the implementation is in sql_type_cast_svpv), the return value of 1 means 'the value could not be cast cleanly and DBIstcf_STRICT was not used'.
Taking the key part of that function, in your case:
case SQL_DOUBLE:
sv_2nv(sv);
/* SvNOK should be set but won't if sv is not numeric (in which
* case perl would have warn'd already if -w or warnings are in effect)
*/
cast_ok = SvNOK(sv);
break;
....
if (cast_ok) {
if (flags & DBIstcf_DISCARD_STRING
&& SvNIOK(sv) /* we set a numeric value */
&& SvPVX(sv) /* we have a buffer to discard */
) {
SvOOK_off(sv);
if (SvLEN(sv))
Safefree(SvPVX(sv));
SvPOK_off(sv);
SvPV_set(sv, NULL);
SvLEN_set(sv, 0);
SvCUR_set(sv, 0);
}
}
if (cast_ok)
return 2;
SvNOK should be set for you. Without digging in further into sv_2nv, the core of the problem is that "4.8g" is not a numeric type, as the numeric flag in the scalar value is not set (this is what SvNOK checks for).
My suggestion, use a regular expression to strip that input before calling sql_type_cast.
The typo in the documentation is now fixed in the subversion trunk.
Here is a brief explanation of why sql_type_cast was added.
Although there is nothing to stop you using sql_type_cast it was specifically added for drivers (DBDs) to cast data returned from the database. The original issue it solved was that integers are mostly bound as strings so when the data is returned from the database the scalar's pv is set. Some modules like JSON::XS are clever and look at the pv to help decide if the scalar is a number of not. Without the sql_type_cast JSON::XS was converting a scalar containing a 1 but with the pv set to "1" instead of the shorter 1 in JSON conversions.
To my knowledge only DBD::Oracle does this right now although it is in the TODO for DBD::ODBC.

sprintf fails spontaneously depending on what printf and NSLog calls there are

Hello I have a bizarre problem with sprintf. Here's my code:
void draw_number(int number,int height,int xpos,int ypos){
char string_buffer[5]; //5000 is the maximum score, hence 4 characters plus null character equals 5
printf("Number - %i\n",number);
sprintf(string_buffer,"%i",number); //Get string
printf("String - %s\n",string_buffer);
int y_down = ypos + height;
for (int x = 0; x < 5; x++) {
char character = string_buffer[x];
if(character == NULL){ //Blank characters occur at the end of the number from spintf. Testing with NULL works
break;
}
int x_left = xpos+height*x;
int x_right = x_left+height;
GLfloat vertices[] = {x_left,ypos,x_right,ypos,x_left,y_down,x_right,y_down};
rectangle2d(vertices, number_textures[atoi(strcat(&character,"\0"))], full_texture_texcoords);
}
}
With the printf calls there, the numbers are printed successfully and the numbers are drawn as expected. When I take them away, I can't view the output and compare it, of-course, but the numbers aren't rendering correctly. I assume sprintf breaks somehow.
This also happens with NSLog. Adding NSLog's anywhere in the program can either break or fix the function.
What on earth is going on?
This is using Objective-C with the iOS 4 SDK.
Thank you for any answer.
Well this bit of code is definately odd
char character = string_buffer[x];
...
... strcat(&character,"\0") ...
Originally I was thinking that depending on when there happens to be a NUL terminator on the stack this will clober some peice of memory, and could be causing your problems. However, since you're appending the empty string I don't think it will have any effect.
Perhaps the contents of the stack actually contain numbers that atoi is interpretting?Either way I suggest you fix that and see if it solves your issue.
As to how to fix it Georg Fritzsche beat me to it.
With strcat(&character,"\0") you are trying to use a single character as a character array. This will probably result in atoi() returning completely different values from what you're expecting (as you have no null-termination) or simply crash.
To fix the original approach, you could use proper a zero-terminated string:
char number[] = { string_buffer[x], '\0' };
// ...
... number_textures[atoi(number)] ...
But even easier would be to simply use the following:
... number_textures[character - '0'] ...
Don't use NULL to compare against a character, use '\0' since it's a character you're looking for. Also, your code comment sounds surprised, of course a '\0' will occur at the end of the string, that is how C terminates strings.
If your number is ever larger than 9999, you will have a buffer overflow which can cause unpredicable effects.
When you have that kind of problem, instantly think stack or heap corruption. You should dynamically allocate your buffer with enough size- having it as a fixed size is BEGGING for this kind of trouble. Because you don't check that the number is within the max- if you ever had another bug that caused it to be above the max, you'd get this problem here.