C treats char as a letter, not a one-byte integer - c

So let's say I need to take a number from user input, only from 0 to 9. Everything else is out of bounds.
I figure this number ought to fit within a one-byte integer, a char (0-255). Typically I see people use int here, but that's wasteful, isn't it? 32 bits (or whatever the implementation is) just to store the numbers 0-9?
What I find is that the code below (which uses shorts) works, but when I change it to use only a one-byte char, not a two-byte short, it begins to treat the user input as a string, so if a user enters 235, convert() no longer catches the error condition. It just returns the first digit of their number.
I'm aware that C implements strings as arrays of characters. Is there a way to use char to represent a single one-byte integer without treating it like a string? Should I just abandon the idea of using one-byte integers as numbers?
(I shortened this to test for 0-1, not 0-9, for the sake of brevity)
#include <stdio.h>
char *convert(short digit)
{
char *english;
switch (digit) {
case 0:
english = "zero";
break;
case 1:
english = "one";
break;
default:
english = "";
break;
}
return english;
}
void main(void)
{
short digit;
char *english;
printf("Enter a digit between 0 and 9: ");
scanf("%hu", &digit);
english = convert(digit);
if (english[0] == '\0') {
printf("\nYour number was invalid\n");
} else {
printf("\nYou entered the number %s\n", english);
}
}
Update:
Here's the attempted version that accepts a char, not a short:
#include <stdio.h>
char *convert(char digit)
{
char *english;
switch (digit) {
case 0:
english = "zero";
break;
case 1:
english = "one";
break;
default:
english = "";
break;
}
return english;
}
void main(void)
{
char digit;
char *english;
printf("Enter a digit between 0 and 9: ");
scanf("%c", &digit);
english = convert(digit);
if (english[0] == '\0') {
printf("\nYour number was invalid\n");
} else {
printf("\nYou entered the number %s\n", english);
}
}
The observed behavior of the above is that any number entered is flagged as invalid.

Change scanf("%c", &digit); to scanf("%hhd", &digit);
%c is the specifier to read a character and store that character. %d is the specifier to read a number written as base-10 digits and convert that to an integer (with hh meaning to write the answer into a char).
It is nothing to do with how "C treats a char", it is to do with how you instructed the scanf function to treat its input. A char is a one-byte integer.
Also, your code short digit; scanf("%hu", &digit); is incorrect; %hu is the specifier for unsigned short. Either use %hd or unsigned short digit;.

There is no way to use scanf to input a one byte integer. If you mean your format string is "%c" this is an (ascii) character, not a one byte integer. If you wish to treat it as a one byte integer you need to convert it yourself. c = c - '0'; will work find if a value 0-9 was provided.
If instead your intention is to scan in values within the full 8 bit range, you must scan in at least a short and you can take the lower 8 bits if you wish.

