How to use isdigit function in C - c

I'm a complete beginner doing the cs50 course and I need to check if an argument from a user is a digit or not.
this is the code:
#include <stdio.h>
#include <cs50.h>
#include <ctype.h>
int main(void)
{
int i = 1;
if (isdigit(i) == 1)
{
printf("i is a digit");
}
else if (isdigit(i) == 0)
{
printf("i is not a digit");
}
return 0;
}
When I run this code I basically get a reverse of what I should be getting. When i is a number it prints out "i is not a number" and visa versa.
What am I doing wrong? I thought isdigit returns a non-zero value if it is a digit and 0 if not. Basically 1 being true and 0 being false. Is this not correct?
Much appreciated, Thanks!

What am I doing wrong?
"The isdigit function tests for any decimal-digit character". i with a value of 1 is not a digit character.
Try i = '1';. Then i will have the value of a digit character.
Code is testing the return value incorrectly. #tadman. is...() returns 0 or non-zero.
// if (isdigit(i) == 1)
if (isdigit(i))
Note: is...(int ch) functions are only valid for ch in the unsigned char range and EOF.

If you read the documentation for isdigit() you'll note the return value is expressed as:
Non-zero value if the character is a numeric character, zero otherwise.
In other words, don't compare to exactly one, that's not assured. Compare to non-zero.
That being said, this works on characters not integers, although in C the line is blurred. What you want is to ensure this is part of a string, like:
char* n = "12345";
if (isdigit(n[0]) == 0) {
...
}
In your case you're asking if ASCII character 1 is a digit, which it is not. That's the "Start of Heading" (SOH) control character.

isdigit is a character classification function.
It will return zero or non-zero depending on if a character (or rather, a character promoted to an int) is a digit.
For example:
int i = '1'; // Initialize to the character '1'
if (isdigit(i))
{
printf("i is a digit");
}
else
{
printf("i is not a digit");
}
Note that isdigit returns a non-zero value for digit characters, it doesn't have to be 1.

The argument to isdigit should be a single character, not a number. Thus your test code should more properly be
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char c = '1';
if(isdigit(c))
printf("c is a digit");
else
printf("c is not a digit");
return 0;
}

Your code should stick to the definition of the function, and not make assumptions about the returned value if it is a digit - the actual returned value may be implementation dependent, here's a better C-style, though as others have have said, you should be passing in a char, not an int - did the compiler not complain about that?
if (isdigit(i)) {
printf("i is a digit");
} else {
printf("i is not a digit");
}

Related

How to check an edge case in taking command line argument in C and evaluating to int or double?

So I have an assignment to figure out whether a number on the command line is either an integer or a double.
I have it mostly figured it out by doing:
sscanf(argv[x], "%lf", &d)
Where "d" is a double. I then cast it to an int and then subtract "d" with itself to check to see if it is 0.0 as such.
d - (int)d == 0.0
My problem is if the command line arguments contains doubles that can be technically classified as ints.
I need to classify 3.0 as a double whereas my solution considers it an int.
For example initializing the program.
a.out 3.0
I need it to print out
"3.0 is a double"
However right now it becomes
"3 is an int."
What would be a way to check for this? I did look around for similar problems which led me to the current solution but just this one edge case I do not know how to account for.
Thank you.
For example, a way like this:
#include <stdio.h>
int main(int argc, char *argv[]){
if(argc != 2){
puts("Need an argument!");
return -1;
}
int int_v, read_len = 0;
double double_v;
printf("'%s' is ", argv[1]);
//==1 : It was able to read normally.
//!argv[1][read_len] : It used all the argument strings.
if(sscanf(argv[1], "%d%n", &int_v, &read_len) == 1 && !argv[1][read_len])
puts("an int.");
else if(sscanf(argv[1], "%lf%n", &double_v, &read_len) == 1 && !argv[1][read_len])
puts("a double.");
else
puts("isn't the expected input.");
}
To test if a string will covert to a int and/or double (completely, without integer overflow, without undefined behavior), call strtol()/strtod(). #Tom Karzes
The trouble with a sscanf() approach is that the result is undefined behavior (UB) on overflow. To properly detect, use strtol()/strtod().
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>
bool is_int(const char *src) {
char *endptr;
// Clear, so it may be tested after strtol().
errno = 0;
// Using 0 here allows 0x1234, octal 0123 and decimal 1234.
// or use 10 to allow only decimal text.
long num = strtol(src, &endptr, 0 /* or 10 */);
#if LONG_MIN < INT_MIN || LONG_MAX > INT_MAX
if (num < INT_MIN || num > INT_MAX) {
errno = ERANGE;
}
#endif
return !errno && endptr > src && *endptr == '\0';
}
bool is_double(const char *src) {
char *endptr;
// Clear, so it may be tested after strtod().
strtod(src, &endptr);
// In this case, detecting over/underflow IMO is not a concern.
return endptr > src && *endptr == '\0';
}
It is not entirely clear what the specific expectations are for your program, but it has at least something to do with the form of the input, since "3.0" must be classified as a double. If the form is all it should care about, then you should not try to convert the argument strings to numbers at all, for then you will run into trouble with unrepresentable values. In that case, you should analyze the character sequence of the argument to see whether it matches the pattern of an integer, and if not, whether it matches the pattern of a floating-point number.
For example:
int main(int argc, char *argv[]) {
for (int arg_num = 1; arg_num < argc; arg_num++) {
char *arg = argv[arg_num];
int i = (arg[0] == '-' || arg[0] == '+') ? 1 : 0; // skip any leading sign
// scan through all the decimal digits
while(isdigit(arg[i])) {
++i;
}
printf("Argument %d is %s.\n", arg_num, arg[i] ? "floating-point" : "integer");
}
}
That makes several assumptions, chief among them:
the question is strictly about form, so that the properties of your system's built-in data types (such as int and double) are not relevant.
each argument will have the form of either an integer or a floating-point number, so that eliminating "integer" as a possibility leaves "floating-point" as the only alternative. If "neither" is a possibility that must also be accommodated, then you'll also need to compare the inputs that do not have integer form to a pattern for floating-point numbers, too.
only decimal (or smaller radix) integers need be accommodated -- not, for example, hexadecimal inputs.
Under those assumptions, particularly the first, it is not just unnecessary but counterproductive to attempt to convert the arguments to one of the built-in numeric data types, because you would then come to the wrong conclusion about arguments that, say, are not within the bounds of representable values for those types.
For example, consider how the program should classify "9000000000". It has the form of an integer, but supposing that your system's int type has 31 value bits, that type cannot accommodate a value as large as the one the string represents.
int main (int argc,char *argv[])
{
if(argc==2)
{
int i;
double d;
d=atof(argv[1]);
i=atoi(argv[1]);
if(d!=i)
printf("%s is a double.",argv[1]);
else if(d==i)
printf("%s is an int.",argv[1]);
}
else
printf("Invalid input\n");
return 0;
}
You must add #include <stdlib.h>

