Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
This is for C programming. I am studying for a midterm and one of the problems on the practice test had me a little confused and was hoping someone could help walk me through the code.
The code to the function is:
int xtoi(char s[])
{
int i;
int result;
i = result = 0;
while (s[i]) {
if (s[i] >= '0' && s[i] <= '9') {
result = result * 16 + s[i++] - '0';
} else if (s[i >= 'a' && s[i] <= 'f') {
result = result * 16 + s[i++] - 'a' + 10;
} else if (s[i] >= 'A' && s[i] <= 'F') {
result = result * 16 + s[i++] - 'A' +10;
}
}
return result;
}
What is the result of the following?
xtoi("1fa")
The answer should be 506, but I have no idea how. It is probably quite simple but this is all relatively new to me, so any help and guidance is much appreciated.
Thanks in advance.
If s[i] is a char from '0' to '9', convert it to the
corresponding int(0 to 9).
If not, if s[i] is a char from 'a' to 'f', convert it to the corresponding int(10 to 16).
If not, if s[i] is a char from 'A' to 'F', convert it to the corresponding int(10 to 16).
If still not, ignore it.
Then all such numbers are added up (following a rule) to generate the hex value represented by s.
Hint concerning the rule: For example, say you want to get a decimal number consists of '4' and '2'. You'll let a temporary result to be 4 first, multiply it by 10, and add 2 to that temp result. This will give you what you want: 4 * 10 + 2 = 42.
Please think about it again, and I promise you can understand it on your own.
BTW, the result of xtoi("1fa") is identical to that of strtol("1fa", NULL, 16)
The function converts hex strings into ints. It is analagous to atoi.
int xtoi(char s[])
{
int i = 0;
int result = 0;
/* while we haven't seen the null terminator */
while (s[i]) {
/* if this char is a digit
* convert this char to an int and then add
* it to the next decimal place in the number.
* Then advance to the next character */
if (s[i] >= '0' && s[i] <= '9')
result = result * 16 + s[i++] - '0';
/* if this character is a hex digit
* convert this char to an int and then add
* it to the next decimal place in the number.
* Then advance to the next character */
else if (s[i] >= 'a' && s[i] <= 'f')
result = result * 16 + s[i++] - 'a' + 10;
/* same as above, except for uppercase hex digits */
else if (s[i] >= 'A' && s[i] <= 'F')
result = result * 16 + s[i++] - 'A' + 10;
}
/* return the converted number */
return result;
}
This program could be rewritten like this
int xtoi(char s[])
{
int i = 0;
int result = 0;
/* while we haven't seen the null terminator */
for (i = 0; s[i]; ++i) {
/* if this char is a digit
* convert this char to an int and then add
* it to the next decimal place in the number. */
if (isdigit(s[i]))
result = result * 16 + (s[i] - '0');
/* if this character is a hex digit
* convert this char to an int and then add
* it to the next decimal place in the number. */
else if (isxdigit(s[i])) {
/* if the hex digit is uppercase, the char to subtract
* from is also uppercase. Otherwise it is lowercase */
char base = isupper(s[i]) ? 'A' : 'a';
result = result * 16 + s[i] - base + 10;
}
}
/* return the converted number */
return result;
}
The output of the new xtoi("1fa"); is correct: 506.
Related
`
#include <unistd.h>
int ft_atoi(char *str)
{
int c;
int sign;
int result;
c = 0;
sign = 1;
result = 0;
while ((str[c] >= '\t' && str[c] <= '\r') || str[c] == ' ')
{
c++;
}
while (str[c] == '+' || str[c] == '-')
{
if (str[c] == '-')
sign *= -1;
c++;
}
while (str[c] >= '0' && str[c] <= '9')
{
result = (str[c] - '0') + (result * 10);
c++;
}
return (result * sign);
}
#include <stdio.h>
int main(void)
{
char *s = " ---+--+1234ab567";
printf("%d", ft_atoi(s));
}
`
This line: result = (str[c] - '0') + (result * 10);
Why do we subtract zero and multiply by 10? How its convert ascii to int with this operations?
Thanks...
Some detail before answering your question
Internally everything is a number a char is not an exception.
In C char is a promoted type of integer meaning characters are integer in C. The char which is promoted type of integer are mapped to responding ASCII Value.
For example:
Capital Letter Range
65 => 'A' to 90 => 'Z'
Small Letter Range
97 => 'a' to 122 => 'z'
Number Range
48 => '0' to 57 => '9'
To answer your question
The ASCII CHARACTER '0' subtracted from any ASCII CHARACTER that is a digit(0-9) results to an actual Integer.
For Example
'9'(57) - '0'(48) = 9 (int)
'8'(56) - '0'(48) = 8 (int)
Remember char are promoted integer in C Read the detail to understand this statement.
And Also the ASCII CHARACTER '0' added to any INTEGER in the range(0-9) results to an ASCII CHARACTER.
For Example
9 + '0'(48) = '9'(57) (char)
8 + '0'(48) = '8' (56)(char)
Please see ASCII table
The ASCII code for '0' is 48 - not zero. Therefore, to convert to decimal you need to subtract 48
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
If I get the numbers in character string like this and store them in array x[100] and y[100],
x[100] '1' '0' '0' '0' '\0'
y[100] '9' '9' '\0'
The added result should be stored as string, too.
result[100] '1' '0' '9' '9' '\0'
I have tried to use ASCII code to solve this problem, for example:
char number[2] = {'1','2'};
char result;
result = (number[0] - '0') + (number[1] - '0') + '0';
But I am having trouble adjusting digits of x[100] and y[100].
I really need your help :(
You may insist on avoiding the library functions to convert strings to numbers and vice versa, as mentioned by #sjsam.
If you think about it, numbers really start at the LSD (least significant digit, not acid), or on the right side. So start your loop at the last character of each array before the closing \0 and iterate backwards. It won't be trivial, but that's how atoi() works too.
It's rather unfortunate that the Romans started writing from left to right, but that's the root cause of this problem. Otherwise integration of right-to-left Arabic numbers would have been much easier.
Discussing the idiocy of Roman numerals is out of the scope of this answer, let's suffice it to say that their lack of zero prevented any of their programs to finish with a successful exit status, which in turn led to the collapse of the Roman Empire.
Instead of adding byte by byte you may use the functions atoi to convert a string to integer and once you calculate the sum, you may use itoa/snprintf to convert the sum(integer) to its string representation.
See an example below :
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(void)
{
char buffer[100];
int no1,no2,no3;
char number1[] = {'1','0','0','0','\0'};
char number2[] = {'9','0','0','\0'};
/* Converting strings to respective integers */
no1=atoi(number1);
no2=atoi(number2);
no3=no1+no2;
/* Convert integer to a null terminated string
* You could also use itoa(no3,buffer,10);
*/
snprintf(buffer,99,"%d",no3);
printf("no1 : %d\n",no1);
printf("no2 : %d\n",no2);
printf("no3 : %s\n",buffer); // printing the string
return 0;
}
itoa is not supported by some compilers, so the best bet is to use snprintf.
Output
Here buffer is a null terminated array
{'1','9','0','0','\0'}
which we have printed using the %s format specifier.
References
itoa manaul.
snprintf manual.
Note
I have set the buffer size to 100 for this example. However, the maximum value of number of bytes that buffer can hold is the depends upon the maximum value an integer(signed in your case) can contain in your system. (Thanks #BLUEPIXY for reminding this stuff. :) )
Sum ASCII numbers while avoiding converting to binary/decimal:
#include <stdio.h>
#include <string.h>
#include <libc.h>
char *sum(char *x, char *y) {
size_t x_idx = strlen(x);
size_t y_idx = strlen(y);
size_t z_idx = MAX(x_idx, y_idx) + 1;
char *z = malloc(z_idx + 1);
char carry = '0';
while (x_idx > 0 || y_idx > 0 || carry == '1') {
char digit = carry;
if (x_idx > 0 && y_idx > 0) {
digit = (x[--x_idx] + y[--y_idx] + carry) % '0' + '0';
} else if (x_idx > 0) {
digit = (x[--x_idx] + carry) % '0' + '0';
} else if (y_idx > 0) {
digit = (y[--y_idx] + carry) % '0' + '0';
}
carry = (digit > '9') ? '1' : '0';
if (carry == '1') {
digit -= 10;
}
z[--z_idx] = digit;
}
while (z_idx > 0) {
z[--z_idx] = ' '; // pad for now; for production, shift array
}
return z;
}
int main(int argc, char* argv[]) {
char *x = argv[1];
char *y = argv[2];
char *z = sum(x, y);
printf("%s + %s = %s\n", x, y, z);
free(z);
}
USAGE
> ./a.out 1000 99
1000 + 99 = 1099
>
> ./a.out 999 999
999 + 999 = 1998
>
#include <stdio.h>
#include <string.h>
int main(void) {
char x[100] = "1000";
char y[100] = "99";
char result[100+1] = " ";
int x_i = strlen(x)-1;
int y_i = strlen(y)-1;
int r_i = 1 + ((x_i > y_i) ? x_i + 1 : y_i + 1);
int carray = 0, sum;
result[r_i] = 0;
while(--r_i>0){
if(x_i >= 0 && y_i >= 0)
sum = x[x_i--] - '0' + y[y_i--] - '0' + carray;
//else if(x_i < 0 && y_i < 0)
// sum = carray;
else if(y_i < 0)// && x_i >= 0){
sum = x[x_i--] - '0' + carray;
else// if (x_i < 0 && y_i >= 0){
sum = y[y_i--] - '0' + carray;
carray = sum > 9;
result[r_i] = sum % 10 + '0';
}
if(carray)
result[0] = '1';
printf("%s\n", result);
return 0;
}
Here's some code that should give you a good idea.
This begins to handle carrying, but doesn't cover all cases.
It should be a good start.
#include <stdio.h>
#include <string.h>
int main(void) {
char y[100] = "1032";
char x[100] = "2399";
int carry = 0;
char* b = (strlen(x) > strlen(y))? x : y;
char* s = (strlen(x) <= strlen(y))? x : y;
for(int i=strlen(s)-1, j=strlen(b)-1; i>=0; --i,--j)
{
b[j] = (b[j]+s[i]+carry-'0');
carry = 0;
if (b[j] > '9')
{
b[j] = (b[j]-'0')%10+'0';
carry = 1;
}
}
puts(b);
return 0;
}
I'm trying to write my first kernel module so I'm not able to include libraries for atoi, strtol, etc. How can I convert a string to int without these built-in functions? I tried:
int num;
num = string[0] - '0';
which works for the first character, but if I remove the [0] to try and convert the full string it gives me a warning: assignment makes integer from pointer without a cast. So what do I do?
When creating your own string to int function, make sure you check and protect against overflow. For example:
/* an atoi replacement performing the conversion in a single
pass and incorporating 'err' to indicate a failed conversion.
passing NULL as error causes it to be ignored */
int strtoi (const char *s, unsigned char *err)
{
char *p = (char *)s;
int nmax = (1ULL << 31) - 1; /* INT_MAX */
int nmin = -nmax - 1; /* INT_MIN */
long long sum = 0;
char sign = *p;
if (*p == '-' || *p == '+') p++;
while (*p >= '0' && *p <= '9') {
sum = sum * 10 - (*p - '0');
if (sum < nmin || (sign != '-' && -sum > nmax)) goto error;
p++;
}
if (sign != '-') sum = -sum;
return (int)sum;
error:
fprintf (stderr, "strtoi() error: invalid conversion for type int.\n");
if (err) *err = 1;
return 0;
}
You can't remove the [0]. That means that you are subtracting '0' from the pointer string, which is meaningless. You still need to dereference it:
num = string[i] - '0';
A string is an array of characters, represented by an address (a.k.a pointer).
An pointer has an value that might look something like 0xa1de2bdf. This value tells me where the start of the array is.
You cannot subtract a pointer type with a character type (e.g 0xa1de2bdf - 'b' does not really make sense).
To convert a string to a number, you could try this:
//Find the length of the string
int len = 0;
while (str[len] != '\0') {
len++;
}
//Loop through the string
int num = 0, i = 0, digit;
for (i=0; i<len; i++) {
//Extract the digit
digit = ing[i] - '0';
//Multiply the digit with its correct position (ones, tens, hundreds, etc.)
num += digit * pow(10, (len-1)-i);
}
Of course if you are not allowed to use math.h library, you could write your own pow(a,b) function which gives you the value of a^b.
int mypowfunc(int a, int b) {
int i=0, ans=1;
//multiply the value a for b number of times
for (i=0; i<b; i++) {
ans *= a;
}
return ans;
}
I have written the code above in a way that is simple to understand. It assumes that your string has a null character ('\0') right behind the last useful character (which is good practice).
Also, you might want to check that the string is actually a valid string with only digits (e.g '0', '1', '2', etc.). You could do this by including an if... else.. statement while looping through the string.
In modern kernels you want to use kstrto*:
http://lxr.free-electrons.com/source/include/linux/kernel.h#L274
274 /**
275 * kstrtoul - convert a string to an unsigned long
276 * #s: The start of the string. The string must be null-terminated, and may also
277 * include a single newline before its terminating null. The first character
278 * may also be a plus sign, but not a minus sign.
279 * #base: The number base to use. The maximum supported base is 16. If base is
280 * given as 0, then the base of the string is automatically detected with the
281 * conventional semantics - If it begins with 0x the number will be parsed as a
282 * hexadecimal (case insensitive), if it otherwise begins with 0, it will be
283 * parsed as an octal number. Otherwise it will be parsed as a decimal.
284 * #res: Where to write the result of the conversion on success.
285 *
286 * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error.
287 * Used as a replacement for the obsolete simple_strtoull. Return code must
288 * be checked.
289 */
This function skips leading and trailing whitespace, handles one optional + / - sign, and returns 0 on invalid input,
// Convert standard null-terminated string to an integer
// - Skips leading whitespaces.
// - Skips trailing whitespaces.
// - Allows for one, optional +/- sign at the front.
// - Returns zero if any non-+/-, non-numeric, non-space character is encountered.
// - Returns zero if digits are separated by spaces (eg "123 45")
// - Range is checked against Overflow/Underflow (INT_MAX / INT_MIN), and returns 0.
int StrToInt(const char* s)
{
int minInt = 1 << (sizeof(int)*CHAR_BIT-1);
int maxInt = -(minInt+1);
char* w;
do { // Skip any leading whitespace
for(w=" \t\n\v\f\r"; *w && *s != *w; ++w) ;
if (*s == *w) ++s; else break;
} while(*s);
int sign = 1;
if ('-' == *s) sign = -1;
if ('+' == *s || '-' == *s) ++s;
long long i=0;
while('0' <= *s && *s <= '9')
{
i = 10*i + *s++ - '0';
if (sign*i < minInt || maxInt < sign*i)
{
i = 0;
break;
}
}
while (*s) // Skip any trailing whitespace
{
for(w=" \t\n\v\f\r"; *w && *s != *w; ++w) ;
if (*w && *s == *w) ++s; else break;
}
return (int)(!*s*sign*i);
}
" not able to include libraries" --> Unclear if code is allowed access to INT_MAX, INT_MIN. There is no way to determine the minimum/maximum signed integer in a completely portable fashion without using the language provided macros like INT_MAX, INT_MIN.
Use INT_MAX, INT_MIN is available. Else we could guess the char width is 8. We could guess there are no padding bits. We could guess that integers are 2's complement. With these reasonable assumptions, minimum and maximum are defined below.
Note: Shifting into the sign bit is undefined behavior (UB), so don't do that.
Let us add another restriction: make a solution that works for any signed integer from signed char to intmax_t. This disallows code from using a wider type, as there may not be a wider type.
typedef int Austin_int;
#define Austin_INT_MAXMID ( ((Austin_int)1) << (sizeof(Austin_int)*8 - 2) )
#define Austin_INT_MAX (Austin_INT_MAXMID - 1 + Austin_INT_MAXMID)
#define Austin_INT_MIN (-Austin_INT_MAX - 1)
int Austin_isspace(int ch) {
const char *ws = " \t\n\r\f\v";
while (*ws) {
if (*ws == ch) return 1;
ws++;
}
return 0;
}
// *endptr points to where parsing stopped
// *errorptr indicates overflow
Austin_int Austin_strtoi(const char *s, char **endptr, int *errorptr) {
int error = 0;
while (Austin_isspace(*s)) {
s++;
}
char sign = *s;
if (*s == '-' || *s == '+') {
s++;
}
Austin_int sum = 0;
while (*s >= '0' && *s <= '9') {
int ch = *s - '0';
if (sum <= Austin_INT_MIN / 10 &&
(sum < Austin_INT_MIN / 10 || -ch < Austin_INT_MIN % 10)) {
sum = Austin_INT_MIN;
error = 1;
} else {
sum = sum * 10 - ch;
}
s++;
}
if (sign != '-') {
if (sum < -Austin_INT_MAX) {
sum = Austin_INT_MAX;
error = 1;
} else {
sum = -sum;
}
}
if (endptr) {
*endptr = (char *) s;
}
if (errorptr) {
*errorptr = error;
}
return sum;
}
The above depends on C99 or later in the Austin_INT_MIN Austin_INT_MIN % 10 part.
This is the cleanest and safest way I could come up with
int str_to_int(const char * str, size_t n, int * int_value) {
int i;
int cvalue;
int value_muliplier = 1;
int res_value = 0;
int neg = 1; // -1 for negative and 1 for whole.
size_t str_len; // String length.
int end_at = 0; // Where loop should end.
if (str == NULL || int_value == NULL || n <= 0)
return -1;
// Get string length
str_len = strnlen(str, n);
if (str_len <= 0)
return -1;
// Is negative.
if (str[0] == '-') {
neg = -1;
end_at = 1; // If negative 0 item in 'str' is skipped.
}
// Do the math.
for (i = str_len - 1; i >= end_at; i--) {
cvalue = char_to_int(str[i]);
// Character not a number.
if (cvalue == -1)
return -1;
// Do the same math that is down below.
res_value += cvalue * value_muliplier;
value_muliplier *= 10;
}
/*
* "436"
* res_value = (6 * 1) + (3 * 10) + (4 * 100)
*/
*int_value = (res_value * neg);
return 0;
}
int char_to_int(char c) {
int cvalue = (int)c;
// Not a number.
// 48 to 57 is 0 to 9 in ascii.
if (cvalue < 48 || cvalue > 57)
return -1;
return cvalue - 48; // 48 is the value of zero in ascii.
}
I have a char array say char value []={'0','2','0','c','0','3'};
I want to convert this into a byte array like unsigned char val[]={'02','0c','03'}
This is in an embedded application so i can't use string.h functions. How can i do this?
Sicne you talk about an embedded application I assume that you want to save the numbers as values and not as strings/characters. So if you just want to store your character data as numbers (for example in an integer), you can use sscanf.
This means you could do something like this:
char source_val[] = {'0','A','0','3','B','7'} // Represents the numbers 0x0A, 0x03 and 0xB7
uint8 dest_val[3]; // We want to save 3 numbers
for(int i = 0; i<3; i++)
{
sscanf(&source_val[i*2],"%x%x",&dest_val[i]); // Everytime we read two chars --> %x%x
}
// Now dest_val contains 0x0A, 0x03 and 0xB7
However if you want to store it as a string (like in your example), you can't use unsigned char
since this type is also just 8-Bit long, which means it can only store one character. Displaying 'B3' in a single (unsigned) char does not work.
edit: Ok according to comments, the goal is to save the passed data as a numerical value. Unfortunately the compiler from the opener does not support sscanf which would be the easiest way to do so. Anyhow, since this is (in my opinion) the simplest approach, I will leave this part of the answer at it is and try to add a more custom approach in this edit.
Regarding the data type, it actually doesn't matter if you have uint8. Even though I would advise to use some kind of integer data type, you can also store your data into an unsigned char. The problem here is, that the data you get passed, is a character/letter, that you want to interpret as a numerical value. However, the internal storage of your character differs. You can check the ASCII Table, where you can check the internal values for every character.
For example:
char letter = 'A'; // Internally 0x41
char number = 0x61; // Internally 0x64 - represents the letter 'a'
As you can see there is also a differnce between upper an lower case.
If you do something like this:
int myVal = letter; //
myVal won't represent the value 0xA (decimal 10), it will have the value 0x41.
The fact you can't use sscanf means you need a custom function. So first of all we need a way to conver one letter into an integer:
int charToInt(char letter)
{
int myNumerical;
// First we want to check if its 0-9, A-F, or a-f) --> See ASCII Table
if(letter > 47 && letter < 58)
{
// 0-9
myNumerical = letter-48;
// The Letter "0" is in the ASCII table at position 48 -> meaning if we subtract 48 we get 0 and so on...
}
else if(letter > 64 && letter < 71)
{
// A-F
myNumerical = letter-55
// The Letter "A" (dec 10) is at Pos 65 --> 65-55 = 10 and so on..
}
else if(letter > 96 && letter < 103)
{
// a-f
myNumerical = letter-87
// The Letter "a" (dec 10) is at Pos 97--> 97-87 = 10 and so on...
}
else
{
// Not supported letter...
myNumerical = -1;
}
return myNumerical;
}
Now we have a way to convert every single character into a number. The other problem, is to always append two characters together, but this is rather easy:
int appendNumbers(int higherNibble, int lowerNibble)
{
int myNumber = higherNibble << 4;
myNumber |= lowerNibbler;
return myNumber;
// Example: higherNibble = 0x0A, lowerNibble = 0x03; -> myNumber 0 0xA3
// Of course you have to ensure that the parameters are not bigger than 0x0F
}
Now everything together would be something like this:
char source_val[] = {'0','A','0','3','B','7'} // Represents the numbers 0x0A, 0x03 and 0xB7
int dest_val[3]; // We want to save 3 numbers
int temp_low, temp_high;
for(int i = 0; i<3; i++)
{
temp_high = charToInt(source_val[i*2]);
temp_low = charToInt(source_val[i*2+1]);
dest_val[i] = appendNumbers(temp_high , temp_low);
}
I hope that I understood your problem right, and this helps..
If you have a "proper" array, like value as declared in the question, then you loop over the size of it to get each character. If you're on a system which uses the ASCII alphabet (which is most likely) then you can convert a hexadecimal digit in character form to a decimal value by subtracting '0' for digits (see the linked ASCII table to understand why), and subtracting 'A' or 'a' for letters (make sure no letters are higher than 'F' of course) and add ten.
When you have the value from the first hexadeximal digit, then convert the second hexadecimal digit the same way. Multiply the first value by 16 and add the second value. You now have single byte value corresponding to two hexadecimal digits in character form.
Time for some code examples:
/* Function which converts a hexadecimal digit character to its integer value */
int hex_to_val(const char ch)
{
if (ch >= '0' && ch <= '9')
return ch - '0'; /* Simple ASCII arithmetic */
else if (ch >= 'a' && ch <= 'f')
return 10 + ch - 'a'; /* Because hex-digit a is ten */
else if (ch >= 'A' && ch <= 'F')
return 10 + ch - 'A'; /* Because hex-digit A is ten */
else
return -1; /* Not a valid hexadecimal digit */
}
...
/* Source character array */
char value []={'0','2','0','c','0','3'};
/* Destination "byte" array */
char val[3];
/* `i < sizeof(value)` works because `sizeof(char)` is always 1 */
/* `i += 2` because there is two digits per value */
/* NOTE: This loop can only handle an array of even number of entries */
for (size_t i = 0, j = 0; i < sizeof(value); i += 2, ++j)
{
int digit1 = hex_to_val(value[i]); /* Get value of first digit */
int digit2 = hex_to_val(value[i + 1]); /* Get value of second digit */
if (digit1 == -1 || digit2 == -1)
continue; /* Not a valid hexadecimal digit */
/* The first digit is multiplied with the base */
/* Cast to the destination type */
val[j] = (char) (digit1 * 16 + digit2);
}
for (size_t i = 0; i < 3; ++i)
printf("Hex value %lu = %02x\n", i + 1, val[i]);
The output from the code above is
Hex value 1 = 02
Hex value 2 = 0c
Hex value 3 = 03
A note about the ASCII arithmetic: The ASCII value for the character '0' is 48, and the ASCII value for the character '1' is 49. Therefore '1' - '0' will result in 1.
It's easy with strtol():
#include <stdlib.h>
#include <assert.h>
void parse_bytes(unsigned char *dest, const char *src, size_t n)
{
/** size 3 is important to make sure tmp is \0-terminated and
the initialization guarantees that the array is filled with zeros */
char tmp[3] = "";
while (n--) {
tmp[0] = *src++;
tmp[1] = *src++;
*dest++ = strtol(tmp, NULL, 16);
}
}
int main(void)
{
unsigned char d[3];
parse_bytes(d, "0a1bca", 3);
assert(d[0] == 0x0a);
assert(d[1] == 0x1b);
assert(d[2] == 0xca);
return EXIT_SUCCESS;
}
If that is not available (even though it is NOT from string.h), you could do something like:
int ctohex(char c)
{
if (c >= '0' && c <= '9') {
return c - '0';
}
switch (c) {
case 'a':
case 'A':
return 0xa;
case 'b':
case 'B':
return 0xb;
/**
* and so on
*/
}
return -1;
}
void parse_bytes(unsigned char *dest, const char *src, size_t n)
{
while (n--) {
*dest = ctohex(*src++) * 16;
*dest++ += ctohex(*src++);
}
}
Assuming 8-bit bytes (not actually guaranteed by the C standard, but ubiquitous), the range of `unsigned char` is 0..255, and the range of `signed char` is -128..127. ASCII was developed as a 7-bit code using values in the range 0-127, so the same value can be represented by both `char` types.
For the now discovered task of converting a counted hex-string from ascii to unsigned bytes, here's my take:
unsigned int atob(char a){
register int b;
b = a - '0'; // subtract '0' so '0' goes to 0 .. '9' goes to 9
if (b > 9) b = b - ('A' - '0') + 10; // too high! try 'A'..'F'
if (b > 15) b = b - ('a' - 'A); // too high! try 'a'..'f'
return b;
}
void myfunc(const char *in, int n){
int i;
unsigned char *ba;
ba=malloc(n/2);
for (i=0; i < n; i+=2){
ba[i/2] = (atob(in[i]) << 4) | atob(in[i+1]);
}
// ... do something with ba
}
I.E., you enter the number 5, and the character A and the output would yield F. I have no idea how to even start to go about this, any give me a push in the right direction?
Individual characters are represented by numbers according to the ASCII code (usually). In C, if you add a number to a character, you're shifting the character down. Try:
char c = 'A';
int n = 5;
printf("%c\n", c + n);
Look at the ASCII table and note the values of the characters.
Try this:
#include <stdio.h>
char shift_char(char val, char shift)
{
val = toupper(val);
assert(isupper(val));
char arr[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
return arr[ ( (toupper(val) - 'A' + shift) % 26) ];
}
You can get a little fancier if you want to preserve the case of the character. It also assumes, but does not verify shift is non-negative. That case may cause problems with the modulus operation you will need to guard against... or better yet prevent. Still, since this is tagged as homework, that's the sort of thing you should work through.
If you can assume ASCII, it is easier.
Characters are no more than simple numbers: only the interpretation of said numbers changes. In ASCII all letters are sequential; so the number for 'A' + 5 is the number for 'F'; 'F' - 1 is 'E' ..., ...
int ch = 'J';
ch -= 2; putchar(ch);
ch -= 3; putchar(ch);
ch += 7; putchar(ch); putchar(ch);
ch += 3; putchar(ch);
puts("");
Just pay attention to wrapping!
If you can't assume ASCII, you need to convert characters yourself. Something like:
char charr[26] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int ndx = 9; /* charr[9] is 'J' */
ndx -= 2; putchar(charr[ndx]);
ndx -= 3; putchar(charr[ndx]);
ndx += 7; putchar(charr[ndx]); putchar(charr[ndx]);
ndx += 3; putchar(charr[ndx]);
puts("");
Do not forget the wrapping
Other people have pointed out that you can use ASCII.
An easy way to handle wrapping is with modulus arithmetic:
char result, ch;
int offset;
... // Populate ch with the letter to be changed and offset with the number.
result = ch - 'a';
result = (result + offset) % 26; // 26 letters in the alphabet
result += 'a';
char shift_char(char c, char shift)
{
if(isalpha(c)) {
if (c>='A' && c<='Z') {
return 'A' + ( (c - 'A' + shift) % 26);
} else if(c>='a' && c<='z') {
return 'a' + ( (c - 'a' + shift) % 26);
}
}
return c;
}