Typically I see people use int here, but that's wasteful, isn't it? 32 bits (or whatever the implementation is) just to store the numbers 0-9?
Using char instead of short won't save space and will be most likely counter productive in most cases.
For function parameters, the compiler will generate code that passes chars as ints (otherwise the stack will become unaligned which generates a fault on most platforms and severly slows down the machine on most others.
For most arithmetic operations on chars, the compiler will be forced to do integer promotion (extend char to int, do the operation and truncate the result).

Related

Output the decimal versions of unknown input

I'm trying to write a short script to take in 6 inputs that will be in decimal, hex or octal and output their decimal versions. For example, if I input 1, 1 should be output. Input 010, should get 8, input 0x20, should get 32. Do I have to test each value to see if scanf reads it as its type or can I cast them after scanf reads them all as is? Also do I need functions to convert the octal and hex values to decimal or can I cast them? I'm very new to C and don't understand it well yet, but here's what I have so far (it outputs -200 as 32765 for whatever reason):
int num[6];
printf("Enter six integers:\n");
int i = 0;
int j;
while (i < 6){
if(scanf("0x%x", &j) == 1){
scanf("0x%x", &num[i]);
//hexToDec(num[i]);
} else if (scanf("0%o", &j) == 1){
scanf("0%o", &num[i]);
//octalToDec(num[i]);
} else {
scanf("%i", &num[i]);
}
i+=1;
}
for(int p = 0; p < 6; p+=1){
printf("%i\n", num[p]);
}
Answer for future reference: simply scanning in with "%i" does the conversions automatically. Revised code below:
int main(){
int num[6];
printf("Enter six integers:\n");
int i = 0;
while (i < 6){
scanf("%i", &num[i]);
i+=1;
}
for(int p = 0; p < 6; p+=1){
printf("%i\n", num[p]);
}
}
You cannot use scanf("0x%x", &j) to test the input and then use scanf("0x%x", &num[i]) to put the value in num[i]. scanf consumes the characters it accepts, so they are no longer in the input stream. When you do the second scanf, the characters are gone.
Instead, just attempt to scan the desired thing. If it works, you are done. If it does not work, go on to an else:
if (scanf("0x%x", &num[i]) == 1)
; // Worked, nothing to do.
else if (scanf("0%o", &num[i]) == 1)
;
else if (scanf("%i", &num[i]) == 1)
;
else
{
// Nothing worked, should put error-handling code here.
}
That is actually not great code for real applications, because scanf will consumes some of the input even if it ultimately fails. For example, with scanf("0x%x", &num[i]), if the input contains “0x” but then contains a non-hexadecimal character, scanf will consume the “0x” but leave the next character. However, it suffices for a learning exercise.
Once you have values in the num array, they are just int values. They are mathematical values, not numerals that are in octal, decimal, or hexadecimal. You can print them with %o for octal, or %d or %i for decimal. The original numeral is irrelevant. When printing, the mathematical value will be used to format a string in the requested base.
You should not use %x for printing an int, as %x is for unsigned int. However, you could convert an int to unsigned int and then print it with %x.
Note that you should not scan to int objects with %x. %x is for scanning to unsigned int objects. You can actually scan hexadecimal, octal, and decimal using:
if (scanf("%i", &num[i]) == 1)
;
else
…
The %i specification will recognize hexadecimal numerals beginning with “0x”, octal numerals beginning with “0”, and decimal numerals.
If you did not want to use %i for all three, because you want direct control for some reason, you will need to write more code. Standard C does not provide a direct way to scan only a hexadecimal numeral to an int. You would need to get the characters from the input and calculate the value from them or scan to an unsigned int and then convert the result to an int.
You can use function strtol with a base of 0 to do auto-detection of the integral value's base according to the input strings format (cf, for example, strtol documentation at cppreference.com):
long strtol( const char *str, char **str_end, int base )
Interprets an integer value in a byte string pointed to by str. ...
The valid integer value consists of the following parts:
(optional) plus or minus sign
(optional) prefix (0) indicating octal base (applies only when the base is 8 or ​0​)
(optional) prefix (0x or 0X) indicating hexadecimal base (applies only when the base is 16 or ​0​)
If the value of base is ​0​, the numeric base is auto-detected.
So a call like long val = strtol("0x10",NULL,0); will yield a decimal value of 16.
Note that you can also use scanf in conjunction with format specifier %i, since this is defined to behave just as a call to strtol:
​int scanf( const char *format, ... )
%i matches an integer. The format of the number is the same as expected by strtol() with the value ​0​ for the base argument (base is
determined by the first characters parsed)
The quick answer is use only
if(scanf("%i", &j) == 1){
...
}
what I have so far (it outputs -200 as 32765 for whatever reason):
Yet error handing of scanf() is troublesome. Far better to read the line of user input with fgets() and then parse the line - perhaps with strtol().

Having Difficulty with isdigit() in C

I have been trying to add some experience in C to my experience with Python and started with a basic addition program. One thing that I'm trying to do is check if the input is a number or a character as seen here:
#include <stdio.h>
#include <ctype.h>
int main()
{
int n, sum=0,c,value;
printf("Enter the Number of Integers You Want to Add\n");
scanf("%d", &n);
if(isdigit(n))
{
printf("Enter %d Integers\n", n);
for(c=1; c<=n; c++)
{
scanf("%d", &value);
if(isalpha(value))
{
printf("ENTER INTEGER NOT CHARACTER\n");
break;
}
else
{
sum = sum + value;
}
}
printf("Sum of Entered Integers = %d\n",sum);
}
else
{
printf("ENTER INTEGER NOT CHARACTER\n");
break;
}
return 0;
}
Initially I had tried this using isalpha(), and the program worked fine when adding numbers but interpreted characters as zeros instead of printing the "not an integer" statement. However, now that I reworked it to use isdigit(), it does not recognize ANY input as an integer, whether or not it is. Is there something that I'm just doing wrong?
When you use scanf to read an integer, you get just that, an integer. (To read a single character, you need %c and a pointer-to-char).
When you use isdigit(), you need to supply the representation of that character (e.g. in ASCII, the character '0' has the representation 48, which is indeed its value as an integer). To recap:
isdigit(0) is false
isdigit(48) is true (for ASCII, ISO8859, UTF-8)
isdigit('0') is true (no matter the character set)
isdigit('0' + n) is true for integers n = 0 ... 9
PS: Not testing the return value from scanf is asking for trouble...
Neither isdigit nor isalpha work as you think they do. The intent of those library functions is to check whether a given code point, represented as an int, is within a subset of points defined by the standard to be digit characters or alpha characters.
You should be checking the results of your scanf calls rather than assuming they just work, and acting on those results accordingly. If you request an integer and one is successfully scanned, then it will tell you so. If that fails, your course of action is probably to consume the rest of the line (through newline or EOF) and possibly try again:
#include <stdio.h>
int main()
{
int n,value,sum=0;
printf("Enter the Number of Integers You Want to Add\n");
if (scanf("%d", &n) == 1 && n > 0)
{
printf("Enter %d Integers\n", n);
while (n--)
{
if (scanf("%d", &value) == 1)
{
sum = sum + value;
}
else
{
// consume the rest of the line. if not EOF, we
// loop around and try again, otherwise break.
while ((value = fgetc(stdin)) != EOF && value != '\n');
if (value == EOF)
break;
++n;
}
}
printf("Sum of Entered Integers = %d\n", sum);
}
return 0;
}
Properly done you should be able to enter valid integers beyond single digits (i.e. values > 10 or < 0), which the above allows.
The %d marker to scanf tells it to interpret the input as a number (more accurately, it indicates that the pointer in the arguments points to an integer type). It can't do anything but put an integer into that argument. If it can't interpret the input as a number, scanf stops scanning the input and returns immediately.
isdigit() evaluates its argument as a character code, as Jens points out above. However, scanf already turned the character code into a pure number.
From the scanf man page:
On success, the function returns the number of items of the argument list
successfully filled.
In your program, you are trying to read just one item from stdin, so scanf should return 1. So check for that and you'll know that it all worked out ok:
printf("Enter the Number of Integers You Want to Add\n");
while(scanf("%d", &n) != 1) {
printf("That's not a valid integer. Please try again.\n");
}
You cannot use isdigit() the way you are using it because you're already using scanf to convert the user input to an integer. If the user had not input an integer, scanf would have already failed.
Look at the man pages for all the C functions you are using, they will show you what the function expects and what the return values will be under different circumstances.
In the case of isdigit(), the input is expected to be an unsigned char representing an ASCII character. This can be a bit confusing because ASCII characters are in fact represented as a type of integer, and a string is an array of those. Unlike languages like Python which hide all that from you. But there is a big difference between the STRING of a number (array of characters that contain the characters of the digits of the number) and the INTEGER itself which is in a form the processor actually uses to do math with... (simplified explanation, but you get the idea).

How to check data type of user input like: int, double and string in c

I am trying to check data type of an user input which can be a int,double, string/char etc. Here is my code:
int main(void)
{
char input[100] = "";
double x;
int num;
char str[20] = "";
int assignments[5] = { 0 };
printf("Pls. provide unput");
fgets(input, 100, stdin);
if (sscanf(input, "%d", &num) == 1)
{
printf("the input is a int.\n");
}
else if (strtod(input, NULL) != 0)
{
printf("the input is a double\n");
}
else if (sscanf(input, "%s", &str) == 1)
{
printf("the input is a string\n");
}
else
{
printf("input not recognized");
}
return 0;
}
but it is not working properly — specially the double part. For any input of double it recognize it as an int.
When you scan char array no need for address "str" instead of "&str", character arrays decay to a pointer.
Now coming to the main issue with why you are having trouble. Remember all integers are also doubles/floats. i.e integers are perfect subset of floats. So if you check for integer/read into int variable first, you will always match any float as float input by user will be truncated to int when reading and hence you will never hit branch checking for double.
The way to fix it is to first test for floating point number.So read input into double, then if it is true you test if it is an integer by casting it to integer and seeing relative difference to see if it is less than some tolerance.
So code that fixes this can be seen below
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
int main()
{
char input[100] = "";
double x;
int num;
char str[20] = "";
int assignment[5] = {0};
double tolerance = 1e-12;
printf("Pls. provide input: ");
fgets(input, 100, stdin);
if (sscanf(input, "%lf", &x) == 1) {
// Is it a number? All integers are also doubles.
num = (int)x; // We cast to int.
if ( fabs(x - num)/x > tolerance ) {
printf("The input is a floating point\n");
} else {
printf("The input is a integer\n");
}
} else if (sscanf(input, "%s", str) == 1) {
// Check if it is string
printf("The input is a string\n");
} else {
// No match error.
printf("input not recognized\n");
}
}
SAMPLE
gcc test.c
Pls. provide input: 3
The input is a integer.
Pls. provide input: 3.3
The input is a floating point.
NOTE You should use something more meaningful like machine precision rather than the tolerance that i show.
No surprises that the code will detect input of a floating point value as an int. For example, given input of 2.3, sscanf()'s %d format will recognise the 2 as an int, and stop when it encounters the decimal point. The decimal point will be there to be read by a subsequent input operation.
Try processing the string in order to check what the string contains. Criteria you could use include looking for non-numeric characters (anything other than digits, decimal points, or sign characters) - if those are found, deem the input to be a string. If only digits and sign characters are found, but no decimal point interpret the string as an int. If digits, sign, and decimal points are found, interpret as a float.
You can also do other techniques, for example, if you want to treat 2.3E32 as a floating point, rather than as a string.
You can tailor the above however you like, depending on your needs.
What ever you do, however, you need to do error checking on every operation (reading, interpreting the string, etc). You are best off assuming the input string contains garbage unless it passes your tests - assuming by default it contains an integer may well get you in trouble with input like 2XYZ.

Splitting a string into integers

For my first task I am using fgets() to read a string from stdin. So I input 1234567890 on the terminal and it stores the string into a variable called str. Now I want to split the digits up and perform addition. In this case the sum would be 55 since 1+2+3+4+5+6+7+8+9+0 = 55.
How can I do that?
My code so far
#include <stdio.h>
#include <string.h>
int main(void){
char str[100];
printf("Please enter the 10 digit number \n");
fgets(str, 10, stdin);
//Separate the digits
//calculate their sum
int sum =..............
//print out the sum
printf("the sum of the digits is: %s", sum);
return(0);
}
Approach 1:
If you know for sure that you're counting for single digit integer, you can use
array indexing to read the digit-by-digit value from the input string.
convert the char value to corresponding int as per the required encoding (mostly, ASCII).
define one int variable , say res to hold the sum. perform the addition and store the result to sum.
loop untill terminating null is reached.
print out the sum using %d format specifier.
Approach 2:
Alternatively, you can do something like (again assuming,single digit integer)
Convert the whole string to integer using strtol(). You must check for the errors in that case.
define one int variable , say res to hold the sum.
perform modulo 10 (%0) on the converted interger value to take out the last digit. add and store that inres`
divide converted interger value by 10 (p /= 10).
continue to step 2 untill the result is 0.
when the converted interger value becomes 0, print the res.
P.S - Just FYI, usual way of splitting a string based on some delimiter is to use strtok().
Since you are sure the string will contain digits, subtract each character from '0' to get their numeric value.
int sum = 0;
fgets(str, sizeof str, stdin);
char *s = &str;
while(*s != '\0') {
sum += *(s++) - '0';
}
printf("the sum of the digits is: %d", sum);
First, make sure that you read only digits by using scanf, like this:
char str[11]; // 10 digits plus \0 terminator
printf("Please enter the 10 digit number \n");
scanf("%10[0-9]", str);
Now you can go through the digits one by one by indexing into str, and subtracting the code of digit zero, like this:
int nextDigit = str[i] - '0';
This should be enough information for you to complete the solution by adding a for loop around it. Make sure that you print sum using %d, not %s like you did in your code sample.

Comparing input character to an array of ints in C

I am kinda new to C and I am having trouble using If statements to compare character input from the user to an int stored in an array.
Eg:
int i[2] = {1,2};
char input[3];
printf("Select option:");
fgets(input,3,stdin);
if(strcmp(*input,i[0]) == 0)
printf("1 chosen");
if(strcmp(*input,i[1]) == 0)
printf("2 chosen");
When compiling I get a warning for both compare statements saying:
warning: passing argument 1 of 'strcmp' makes pointer from integer without cast
warning: passing argument 2 of 'strcmp' makes pointer from integer without cast
I understand that this maybe because Im comparing non-string elements, but how will I cast them then compare them?
When executing I get:
Segmentation fault(core dumped)
Can somebody help?
Nobody yet has explained why what you are doing is incorrect, apart from saying that it is "wrong".
A string in C is just a bunch of consecutive characters in memory, where the last character in the string has a value of zero. You can store a string in a char array, or you can point to somewhere in memory by using a char pointer (char*).
When you input a decimal number, you are reading individual characters that happen to be in the range '0' through to '9', maybe prefixed by an optional '-'. You read these as a string, and if you want to treat them as integers you need to convert them to an integer data type (instead of a series of char values).
That's where something like atoi helps, although it is no longer fashionable. You should use strtol. [There is a whole family of these functions for dealing with unsigned, long long, or combinations thereof, as well as double and float types].
That tackles roughly half of your question. Now, you are using strcmp and expecting it to work. There are a couple of things wrong with what you are doing. The major error is that you can't treat an integer as a string. If you really want to use string comparison, you have to convert the integer to a string. That means you do the reverse of what strtol does.
That's a bigger discussion, and in your case it is not the correct way so I won't go into it. But I'd like to point out that, all things being equal, you are sending the wrong types to strcmp. It expects two char* pointers (const char *, really). What you have done is dereferenced your input pointer to a char for the first element, and then pass an int for the second.
strcmp(*input,i[0])
A pointer is basically just a number. It gives the memory address of some data. In this case, the data is expected to be char type (single bytes). The strcmp function is expecting valid memory addresses (stuff that's actually in your stack or heap). But what you give it is *input (the value of the first character in your input string) and i[0] (the number 1).
Those compiler warnings were telling you something quite important, you know! =)
So, just for completeness (although others have answered this already), you should forget the string comparisons (and make a mental note to learn more about strings in C), and do this:
int value = strtol( input, NULL, 10 );
if( value == i[0] )
printf("1 chosen");
if( value == i[1] )
printf("2 chosen");
There are other ways to go about this. I could talk about how to convert single-digit numbers from a string, but I think I have already ranted for long enough. Hope this is of some help.
Apart from the various methods listed as answers to your qn.
Why don't you take the user input as int??
#include<stdio.h>
int main()
{
int i[2] = {1,2};
int input;
printf("Select option:");
scanf("%d",&input);
if(input==i[0])
printf("1 chosen");
if(input==i[1])
printf("2 chosen");
return 0;
}
You must use scanf instead of fgets:
int input;
printf("Select option:");
scanf("%d", &input);
if (input == i[0])
etcetera.
See http://en.wikibooks.org/wiki/C_Programming/Simple_input_and_output#Input_using_scanf.28.29
Better convert input char to int using atoi function.
http://www.cplusplus.com/reference/cstdlib/atoi/
int i[2] = {1,2};
char input[3];
int opt=0;
printf("Select option:");
fgets(input,3,stdin);
opt = atoi(input);
if(opt== i[0])
printf("1 chosen");
if(opt == i[1])
printf("2 chosen");
You need to compare the input string vs. some known strings. But you're comparing the first character vs. some ints. strcmp() will do what you need if you pass it the right arguments: two strings.
here you are trying to compare a string to an integer using strcmp:
if(strcmp(*input,i[0]) == 0)
that won't work.
you could do that way:
const char *numbers[2] = {"1", "2"};
char input[3];
printf("Select option:");
fgets(input,3,stdin);
if(strcmp(*input, numbers[0]) == 0)
printf("1 chosen");
if(strcmp(*input, numbers[1]) == 0)
printf("2 chosen");
where you compare two strings, instead of comparing a number to a string
or you could convert the input string to an int using sscanf or atoi
int i[2] = {1,2};
char input[3];
int num;
printf("Select option:");
fgets(input,3,stdin);
sscanf(input, "%d", &num);
if(num == i[0])
printf("1 chosen");
else if(num == i[1])
printf("2 chosen");
i didn't compile them though, maybe there's sth which i am missing but that's the general idea
Use atoi. it will convert your string to integer
int i[2] = {1,2};
char input[3];
printf("Select option:");
fgets(input,3,stdin);
if(atoi(input)==i[0]))
printf("1 chosen");
if(atoi(input)==i[1]))
printf("2 chosen");

Resources