Understanding atoi(var-1) versus atoi(var)-1? - c

I had an issue where my C program allocated input data correctly only for values less than 5. I found the error in the creation of the int array holding the values: I had used atoi(var-1) instead of atoi(var)-1.
When var='5', atoi(var-1) is 0 when printed out. Why is the number "5" where the erroneous char to int conversion breaks? And why does it become zero at that point?
I'm just curious about what actually happens with this.

When you write atoi(var - 1), where var is a char*, you are asking the function atoi to read the string which begins at the memory location one lower than var and convert that to an integer.
In general, the character that is at the lower memory address could be anything. You just happened to have it break when your char* was '5', but it could have happened anywhere.
On the other hand atoi(var) - 1 does exactly what you would expect, converting var to an int and then subtracting 1 numerically.

Pointer arithmetic. If var is a string (char *), then var + n is the substring starting at offset n.
const char* s = "12345":
printf("%d\n", atoi(s + 2)); // prints 345
Subtraction is allowed as well: var - 1 is a pointer to one character before the string. This may be anything, but is probably a non-digit character, so atoi returns 0.

Related

Char string length not getting initialized properly despite literally putting in the integer size I want it to be?

I'm working with char arrays in C. I'm setting the size in a previous step. When I print it out it clearly shows the num_digits as 1.
But then when I put it in to set the size of a char array to make it a char array of size num_digits, its setting the size of the array as 6.
In the next step when I print strlen(number_array), it prints 6. Printing it out I get something with a lot of question marks. Does anyone know why this is happening?
int num_digits = get_num_digits(number);
printf("Num digits are %d\n", num_digits);
char number_array[num_digits];
printf("String len of array: %d\n", strlen(number_array));
You need to null terminate your array.
char number_array[num_digits + 1];
number_array[num_digits] = '\0';
Without this null terminator, C has no way of know when you've reached the end of the array.
just use 'sizeof' instead of 'strlen'
printf("String len of array: %d\n", sizeof(number_array));
There are a couple possible issues I see here:
As noted in Michael Bianconi's answer, C character arrays (often called strings) require null terminators. You would explicitly set this this with something like:
number_array[number + 1] = '\0'; /* See below for why number + 1 */
Rather than just setting the last element to null, pre-initializing the entire character array to nulls might be helpful. Some compilers may do this for you, but if not you'll need to do this explicitly with something like:
for (int i = 0; i < num_digits + 1; i ++) number_array[i] = '\0';
Note that with gcc I had to use C99 mode using -std=c99 to get this to compile, as the compiler didn't like the initialization within the for statement.
Also, the code presented sets the length of the character array to be the same length as number's length. We don't know what get_num_digits returns, but if it returns the actual number of significant digits in an integer, this will come up one short (see above and other answer), as you need an extra character for the null terminator. An example: if the number is 123456 and get_number_digits returns 6, you would would need to set the length of number_array to 7, instead of 6 (i.e. number + 1).
char number_array[num_digits]; allocates some space for a string. It's an array of num_digits characters. Strings in C are represented as an array of characters, with a null byte at the end. (A null byte has the value zero, not to be confused with the digit character '0'.) So this array has room for a string of up to num_digits - 1 characters.
sizeof(number_array) gives you the array storage size. That's the total amount of space you have for a string plus its null terminator. At any given time, the array can contain a string of any length up to number_array - 1, or it might not contain a string at all if the array doesn't contain a null terminator.
strlen(number_array) gives you the length of the string contained in the array. If the array doesn't contain a null terminator, this call may return a garbage value or crash your program (or make demons fly out of your nose, but most computers fortunately lack the requisite hardware).
Since you haven't initialized number_array, it contains whatever happened to be there in memory before. Depending on how your system works, this may or may not vary from one execution of the program to the next, and this certainly does vary depending on what the program has been doing and on the compiler and operating system.
What you need to do is:
Give the array enough room for the null terminator.
Initialize the array to an empty string by making setting the first character to zero.
Optionally, initialize the whole array to zero. This is not necessary, but it may simplify further work with the array.
Use %zu rather than %d to print a size. %d is for an int, but sizeof and strlen return a size_t, which depending on your system may or may not be the same size of integers.
char number_array[num_digits + 1];
number_array[0] = 0; // or memset(number_array, 0, sizeof(number_array));
printf("Storage size of array: %zu\n", sizeof(number_array));
printf("The array contains an empty string: length=%zu\n", strlen(number_array));