Validation of infinite input char \ number

I need to get a valid number from the user between 0-9 without duplicates.
The valid number can have any number of digit, from 1 to 10.
If the user type "space" or any kind of char, then the input is invalid.
My algorithm :
1) Create an array of char in size of 10, then initialize all cells to '0'.
2) For every char that reads from the user, check if the char actually between 0-9.
2.1) If true: count the respectively cell number +1.
2.2) Else "error".
2.3) If I get to a cell that already has +1, means this number already exist, then "error".
Now a few questions about my idea:
1) Is there any better\easy algorithm to do that?
2) The user doesn't type char by char, means I can get an infinite char length, so where do I store everything?
The answer to 2) is: you don't store the characters at all, you process them one by one. You only need storage to remember which digits you have already seen. I'd do it like this:
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char seen[10] = { 0 };
int c, loops;
for (loops = 0; (c = getchar()) != EOF && loops < 10; ++loops)
{
if (!isdigit(c)) {
printf ("Not a digit: %c\n", c);
break;
}
c -= '0';
if (seen[c]) {
printf ("Already seen: %d\n", c);
break;
}
seen[c] = 1;
}
return 0;
}
Try to modify this program as an exercise: reduce the storage requirements of the seen[] array. As written it uses one byte per digit. Make the program use only one bit per digit.

Unexpected Results Using isdigit(x)

I am using the following code. I expect the output to be "Yes", but I instead get "No." I must be missing something very simple and fundamental.
#include <stdio.h>
#include <ctype.h>
int main(void)
{
int n = 3;
if (isdigit(n))
{
printf("Yes\n");
}
else
{
printf("No\n");
}
return 0;
}
isdigit checks whether the character passed to it is a numeric character. Therefore, its argument should be char type or int type which is the code of a character.
Here, you are passing 3 to isdigit. In ASCII, 3 is the code of the character ETX (end of text) which is a non-numeric character. Therefore, isdigit(3) returns false.
isdigit() expects a character code, while you expect it to accept a plain number.
Those expectations do not mesh well.
Character literals:
'0' ordinal 0x30 -- 48
'1' ordinal 0x31 -- 49
'2' ordinal 0x32 -- 50
... You get the drift
'3' is not a digit, here, in the way isdigit() considers them. Change int n = 3 to int n = '3'. If your system uses ASCII, for instance, 3 is the end of text marker, whereas 51, equivalent to '3', is the actual character three.
isdigit expects a character. If the character passed is digit then it returns non zero. 3 is an ASCII value of non-printable character, i.e it doesn't represent a digit and that's why your else body gets executed.
man isdigit:
These functions check whether c, which must have the value of an
unsigned char or EOF, falls into a certain character class according to
the current locale.
3 is an integer, not a character. In fact, 3 is also the value of a character that is not a digit.
The string "\03\t\nABCabc123" is composed of 12 characters. The first 9 characters all return false when applied to the isdigit() function. The last 3 all return true;
#include <ctype.h>
#include <stdio.h>
int main(void) {
char data[] = "\03\t\nABCabc123";
// data[0] = 3;
char *ptr = data;
while (*ptr) {
printf("%4d --> %s\n", *ptr, isdigit((unsigned char)*ptr) ? "true" : "false");
ptr++;
}
return 0;
}
Here is the implementation of function isdigit:
int isdigit(char c)
{
return '0' <= c && c <= '9';
}
In simple words, function isdigit takes a char argument as input:
If the input represents a decimal digit in ASCII format, then the function returns 1.
If the input does not represent a decimal digit in ASCII format, then the function returns 0.
When you call function isdigit with an int argument, the argument is first truncated to a char. That doesn't make any difference in your example, since 3 fits into a char, so no information is lost due to the truncation. However, since 3 does not represent any decimal digit in ASCII format, the return-value of isdigit(3) is 0.
To summarize the above:
The return-value of isdigit('0') is 1.
The return-value of isdigit('1') is 1.
...
The return-value of isdigit('9') is 1.
In all other cases, the-return value of isdigit(...) is 0.
Your mistake probably stems from the assumption that '3' == 3. If you want to check whether or not an int variable stores a single-decimal-digit value, then you can implement a slightly different function:
int isintdigit(int i)
{
return 0 <= i && i <= 9;
}

