Why do I get this unexpected result using atoi() in C? - c

I don't understand the results of the following C code.
main()
{
char s[] = "AAA";
advanceString(s);
}
void advanceString(p[3])
{
int val = atoi(p);
printf("The atoi val is %d\n",val);
}
Here the atoi value is shown as 0, but I could not figure out the exact reason.
As per my understanding, it should be the summation of decimal equivalent of each values in the array? Please correct me if I am wrong.

atoi() converts a string representation of an integer into its value. It will not convert arbitrary characters into their decimal value. For instance:
int main(void)
{
const char *string="12345";
printf("The value of %s is %d\n", string, atoi(string));
return 0;
}
There's nothing in the standard C library that will convert "A" to 65 or "Z" to 90, you'd need to write that yourself, specifically for whatever charset you're expecting as input.
Now that you know what atoi() does, please don't use it to deal with numeric input in whatever you come up with. You really should deal with input not being what you expect. Hmm, what happens when I enter 65 instead of A? Teachers love to break things.
atoi() doesn't do any error checking whatsoever, which makes anything relying on it to convert arbitrary input fragile, at best. Instead, use strtol() (POSIX centric example):
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(void)
{
static const char *input ="123abc";
char *garbage = NULL;
long value = 0;
errno = 0;
value = strtol(input, &garbage, 0);
switch (errno) {
case ERANGE:
printf("The data could not be represented.\n");
return 1;
// host-specific (GNU/Linux in my case)
case EINVAL:
printf("Unsupported base / radix.\n");
return 1;
}
printf("The value is %ld, leftover garbage in the string is %s\n",
// Again, host-specific, avoid trying to print NULL.
value, garbage == NULL ? "N/A" : garbage);
return 0;
}
When run, this gives:
The value is 123, leftover garbage in
the string is abc
If you don't care about saving / examining the garbage, you can set the second argument to NULL. There is no need to free(garbage). Also note, if you pass 0 as the third argument, it's assumed the input is the desired value of a decimal, hex or octal representation. If you need a radix of 10, use 10 - it will fail if the input is not as you expect.
You'd also check the return value for the maximum and minimum value a long int can handle. However, if either are returned to indicate an error, errno is set. An exercise for the reader is to change *input from 123abc to abc123.
It's important to check the return, as your example shows what happens if you don't. AbcDeFg is not a string representation of an integer, and you need to deal with that in your function.
For your implementation, the most basic advice I can give you would be a series of switches, something like:
// signed, since a return value of 0 is acceptable (NULL), -1
// means failure
int ascii_to_ascii_val(const char *in)
{
switch(in) {
// 64 other cases before 'A'
case 'A':
return 65;
// keep going from here
default:
return -1; // failure
}
.. then just run that in a loop.
Or, pre-populate a dictionary that a lookup function could scope (better). You wouldn't need hashes, just a key -> value store since you know what it's going to contain in advance, where the standard ASCII characters are keys, and their corresponding identifiers are values.

It tries to convert the string into an integer. Since AAA cannot be converted into an integer the value is 0. Try giving it 42 or something.
If no valid conversion could be
performed, a zero value is returned.
See http://www.cplusplus.com/reference/clibrary/cstdlib/atoi/

Read atoi() as a to i (ASCII to integer).
atoi() converts a string representing a decimal number, to integer.
char s[] = "42";
int num = atoi(s); //The value of num is 42.

atoi expects its argument to be a string representation of a decimal (base-10) integer constant; AAA is not a valid decimal integer constant, so atoi returns 0 because it has no other way to indicate that the input is invalid.
Note that atoi will convert up to the first character that isn't part of a valid integer constant; in other words, "123" and "123w" will both be converted to 123.
Like everyone else is saying, don't use atoi; use strtol instead.

Related

CS50 Pset 2- ceasar cipher

It isn't showing what I want it to show that is the ciphered version of the input text but instead symbols, as I guess, looks kinda like a '?' comes out as the output in the terminal. could anyone help me in finding what I missed or did wrong?
#include <cs50.h>
#include <stdio.h>
#include <string.h>
int main(int argc, string argv[])
{
if (argc == 2)
{
string ptext = get_string("plaintext: ");
int key = (int) argv[1];
printf("ciphertext: ");
for (int i = 0, n = strlen(ptext); i < n; i++)
{
printf("%c", (( ptext[i] + key ) % 26);
}
printf("\n");
}
else
{
printf("Invalid input. \n");
}
}
I expect the output of 'hello' to be 'ifmmp' but instead, it doesn't.
This code is wrong:
int key = (int) argv[1];
argv is an array of string, which in CS50 is nothing more than an obfuscated char * pointer.
Per 5.1.2.2.1 Program startup of the C standard:
The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:
int main(void) { /* ... */ }
or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):
int main(int argc, char *argv[]) { /* ... */ }
or equivalent; ...
So argv[1] is a char * pointer value, which you then assign to an int value. That's taking the address of some memory (say the value in argv[1] is 0xFF0403220020480C) and trying to stuff it into the likely 4 bytes of the int variable key (which in this case would be assigned the truncated value 0x0020480C.)
That's not what you appear to be trying to do.
(IMO, your problem here is a perfect example of why CS50's obfuscation of char * with the string type is a tremendously bad idea. You simply can't understand C without understanding pointers and NUL-terminated char strings accessed via a char * pointer, and the obfuscation that string does makes that harder.)
If you want to convert a string to a numeric value, you likely want something like strtol() (never use atoi() as it has no error checking and its use can invoke undefined behavior):
char firstCharNotConverted;
// set errno to zero as strtol()
errno = 0;
long key = strtol( argv[ 1 ], &firstCharNotConverted, 0 );
// if errno is now non-zero, the call to strtol() failed (per Linux man page)
// need to examine key and the contents of firstCharNotConverted
// to figure out why
if ( errno != 0 )
{
...
}
Proper headers omitted as an exercise for anyone trying to use this code ;-)
Note that I used long for key, as you can't do complete and proper error checking on strtol() if you cast the return value to int.
Error checking strtol() can be somewhat complex as the value returned (and assigned to key in the above code) can be any value and there are no values possible that aren't legitimate long values that strtol() can return to indicate an error, so for proper error checking you need to check the values of both errno and firstCharNotConverted to properly determine if an error did occur. The Linux man page states:
Since strtol() can legitimately return 0, LONG_MAX, or LONG_MIN
(LLONG_MAX or LLONG_MIN for strtoll()) on both success and failure,
the calling program should set errno to 0 before the call, and then
determine if an error occurred by checking whether errno has a
nonzero value after the call.
After that call to strtol(), you need to check if key is LONG_MIN, or LONG_MAX with errno equal to ERANGE for underflow or overflow, or if key is 0 you need to check the contents of firstCharNotConverted to determine why the conversion may have failed. Note that if key is zero and firstCharNotConverted isn't equal to argv[ 1 ], then input string was properly converted from zero.
Your Ceaser cipher implementation is also wrong:
for (int i = 0, n = strlen(ptext); i < n; i++)
{
printf("%c", (( ptext[i] + key ) % 26);
}
printf("\n");
will just print out characters with values from 0 through 25 - which aren't letters in the ASCII character set.
There a numerous Ceaser ciphers questions already posted here, so I'm not going to write the code. See Caesar's Cipher Code for one example question.
The problem is here printf("%c", (( ptext[i] + key ) % 26);. Specifically with % 26. It certainly looks exactly like the problem set:
More formally, if p is some plaintext (i.e., an unencrypted message),
pi is the ith character in p, and k is a secret key (i.e., a
non-negative integer), then each letter, ci, in the ciphertext, c, is
computed as
ci = (pi + k) % 26
wherein % 26 here means “remainder when dividing by 26.”
But, the pset goes on to say:
think of A (or a) as 0, B (or b) as 1, …, H (or h) as 7, I (or i) as
8, …, and Z (or z) as 25.
The problem is that the character ptext[i] is the ascii value of the letter, not the "alphabet index".
Perhaps review the spoiler in the pseudocode section of the Lab, particularly #5:
Iterate over each character of the plaintext:
If it is an uppercase letter, rotate it, preserving case, then print out the rotated character
If it is a lowercase letter, rotate it, preserving case, then print out the rotated character
If it is neither, print out the character as is
You may find this walkthrough (from an earlier iteration of the course) helpful.

atoi ignores a letter in the string to convert

I'm using atoi to convert a string integer value into integer.
But first I wanted to test different cases of the function so I have used the following code
#include <stdio.h>
int main(void)
{
char *a ="01e";
char *b = "0e1";
char *c= "e01";
int e=0,f=0,g=0;
e=atoi(a);
f=atoi(b);
g=atoi(c);
printf("e= %d f= %d g=%d ",e,f,g);
return 0;
}
this code returns e= 1 f= 0 g=0
I don't get why it returns 1 for "01e"
that's because atoi is an unsafe and obsolete function to parse integers.
It parses & stops when a non-digit is encountered, even if the text is globally not a number.
If the first encountered char is not a space or a digit (or a plus/minus sign), it just returns 0
Good luck figuring out if user input is valid with those (at least scanf-type functions are able to return 0 or 1 whether the string cannot be parsed at all as an integer, even if they have the same behaviour with strings starting with integers) ...
It's safer to use functions such as strtol which checks that the whole string is a number, and are even able to tell you from which character it is invalid when parsing with the proper options set.
Example of usage:
const char *string_as_number = "01e";
char *temp;
long value = strtol(string_as_number,&temp,10); // using base 10
if (temp != string_as_number && *temp == '\0')
{
// okay, string is not empty (or not only spaces) & properly parsed till the end as an integer number: we can trust "value"
}
else
{
printf("Cannot parse string: junk chars found at %s\n",temp);
}
You are missing an opportunity: Write your own atoi. Call it Input2Integer or something other than atoi.
int Input2Integer( Str )
Note, you have a pointer to a string and you will need to establish when to start, how to calculate the result and when to end.
First: Set return value to zero.
Second: Loop over string while it is not null '\0'.
Third: return when the input character is not a valid digit.
Fourth: modify the return value based on the valid input character.
Then come back and explain why atoi works the way it does. You will learn. We will smile.

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;
}

how to get first word in string and convert to int

I have a char array, and I want to get the first number from it.
e.g if my char array is 34 400 43 33. I want 34 as in int.
int FirstInt(char chars[])
{
return atoi(chars.substr(0, bursts.find(' ')));
}
I was thinkign something like this but it is not valid. ANy ideas?
int FirstInt(char chars[])
{
int x;
sscanf(chars, "%d", &x);
return x;
}
You don't need to tokenize the string or use sscanf if all you want is the first number:
return atoi(str);
From the man page
The atoi() function converts the initial portion of the string pointed
to by nptr to int
Which means it will stop when it finds a non-numeric character, like a white space.
Edit:
Note that it's impossible to detect errors with atoi, since it returns 0 on error in some implementations and doesn't set errno (AFAIK), so it's probably better to use strtol
See this link Converting Strings to Numbers

Getting a hex value from a "0x4000" (for example) string and storing it in a variable.

Here's my issue:
I have written a function to detect if a string is hex based off of the "0x####" format:
int lc3_hashex(char *str)
{
int val = 0;
char *to;
to = strndup(str+2, 10);
val = sscanf(to, "%x", &val);
if (val)
{
return val;
}
return 0;
}
Assuming the parameter is of the form "0x####", it returns the decimal version of the post "0x" numbers. But is there any built in way (or a way I am just overseeing) to get the integer value of the hexidecimal number "0x4000" as opposed to the integer value of "4000"?
Thanks.
You can reduce that function to:
int cnt = sscanf(str, "%x", &val);
if (cnt == 1) {
// read a valid `0xNNNN` string
}
scanf with the %x format already does the hex conversion, and deals with the 0x prefix just fine. Also, it's return value is the number of items matched, so you can use that to determine if it did find a hex value in str or not.
With this you have both pieces of information you need: whether or not the string was formatted as you expected it, and what value it was (properly converted). It also avoids a string allocation (which you're not freeing), and the bug your code has if strlen(str) is less than two.
If you change your function signature to:
int check_and_get_hex(const char *str, int *val);
(or something like that), update the sscanf call accordingly (passing val rather than &val), and return (cnt == 1), you can get both the "it's a valid hex string" and the value to the caller in a single shot.
Use strtol from stdlib.h and specify the base as 16.
The only downside is that this function returns 0 upon failure, so you'll want to check to make sure the input to it is not 0.
I fail to understand why the string is being cut short before doing sscanf. If you want the string in hex value to be converted to the decimal integer, you can give it directly.
#include<stdio.h>
int main()
{
char sHex[7] = "0x2002";
int nVal = 0;
sscanf( sHex, "%x", &nVal );
printf( "%d", nVal );
return 0;
}
This will print 8194, the decimal value for 0x2002. By giving "%x" to sscanf, you are specifying the input string as hexadecimal. so, the preceding "0x" is fine.

Resources