LOGIC of Converting a "char" into an "int" in C

everyone!
please help me understand the following problem...
So i will have a STRING-type input of a note, looks like "A5" or "G#2" or "Cb4" etc. And i need to extract an octave index, which is the last digit "5" or "2" or "4"... And after exctraction i need it as an int-type.
So I did this:
string note = get_string("Input: ");
int octave = atoi(note[strlen(note) - 1]);
printf("your octave is %i \n", octave);
But it gave me an error "error: incompatible integer to pointer conversion passing 'char' to parameter of type 'const char *'; take the address with & [-Werror,-Wint-conversion]"
Then I tryied to throug away the math from the function, and did this:
int extnum = strlen(note) - 1;
int octave = atoi(note[extnum]);
It didn't work as well. So i did my reserch on atoi function and i don't get it...
ATOI expects a string (CHECK)
Converts it to an interger, not the ASCII meanning (CHECK)
Library for atoi function (CHECK)
What I am doing in basically asking "take n-th character of that string and make it an int".
After googling for some time a found an other code example where a guy uses atoi with this symbol '&'. So i did this:
int octave = atoi(&note[strlen(note) - 1]);
And IT WORKED! But I can't understand WHY it worked with the & symbol and didnt work without it....Cause it always worked without it! There was a million times i was giving a single-character string like '5' or so ond just used atoi and it worked perfectly...
Plesase help me, why in this case it acts so weird?
C does not have a native string type. Strings are usually represented as char array or a pointer to char.
Assuming that string is just a typedef to char *.
if note is an array of chars, note[strlen(note)-1] is just the last character. Since atoi expects a pointer to char (which has to be null-terminated) you have to pass the address of the char and not the value.
The task to convert one char digit to int could also be solved easier:
int octave = note[strlen(note) - 1] - '0';
The function atoi takes a pointer to a character array as the input parameter (const char*). When you call note[strlen(note) - 1] this is a single character (char), in order to make atoi work you need to provide the pointer. You do that by adding & as you've done. This then works, because right after that single digit there is a null character \0 that terminates the string - because your original string was null-terminated.
Note however that doing something like this would not be a good idea:
char a = '7';
int b = atoi(&a);
as there is no way to be sure what the next byte in memory is (following the byte that belongs to a), but the function will try to read it anyway, which can lead to undefined behaviour.
The last character is... well a character! not a string. So by adding the & sign, you made it a pointer to character (char*)!
You can also try this code:
char a = '5';
int b = a - '0';
Gives you ASCII code of 5 minus ASCII code of 0
From the manual pages, the signature of atoi is: int atoi(const char *nptr);. So, you need to pass the address of a char.
When you do this: atoi(note[strlen(note) - 1]) you pass the char itself. Thus, invoking UB.
When you use the &, you are passing what the function expects - the address. Hence, that works.
atoi excepts a string (a pointer to character), not a single character.
However, you should never use atoi since that function has bad error handling. The function strtol is 100% equivalent but safer.
You need to do this in two steps:
Find the first digit in the string.
From there, convert it to integer by calling strtol.
1) can is solved by looping through the string, checking if every item is a digit by calling isdigit from ctype.h. Simultaneously, check for the end of the string, the null terminator \0. When you find the first digit, save a pointer to that address.
2) is solved by passing the saved pointer to strtol, such as result = strtol(pointer, NULL, 10);.

Does strlen return same value for a binary and ascii data

