I am trying to understand how the putchar('0' + r); works. Below, the function takes an integer and transform it to binary.
void to_binary(unsigned long n)
{
int r;
r = n % 2;
if (n >= 2)
to_binary(n / 2);
putchar('0' + r);
}
I google the definition of putchar but I didn't find this. To test it, I added a printf to see the value of the r:
void to_binary(unsigned long n)
{
int r;
r = n % 2;
if (n >= 2)
to_binary(n / 2);
printf("r = %d and putchar printed ", r);
putchar('0' + r);
printf("\n");
}
and I run it (typed 5) and got this output:
r = 1 and putchar printed 1
r = 0 and putchar printed 0
r = 1 and putchar printed 1
So I suppose that the putchar('0' + r); prints 0 if r=0, else prints 1 if r=1, or something else happens?
In C '0' + digit is a cheap way of converting a single-digit integer into its character representation, like ASCII or EBCDIC. For example if you use ASCII think of it as adding 0x30 ('0') to a digit.
The one assumption is that the character encoding has a contiguous area for digits - which holds for both ASCII and EBCDIC.
As pointed out in the comments this property is required by both the C++ and C standards. The C standard says:
5.2.1 - 3
In both the source and execution basic character sets, the value of
each character after 0 in the above list of decimal digits shall be
one greater than the value of the previous.
'0' represents an integer equal to 48 in decimal and is the ASCII code for the character 0 (zero). The ASCII code for the character for 1 is 49 in decimal.
'0' + r is the same as 48 + r. When r = 0, the expression evaluates to 48 so a 0 is outputted. On the other hand, when r = 1, the expression evaluates to 49 so a 1 is outputted. In other words, '0' + 1 == '1'
Basically, it's a nice way to convert decimal digits to their ASCII character representations easily. It also works with the alphabet (i.e. 'A' + 2 is the same as C)
It's a common technique used for char handing.
char a = '0' + r (r in [0,9]) will convert an integer to its char format based on given char base (i.e. '0' in this case), you will get '0'...'9'
Similarly, char a = 'a' + r or char a = 'A' + r (r in [0,25]) will convert an integer to its char format, you will get 'a'...'z' or 'A'...'Z' (except for EBCDIC systems which has discontinuous area for alphabets).
Edit:
You can also do the other way around, for example:
char myChar = 'c';
int b = myChar - 'a'; // b will be 2
Similar idea is used to convert a lowercase char to uppercase:
char myChar = 'c';
char newChar = myChar - 'a' + 'A'; // newChar will be 'C'
U are adding the ASCII value of the number's
say '0' ASCII value is 48
'1' -> 49,and so on CHECK HERE FOR COMPLETE TABLE
so when u add one to 48 it will 49 and putchar functuion prints the character sent to it. when u do
putchar('0' + r )
if r = 1 putchar(48 + 1) (converting into ASCII value)
putchar(49) which is 1
Related
I first did this:
// Convert ASCII range down to a value from 0 to 25
char uppercase[27] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char lowercase[27] = "abcdefghijklmnopqrstuvwxyz";
char convertedUppercase[27];
char convertedLowercase[27];
for (int i = 0; i <= 26; i++)
{
convertedUppercase[i] = uppercase[i] - 'A';
convertedLowercase[i] = lowercase[i] - 'a';
}
// For each character in the plaintext: (DOESN'T WORK)
for (int i = 0, n = strlen(p); i <= n; i++)
{
// Rotate the character if it's a letter // ci = (pi + k) % 26
if (isalpha(p[i]))
{
if (isupper(p[i]))
{
c[i] = ((p[i]) + k) % 26;
}
else if (islower(p[i]))
{
c[i] = ((p[i]) + k) % 26;
}
}
}
printf("ciphertext: %s\n", c);
but then I realized that the value of convertedUppercase will just be like 0 = NUL instead of 0 = A. Can anyone give me a hint what to do?
edit:
From the CS50 Discord:
"The caesar cipher formula (p + k) % 26 works on the premise that p (the plain text character) has a value of 0 - 25 (representing a - z or A - Z)
So if your plain char is 'x', that would have a value of 23, and if your key was 2, then the ciphered char would be:
(23 + 2) % 26
( 25 ) % 26
= 25 'z'
I'm kinda lost on how to do it.
This would be so much easier if you would provide a MRE.
I guess what you are observing is that you see a truncated cipertext if you attempt to output it via printf() with "%s".
This is however only because any "A" (that is a ciper A, i.e. after shifting by key) results in a 0 (which terminates string output, being the '\0' terminator) and most other letters result in unprintable characters.
This is because you only shift by key and map to 0-25 what needs to be the number representation (i.e. numeric instead of textual ciper) here:
c[i] = ((p[i]) + k) % 26;
In order to turn into textual cipher instead of numeric ciper, you need to do
convert textual to numeric, with -'A'
shift by key, with +k
map to 0-25, with %26
convert numeric to textual, with +'A'
I.e.
c[i] = ((p[i]-'A') + k) % 26 + 'A';
E.g. "H" from "Hello World".
textual to numeric, 'H' - 'A' -> 7
shift by key, 7 + 4 -> 11
map to 0-25, 11%26 -> 11
numeric to textual, 11 + 'A' -> 'L' is cipher
E.g. "W" from "Hello World".
textual to numeric, 'W' - 'A' -> 22
shift by key, 22 + 4 -> 26
map to 0-25, 26%26 -> 0
numeric to textual, 0 + 'A' -> 'A' is cipher
I have a given exercise that wants me to find the uppercase letter that is K places from the letter in this case char variable that is named C. The range is uppercase letters from A to Z.
For example if the input is B 3 the output should be E. For this specific input its simple you just sum the values and you get your answer but for example what if we go out of the range. Here is one example F 100 the program should output B because if the value is > than Z the program starts from A.
If there are some confusions I will try to explain it more here are some test cases and my code that only work if we don't cross the range.
Input Output
B 3 E
X 12345 S
F 100 B
T 0 T
#include <stdio.h>
int main(){
int K;
char C,rez;
scanf("%c %d",&C,&K);
int ch;
for(ch = 'A';ch <= 'Z';ch++){
if(C>='A' && C<='Z'){
rez = C+K;
}
}
printf("%c",rez);
return 0;
}
Think of the letters [A-Z] as base 26 where zero is A, one is B and 25 is Z.
As we sum of the letter (in base 26) and the offset, it is only the least significant base 26 digit we have interest, so use % to find the least significant base 26 digit much like one uses % 10 to find the least significant decimal digit.
scanf(" %c %d",&C,&K);
// ^ space added to consume any white-space
if (C >= 'A' && C <= 'Z') {
int base26 = C - 'A';
base26 = base26 + K;
base26 %= 26;
int output = base26 + 'A';
printf("%c %-8d %c\n", C, K, output);
}
For negative offsets we need to do a little more work as % in not the mod operator, but the remainder. This differs with some negative operands.
base26 %= 26;
if (base < 0) base26 += 26; // add
int output = base26 + 'A';
Pedantically, C + K may overflow with extreme K values. To account for that, reduce K before adding.
// base26 = C + K;
base26 = C + K%26;
We could be a little sneaky and add 26 to insure the sum is not negative.
if (C >= 'A' && C <= 'Z') {
int base26 = C - 'A';
base26 = base26 + K%26 + 26; // base26 >= 0, even when K < 0
base26 %= 26; // base26 >= 0 and < 26
int output = base26 + 'A';
printf("%c %-8d %c\n", C, K, output);
}
... or make a complex one-line
printf("%c %-8d %c\n", C, K, (C - 'A' + K%26 + 26)%26 + 'A');
This can be accomplished by using 2 concepts.
ASCII value
Modulus operator (%)
In C every character has an ASCII value. Basically it goes from 0-127.
The character 'A' has the value of 65
The character 'B' has the value of 66 (65 + 1)
and so on...
Until Z which is 65 + 25 = 90
And the 2nd concept I want to highlight in math is modulo arithmetic where if you always want to map a number to certain range, you can use a modulus operator.
Modulus is the reminder that you get after dividing a number by another number.
In our case, we have 26 alphabets so we can always get a number between 0 to 25
For the example you took
100 % 26 = 22
But you have to consider the starting point too.
So, we always subtract the initial alphabet by the value of 'A', i.e. 65 so that 'A' maps to 0 and 'Z' maps to 25
So, if we start with 'F' and need to go 100 places..
Subtract 'A' value from 'F' value. Characters behave like numbers so you can actually store 'F' - 'A' in an integer
In this case 'F' - 'A' = 5
Next we add the offset to this.
5 + 100 = 105
Then we perform modulus with 26
105 % 26 = 1
Finally add the value of 'A' back to the result
'A' + 1 = 'B'
And you are done
Get the remainder of input number with 26 using modulo operator. If sum of input character and remainder is less than or equal to Z then its the answer otherwise again find the remainder of sum with 26 and that will be answer (take care of offset because the ASCII decimal value of letter A is 65).
Roughly the implementation will be:
#include <stdio.h>
int main(){
int K;
char C, rez;
scanf("%c %d",&C,&K);
// Validate the user input
int ch;
int rem = K % 26;
if ((rem + C) - 'A' < 26) {
rez = rem + C;
} else {
rez = ((rem + C - 'A') % 26) + 'A';
}
printf("%c\n",rez);
return 0;
}
Note that, I know there is scope of improvement in the implementation. But this is just to give an idea to OP about how it can be done.
Output:
# ./a.out
B 3
E
# ./a.out
X 12345
S
# ./a.out
F 100
B
# ./a.out
T 0
T
I have been trying to translate this code to put it in simple terms to understand but can't quite get it.
Can someone help me understand it better and why the next line would they want to divide by 16?
char r = (c+n1+n2)>=16 ?
((c+n1+n2)-16+'0') :
((c+n1+n2)>9?((c+n1+n2)+55):(c+n1+n2)+'0');
c = (c+n1+n2)/16;
the lines above this are a while loop to print multiple numbers and are:
int i=s1-1, j=s2-1, c=0, k=0;// sets up for the calculations -1
// for the s1 and s2 because you do not want null character included here
// k is the number of places we use for addition
printf("COL d d c\n");
while(i>=0 || j>=0){
int n1 = i<0?0:num1[i]-'0';// is converting from the character representation
// of a number to the actual integer value of the same digit if not 0
int n2 = j<0?0:num2[j]-'0';
char r = (c+n1+n2)>=16 ?
((c+n1+n2)-16+'0') :
((c+n1+n2)>9?((c+n1+n2)+55):(c+n1+n2)+'0');
c = (c+n1+n2)/16;
printf("%3d : %d+%d+%d = %c\n", k, n1, n2, c, r);
i--;
j--;
k++;
}
It seems, the function above was intended to add two hex strings. I believe this, because the line in question encodes hex characters and the overflow, that occurs when adding two digits is treated in a way, that makes only sense if the digits are treated as 4 bit digts (hex digits). E.g. because of the division by 16.
If I am right, the hex decoding contains a bug, while the hex encoding for outputting the result seems almost correct. Almost, because if I got it right, the original version will not be able to calculate string additions like "00F" + "00F" correctly (see last output below).
It seems, as if even the original author was overwhelmed by his code.
Here is a version, that should do, what the original author intended to do:
void string_add(char num1[], char num2[], int s1, int s2) {
int i=s1-1, j=s2-1, c=0, k=0;// sets up for the calculations -1 for the s1 and s2 because you do not want null character included here
int z=0;
// k is the number of places we use for addition
printf("COL d d c\n");
while(i>=0 || j>=0){
/*
* the following lines represent the expressions
* int n1 = i<0?0:num1[i]-'0';// is converting from the character representation of a number to the actual integer value of the same digit if not 0
* int n2 = j<0?0:num2[j]-'0';
* I added the conversion of hex digits in the range A-F
*/
int n1, n2= 0;
char r;
if(i>=0) {
n1= num1[i];
if(n1>='A') {
n1-= 'A'-10;
} else {
n1-= +'0';
}
}
if(j>=0) {
n2= num2[j];
if(n2>='A') {
n2-= 'A'-10;
} else {
n2-= '0';
}
}
/*
* the following code is, what the line
* char r = (c+n1+n2)>=16?((c+n1+n2)-16+'0'):((c+n1+n2)>9?((c+n1+n2)+55):(c+n1+n2)+'0');
* originally did (I also do a partial calculation of the line
* c = (c+n1+n2)/16;
* to avoid repeating the term
*/
c= c+n1+n2;
r= c&15; // only take the lower 4 bits (ignore overflow bits)
z|= r << (4*k);
// construct the binary representation (shift the 4 bits into position and use bitwise or to add them to z)
if(r>9) {
r+= 'A'-10; // produces chars in range A-F = (ascii('G')-16+c
} else {
r+= '0'; // produces chars in range 0-9 if no overflow occurs
}
/*
* now just do the /16 part of
* c = (c+n1+n2)/16;
*/
c/= 16;
printf("%3d : %d+%d+%d = %c\n", k, n1, n2, c, r);
i--;
j--;
k++;
}
printf("%d\n", z);
}
void main(void) {
char s1[]= "0100";
char s2[]= "0B01";
string_add(s1, s2, 4, 4);
}
Tests (first output is from the version above, second from the original version):
"0005"+"0005"=
COL d d c
0 : 5+5+0 = A
1 : 0+0+0 = 0
2 : 0+0+0 = 0
3 : 0+0+0 = 0
10
COL d d c
0 : 5+5+0 = A
1 : 0+0+0 = 0
2 : 0+0+0 = 0
3 : 0+0+0 = 0
"9989"+"0987"=
COL d d c
0 : 9+7+1 = 0
1 : 8+8+1 = 1
2 : 9+9+1 = 3
3 : 9+0+0 = A
41744
COL d d c
0 : 9+7+1 = 0
1 : 8+8+1 = 1
2 : 9+9+1 = 3
3 : 9+0+0 = A
"000F"+"000F"=
COL d d c
0 : 15+15+1 = E
1 : 0+0+0 = 1
2 : 0+0+0 = 0
3 : 0+0+0 = 0
30
COL d d c
0 : 22+22+2 = L
1 : 0+0+0 = 2
2 : 0+0+0 = 0
3 : 0+0+0 = 0
The last output seems suspicuous. Was this really intended?
The code seems to perform the addition of 2 numbers stored as hexadecimal encoded strings. It is obfuscated in silly ways. Here is how to improve readability:
white space should be used wisely to make the logic more obvious: typically insert a space character on both sides of binary operators, between keywords and the corresponding ( and before the { opening a block.
the magic constant 55 should be replaced with 'A' - 10, making it more evident that the code performs a conversion from a numeric value to a hexadecimal digit character.
intermediary values should be computed and stored into aptly named local variables.
comments can be used for non obvious steps.
The code seems incorrect:
c > 0 should be tested too to account for possible overflow on the most significant digit.
conversion from hex should be performed when reading digits from the num1 and num2 strings, converting digits A through F to the values 10 to 15.
the resulting digit would be incorrect if c + n1 + n2 >= 26
Here is an attempt at fixing the code:
// s1 is the length of hex encoded string num1
// s2 is the length of hex encoded string num2
int carry = 0;
int i = s1, j = s2, k = 0;
// k is the number of places we use for addition
printf("COL d d c\n");
while (i > 0 || j > 0 || carry > 0) {
// get the digit values from num1 and num2
char c1 = i == 0 ? '0' : num1[--i];
char c2 = j == 0 ? '0' : num2[--j];
int d1 = c1 <= '9' ? c1 - '0' : c1 - 'A' + 10;
int d2 = c2 <= '9' ? c2 - '0' : c2 - 'A' + 10;
int digit = carry + d1 + d2;
carry = digit >> 4;
digit %= 15;
char r = digit > 9 ? (digit - 10 + 'A') : (digit + '0');
printf("%3d : %d+%d+%d = %c\n", k, d1, d2, carry, r);
k++;
}
I am new to C and I was looking for a custom function in C that would convert a string to an integer and I came across this algorithm which makes perfect sense except for one part. What exactly is the -'0' doing on this line n = n * 10 + a[c] - '0';?
int toString(char a[]) {
int c, sign, offset, n;
if (a[0] == '-') { // Handle negative integers
sign = -1;
}
if (sign == -1) { // Set starting position to convert
offset = 1;
}
else {
offset = 0;
}
n = 0;
for (c = offset; a[c] != '\0'; c++) {
n = n * 10 + a[c] - '0';
}
if (sign == -1) {
n = -n;
}
return n;
}
The algorithm did not have an explanation from where I found it, here.
The reason subtracting '0' works is that character code points for decimal digits are arranged sequentially starting from '0' up, without gaps. In other words, the character code for '5' is greater than the character code for '0' by 5; character code for '6' is greater than the character code for '0' by 6, and so on. Therefore, subtracting the code of zero '0' from a code of another digit produces the value of the corresponding digit.
This arrangement is correct for ASCII codes, EBSDIC, UNICODE codes of decimal digits, and so on. For ASCII codes, the numeric codes look like this:
'0' 48
'1' 49
'2' 50
'3' 51
'4' 52
'5' 53
'6' 54
'7' 55
'8' 56
'9' 57
Assuming x has a value in the range between '0' and '9', x - '0' yields a value between 0 and 9. So x - '0' basically converts a decimal digits character constant to its numerical integer value (e.g., '5' to 5).
C says '0' to '9' are implementation defined values but C also guarantees '0' to '9' to be sequential values.
I would like to have the equivalent of
void print3( char a, uint8_t b, int8_t c )
{
printf("%c %" PRIu8 " %" PRIi8 "\n", a, b, c);
}
using the write syscall. The problem is, I don't know how to print an integer using write. Only commands from ANSI C are allowed and using sprintf to format strings is forbidden.
Example syntax to use write:
const char msg[] = "Hello World!";
write(STDOUT_FILENO, msg, sizeof(msg)-1);
Edit: I am not allowed to use sprintf neither itoa.
Consider the number 155, if you divide by 100 then there's 1 hundred and the remainder is 55, divide by 10 you get 5 10s and the remainder is 5, divide that by 1 you get 5. now concatenate those numbers 1-5-5 you get the final number.This should get you started.
Each digit of the number to be printed is represented as a character.
There are two pieces to the solution:
calculate the digits of the number in the chosen base, 10 I assume in this case
convert the digit to a character and write it
For the step of calculating the digits, you will use the / and % operators; this will give the digits in "reverse" order, so you'll need to squirrel them away before writing them.
For converting the digits to characters, consider two approaches: simple arithmetic (using the ASCII character values), or an array lookup.
You will have to do the conversion yourself. The code below converts to ASCIIZ (C string), not simple ASCII, but it's useable:
int ltoa(long x, char *str, size_t str_size)
{
long y = 1;
size_t i, s;
for (s = 0; y < x; s++)
y *= 10;
if (str_size < s+1)
return s+1;
str[s--] = 0x0;
while(s)
{
str[s--] = '0' + (x % 10);
x /= 10;
}
str[0] = '0' + x;
return 0;
}
Do you know that 9(in ascii) == '0' + 9 :
char a =0;
a = '0';
printf("%c",a); //will print 0
a = '0' + 8;
printf("%c",a);//will print 8
EDIT:
int a = 1234;
now to convert it to char* b:
algorithm:
for each digit in a:
b.append(digit+'0')
you have to understand that char is a container of 8 bits == byte and can be a number, a letter in ASCII or whatever you want to represent within 8 bits