Cesar Cipher out of bounds in Array

Guys i found this caesar cipher code in some site.... But when i run it its showing segmentation fault in online compilers..but in c compiler tat i'm using its showing processor fault... can anyone pls point out the wrong in this code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define caesar(x) rot(13, x)
#define decaesar(x) rot(13, x)
#define decrypt_rot(x, y) rot((26-x), y)
void rot(int c, char *str)
{
int l = strlen(str);
const char *alpha[2] = { "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
int i;
for (i = 0; i < l; i++)
{
if (!isalpha(str[i]))
continue;
str[i] = alpha[isupper(str[i])][((int)(tolower(str[i])-'a')+c)%26];
}
}
int main()
{
char str[] = "This is a top secret text message!";
printf("Original: %s\n", str);
caesar(str);
printf("Encrypted: %s\n", str);
decaesar(str);
printf("Decrypted: %s\n", str);
return 0;
}
Your code is correct except for one detail, which might be confusing for a newbie.
It is ok to assume that the function isupper() returns a boolean value 1 or 0, but if you check the documentation it says A value different from zero (i.e., true) if indeed c is an uppercase alphabetic letter. Zero (i.e., false) otherwise.
This and the fact that isupper() is returning an int and not a _Bool is causing the problem.
int isupper ( int c );
When returning true isupper() might return any non_zero value. In my case it returns 8 ( a specific bit-field ). Probably the same in yours.
All you have to do is cast the return of isupper() to _Bool
printf("%d %d" ,(_Bool)isupper('A') , ((int)(tolower(str[i])-'a')+c)%26 ) ;
The problem is most likely your use of isupper as index. It's not guaranteed to return 1 for true values, just that it will return "true" (which may be any non-zero value).
You can solve this by using the ternary expression:
alpha[isupper(str[i]) ? 1 : 0][...]
In the first iteration of your loop, you are trying to write to alpha[256][6], (isupper does not always return 0 or 1)
isupper returns: A value different from zero (i.e., true) if indeed c is an
uppercase alphabetic letter. Zero (i.e., false) otherwise.
alpha[isupper(str[i]) != 0][...] will do the trick
Also note that you should always cast the argument you pass to
isupper(), isalnum(), etc. to unsigned char

Converting ASCII code to a character value

I've just started learning how to program in C and I'm trying to make a program that accepts a number and uses it as an ASCII value to return the ASCII character associated with that value.
The program works when the parameters are predefined but when I introduce the scanf function it compiles but doesnt give me the same results.
Here is my code :
#include <stdio.h>
int main(void)
{
question2();
return 0;
}
int question2(void)
{
int myInt = 65;
scanf("%d", myInt);
char ch = myInt;
printf("%c",ch);
return 0;
}
Cheers and thanks for any help guys.
You need to pass the address of myInt to scanf() (the compiler should have emitted a warning for this):
scanf("%d", &myInt);
You should also check the return value of scanf() to ensure myInt was actually assigned to. scanf() returns the number of assignments made, which in this case is expected to be 1:
if (1 == scanf("%d", &myInt))
{
}
Note that int has a larger range values than a char so you should check that the value stored in myInt will fit into a char. There are macros defined in the header limits.h that you can use to check:
if (1 == scanf("%d", &myInt))
{
if (myInt >= CHAR_MIN && myInt <= CHAR_MAX)
{
printf("%c\n", (char) myInt);
}
else
{
printf("%d out-of-range: min=%d, max=%d\n",
myInt, CHAR_MIN, CHAR_MAX);
}
}
The compiler should have also emitted an implicit function declaration warning with respect to question2(). To correct, place the definition of question2(), or a declaration for question2(), prior to main().

Resources