Please find the below code snippet.
unsigned char bInput[20];
unsigned char cInput[20];
From a function, I get a binary data in bInput and I determined its length using strlen(bInput).
I converted bInput which is in binary to ASCII and stored in cInput and printed its length. But both are different.
I am new to programming. Please guide regarding its behaviour.
Function strlen returns the index of the first character in memory with a value of 0 (AKA '\0'), starting from the memory address indicated by the input argument passed to this function.
If you pass a memory address of "something else" other than a zero-terminated string of characters (which has been properly allocated at that memory address), then there's a fair chance that it will result with a memory-access violation (AKA segmentation fault).
result wont be same for both cases.
Below is one sample scenario:
Null is valid UTF-8, it just doesn't work with C 'strings'.
char temp[8];
buf = "abcde\0f";
What we have here is a buffer of length 8, which contains these char values:
97 98 99 100 101 0 102 0
here,strlen(temp) is equal to 5 as per strlen design,however,The actual length of the buffer is eight.
strlen() counts each byte untill it reaches NULL character ('\0' that means value of a byte is zero). So if you are getting different length for binary and ascii characters means you need to check the below two points in your conversion logic,
what you are doing if binary value is zero.
whether you are converting any nonzero binary value to zero.

Strange behaviour snprintf c

After a few weeks playing with C, I now hate it.
I'm now trying to pass arguments to another program using execl, and the formatting of the arguments does weird things:
int select[2], result[2];
char str_w,str_r;
snprintf(&str_w, 2, "%d", select[1]);
snprintf(&str_r, 2, "%d", result[0]);
printf("%d %d %s %s\n", select[1], result[0], &str_w, &str_r);
execl("./recive.x","./recive.x",&str_w,&str_r,(char *)NULL);
What is important here is the snprintf: I'm trying to convert a number in a vector to a string. The number will be smaller than 10. When I execute this, the result of the printf shown is:
5 6 6
Which means that there's a number (5) in select[1], there's a number (6) in result[0] and result[0] is converted to string properly but select[1] no.
What the hell is that behaviour!!
Thank you in advance!
After a few weeks playing with C, I now hate it.
This is not an uncommon reaction. A third of my intro CS class changed majors, citing difficulty with C (it's a horrible teaching language). It took me several years to adequately wrap my head around it, but once I did I came to appreciate it.
char str_w,str_r;
snprintf(&str_w, 2, "%d", select[1]);
snprintf(&str_r, 2, "%d", result[0]);
This is your main problem; str_w and str_r are only large enough to hold a single char value, not strings (which would require at least 2 characters). Instead, you need to declare str_w and str_r as arrays of char, large enough to hold the string representation of the largest int you'll be expecting, plus space for a sign (if the value is negative), plus the 0 terminator. For example, if you weren't limiting the value on select or result:
#define MAX_DIGITS 20 // max decimal digits for 64-bit integer
#define SIZE MAX_DIGITS+2 // 2 extra for sign and 0 terminator
char str_w[SIZE], str_r[SIZE];
sprintf(str_w, "%d", select[1]);
sprintf(str_r, "%d", result[0]);
By making your target arrays large enough for any possible input, you don't have to worry about overflow. Yes, you suffer a little internal fragmentation, and depending on your application that may or may not be an issue. But I just like keeping things simple.
If you know for a fact that your select and result arrays will never hold values outside the range 0..10, then you can set SIZE to 3 (up to 2 digits plus 0 terminator).
Which means that there's a number (5) in select[1], there's a number (6) in result[0] and result[0] is converted to string properly but select[1] no.
What the hell is that behaviour!!
Since you passed an address to a buffer that wasn't large enough to hold the result, the behavior is undefined, meaning the compiler isn't obligated to warn you that you're doing something dangerous.
Here's what's most likely happening (since the behavior is undefined, any sequence of events is possible, but I think this is a reasonable interpretation of the result). First of all, assume your variables are laid out in memory as follows:
Item Address Value
---- ------- -----
str_r 0xffec1230 ??
str_w 0xffec1231 ??
0xffec1232 ??
str_w and str_r are allocated to consecutive bytes, and their initial value is indeterminate. After the first snprintf to str_w, your memory now looks like this:
Item Address Value
---- ------- -----
str_r 0xffec1230 ??
str_w 0xffec1231 '5'
0xffec1232 0
snprintf will write a trailing 0 terminator to the buffer; in this case, it writes the terminator to the byte following str_w. After the second sprintf call, memory now looks like this:
Item Address Value
---- ------- -----
str_r 0xffec1230 '6'
str_w 0xffec1231 0
0xffec1232 0
The second snprintf call wrote the 0 terminator to the byte following str_r, which just happens to be str_w; you wound up clobbering the value written to it previously. That's why you see the str_r string but not the str_w string.
A call to sprintf must provide a buffer for the printed data, enough to fit the entire output. You are passing a pointer to a single char, so the output clearly does not fit.
char str_w[2];
snprintf(str_w, 2, "%d", select[0]);
A better way to convert a one-digit number to a string is as follows:
char res[2];
res[0]=num+'0';
res[1]=0;
Note the single quotes around the first zero: the idea is to add the code of the zero character to the one-digit number.
With char you define the only one byte variable. It must me an array of chars.

New to C: whats wrong with my program?

I know my way around ruby pretty well and am teaching myself C starting with a few toy programs. This one is just to calculate the average of a string of numbers I enter as an argument.
#include <stdio.h>
#include <string.h>
main(int argc, char *argv[])
{
char *token;
int sum = 0;
int count = 0;
token = strtok(argv[1],",");
while (token != NULL)
{
count++;
sum += (int)*token;
token = strtok(NULL, ",");
}
printf("Avg: %d", sum/count);
printf("\n");
return 0;
}
The output is:
mike#sleepycat:~/projects/cee$ ./avg 1,1
Avg: 49
Which clearly needs some adjustment.
Any improvements and an explanation would be appreciated.
Look for sscanf or atoi as functions to convert from a string (array of characters) to an integer.
Unlike higher-level languages, C doesn't automatically convert between string and integral/real data types.
49 is the ASCII value of '1' char.
It should be helpful to you....:D
The problem is the character "1" is 49. You have to convert the character value to an integer and then average.
In C if you cast a char to an int you just get the ASCII value of it. So, you're averaging the ascii value of the character 1 twice, and getting what you'd expect.
You probably want to use atoi().
EDIT: Note that this is generally true of all typecasts in C. C doesn't reinterpret values for you, it trusts you to know what exists at a given location.
strtok(
Please, please do not use this. Even its own documentation says never to use it. I don't know how you, as a Ruby programmer, found out about its existence, but please forget about it.
(int)*token
This is not even close to doing what you want. There are two fundamental problems:
1) A char* does not "contain" text. It points at text. token is of type char*; therefore *token is of type char. That is, a single byte, not a string. Note that I said "byte", not "character", because the name char is actually wrong - an understandable oversight on the part of the language designers, because Unicode did not exist back then. Please understand that char is fundamentally a numeric type. There is no real text type in C! Interpreting a sequence of char values as text is just a convention.
2) Casting in C does not perform any kind of magical conversions.
What your code does is to grab the byte that token points at (after the strtok() call), and cast that numeric value to int. The byte that is rendered with the symbol 1 actually has a value of 49. Again, interpreting a sequence of bytes as text is just a convention, and thus interpreting a byte as a character is just a convention - specifically, here we are using the convention known as ASCII. When you hit the 1 key on your keyboard, and later hit enter to run the program, the chain of events set in motion by the command window actually passed a byte with the value 49 to your program. (In the same way, the comma has a value of 44.)
Both of the above problems are solved by using the proper tools to parse the input. Look up sscanf(). However, you don't even want to pass the input to your program this way, because you can't put any spaces in the input - each "word" on the command line will be passed as a separate entry in the argv[] array.
What you should do, in fact, is take advantage of that, by just expecting each entry in argv[] to represent one number. You can again use sscanf() to parse each entry, and it will be much easier.
Finally:
printf("Avg: %d", sum/count)
The quotient sum/count will not give you a decimal result. Dividing an integer by another integer yields an integer in C, discarding the remainder.
In this line: sum += (int)*token;
Casting a char to an int takes the ASCII value of the char. for 1, this value is 49.
Use the atoi function instead:
sum += atoi(token);
Note atoi is found in the stdlib.h file, so you'll need to #include it as well.
You can't convert a string to an integer via
sum += (int)*token;
Instead you have to call a function like atoi():
sum += atoi (token);
when you cast a char (which is what *token is) to int you get its ascii value in C - which is 49... so the average of the chars ascii values is in fact 49. you need to use atoi to get the value of the number represented

Resources