String concatenation with variables and pointers in 'C' - c

I am working on Caesar's cipher for an online course and I have a problem with the original value being in the final output, and I cannot seem to get rid of it. My suspicion is that it is due to
strcpy(str1, &final_val);
strcat(str2, str1);
being called in a wrong way, so when I run
make test && ./test 1
This provides my program with the argument 1 and provides the key to shift the letters and encode the message. I expect to see
plaintext: asd
ciphertext: bcd
Instead I get
plaintext: asd
ciphertext: bacbdc
If you want to try out the code, you will need to do it inside of this sanbox, because it has the required CS50 library.
Code
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
bool input_valid(int count, string arg);
bool in_alphabet(int count, string arg);
int main(int argc, string argv[]) {
int key;
int ascii_val;
char final_val;
string string;
char str1[80];
char str2[80];
// check input again if validation fails
if (!input_valid(argc, argv[1])) {
printf("Invalid input!\nUSAGE: ./caesar key\n");
return 1;
}
string = get_string("plaintext: ");
// get integer from string input
key = strtol(argv[1], NULL, 10);
for (int i = 0; i < strlen(string); i++) {
ascii_val = (int)string[i];
bool valid_lower_case = (ascii_val + key) >= 97 && (ascii_val + key) <= 122;
bool valid_upper_case = (ascii_val + key) >= 65 && (ascii_val + key) <= 90;
// check if value is a letter
if (isalpha(string[i])) {
// check if value is in the valid alphabet range
if (valid_lower_case || valid_upper_case) {
final_val = ascii_val + key;
} else {
// for lowercase: wrap around if the letter passes 'z'
final_val = 97 + (key - (122 - (ascii_val - 1)));
}
} else {
final_val = ascii_val;
}
strcpy(str1, &final_val);
strcat(str2, str1);
}
for (int i = 0; i < 5; i++) {
printf("%i\n", str2[i]);
}
printf("ciphertext: %s\n", str2);
}
bool input_valid(int count, string arg) {
// input has more args than just the file name
// input is an integer
return count > 1 && isdigit(arg[0]);
}

strcpy(str1, &final_val); is undefined behavior. strcpy expects both parameters to be pointers to null-terminated strings. However, since arrays decay to pointers when passed to functions, strcpy doesn't know the difference between a pointer to an array of characters and the address of a single char variable.
It will try to copy memory beginning at &final_val into str1, only stopping when it encounters a null terminator elsewhere in your process' memory, if there is one. To copy a single character to a string, just use str[x] = ch and str[x + 1] = '\0'.

Replaced strcopy() and strcat(). Got it to work by appending to str2 like this:
str2[i] = final_val;

Related

Why I am getting an empty value in this implementation of Caesar's cipher?

I've implemented the Caesar's cipher in C, and, despite the algorithm is working, I didn't understood why (sometimes) I get an empty value if I do not subtract the first letter of the alphabet before adding the key. Here's the full code (see line 59 or search for return (letter + k) % 26):
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
string caesar_cipher(string text, int k);
char replace_letter(char letter, int k);
bool is_numeric(string input);
int main(int argc, string argv[])
{
if (argc != 2 || (argc == 2 && !is_numeric(argv[1])))
{
fprintf(stderr, "You must specify a key to the cipher! Exiting...\n");
exit(EXIT_FAILURE);
}
// Convert command line argument to integer.
int k = atoi(argv[1]);
// Prompts user for the text to encrypt
string text = get_string("plaintext: ");
// Returns encrypted text
printf("ciphertext: %s\n", caesar_cipher(text, k));
exit(EXIT_SUCCESS);
}
string caesar_cipher(string text, int k)
{
int text_length = strlen(text);
string ciphered_text = text;
for (int i = 0; text[i] != '\0'; i++)
{
ciphered_text[i] = replace_letter(text[i], k);
}
return ciphered_text;
}
char replace_letter(char letter, int k)
{
// Early return when 'letter' is a non-alphabetical character
if (!isalpha(letter))
{
return letter;
}
char operation_letter = 'a';
if (isupper(letter))
{
operation_letter = 'A';
}
// return (letter + k) % 26; // Sometimes, returns an empty value
return ((letter - operation_letter + k) % 26) + operation_letter;
}
// Loop over characters to check if each one of them is numeric
bool is_numeric(string input)
{
for (int i = 0; input[i] != '\0'; i++)
{
// If character is not numeric
// returns false.
if (isdigit(input[i]) == 0)
{
return false;
}
}
return true;
}
Can anybody explain why this happens?
You need to account for the first letter of the alphabet (either a or A) in your functions because chars are internally represented as an integer number (usually only a single byte, but it depends on the encoding). In ASCII for example, doing a % 26 will result in any of the 26 first values of the ASCII table, none of which are actual letters. Hopefully I made myself clear.

Why am I getting errors?

I'm trying to create a program that accepts cmd line arguments to encipher a plaintext!
The program must accept one cmd line argument after its name when making it and this would be the key which by the plaintext (only) alphabetical characters are rotated by this key (e.g. it's number is added to the real alphabet ASCII number resulting in another alphabet to be printed!
it is supposed to print an error message when one argument is present (e.g. here:/make encipher)
instead of here:/make encipher 12 <-- 12 = key!
I am getting a segmentation fault when running the program without the key argument, why?
This is the full code. I'm posting it because I need to learn where is my fault's exact location
and why is it triggered?!
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h> // To use atoi (converting a string to an int)
#include <ctype.h>
#include <string.h>
bool key_is_numb(string argv[]);
void encipher(string txt, int key);
int main(int argc, string argv[])
{
if (key_is_numb(argv) == false)
{
printf("Usage: ./caesar key\n");
return 1;
}
else
{
int key = atoi(argv[1]);
string plaintext;
if (argc == 2 && key > 0)
{
plaintext = get_string("plaintext: ");
encipher(plaintext, key); // A function that prints the ciphered text
return 0; // returns Zero as main return value which means "All good"!
}
else if (argc == 1 || argc > 2 || key <= 0)
{
printf("Usage: ./caesar key\n");
return 1;
}
} // End else.
} // End main()å func.
bool key_is_numb(string argv[])
{
int n = strlen(argv[1]);
for (int i = 0; i < n; i++) // checking element by element in the second string of the argv[] array of strings
{
if (isdigit(argv[1][i]) == 0) // if the entered string "key" contains chars other than digits.
{
return false; // break out of the if statement & the entire function key_is_numb()
// and return false as soon as a letter is encountered.
}
else
{
continue; // go up & start the next iteration for the for loop.
}
// if only digits encountered then this for loop will come to an end and exist from here.
} // End for loop
return true; // function exits and return boolean true from here.
} // End key_is_numb() func.
void encipher(string txt, int key)
{
printf("ciphertext: ");
for (int i = 0, n = strlen(txt); i <= n; i++) // strlen counts the number of elements in a string excluding '\0'
{
char c = txt[i];
if (isalpha(c))
{
if (isupper(c))
{
char m = 'A'; // This is a modifyer character equals to 'A' = 65 so that it is indexed # ZERO!
printf("%c", (c - m + key) % 26 + m );
//c = ((((int)txt[i] - 65) + key) % 26) + 65; // char c = 65 <-- 65 is an ASCII code equals 'A'
}
else if (islower(c))
{
char m = 'a'; // This is a modifying character 'a' = 97
printf("%c", (c - m + key) % 26 + m );
}
}// End if(alpha).
else
{
printf("%c", c);
}
} // End for().
printf("\n");
} // End encipher() func.
int n = strlen(argv[1]);
in key_is_numb() and
int key = atoi(argv[1]);
in main().
If you didn't enter a key argument, argv[1] as equal as argv[argc] is a null pointer as stated in C17, §5.1.2.2.1/2.
Any attempt to access its data is undefined behavior and probably caused the segmentation fault.
Well you are assuming that argv[1] is defined in key_is_numb. However, in C and C++, the second parameter of the main function contains command line arguments. Which, in your case will be the name of the binary as the first element, then any other arguments. This is why when you are running the program without arguments, it will segfault, as there are no argument to put in argv, and no default value either.
You should always check the size of argv, by using the number stored in argc, before trying to read anything in argv.
Your segmentation fault comes from this line int n = strlen(argv[1]);, but I'd highly suggest you to learn to use debugger software like valgrind, which if the program has been compiled with debug flag will tell you the exact line.
Other debugger are really useful too, so you should learn to use them, as they usually report this kind of errors.
Your code asumes there is always an argv[1]. You should check argc which tells the number of arguments. For example:
int main(int argc, string argv[])
{
if (argc < 2) {
printf("Key required\n");
exit (1);
}

How Do I Rotate A Char Alphabetically By Integer X?

I've been trying different solutions but am not sure where to look for the solution.
I prompt the user for "plaintext" and whatever their input is, the chars in their input need to be rotated alphabetically by a number (aka the key) which they provide.
For example: plaintext: HELLO would spit out ciphertext: IFMMP if the key were 1.
Assuming the key will always be a number, here is what my code looks like which attempts to rotate each char by Key: 1. I'm a real noob so please break it down is possible.
{
string s = get_string("plaintext: ");
printf("ciphertext: %s\n", s + 1);
}
The remaining code (which includes identifying and filtering out the key is:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
int counter = 0;
if (argc == 2)
{
for(int k = 0; k <strlen(argv[1]); k++)
{
if (isdigit(argv[1][k]))
{
counter++;
}
}
if (strlen(argv[1]) == counter)
{
string s = get_string("plaintext: ");
if(s)
{
printf("ciphertext: %s\n", s + 1);
free(s);
}
}
else
{
printf("Usage: ./caesar key\n");
}
}
else
{
printf("Usage: ./caesar key\n");
}
}
Any assistance would be appreciated.
I do not have cs50, so cannot test everything in your code, but addressing your primary question: How Do I Rotate A Char Alphabetically By Integer X, the code section you have identified as where the rotation occurs:
if (strlen(argv[1]) == counter)
{
string s = get_string("plaintext: ");
printf("ciphertext: %s\n", s + 1);
}
But this section doesn't really rotate the text. It rather attempts to print the string obtained from the call to get_string after adding 1 to it. First, this is not modifying s at all. Second, s + 1 is not a legal operation. To rotate s, additional code is needed between those two lines.
If I understand get_string(), it is essentially a combination of printf(), fgets(,,stdin) and strdup().
So after the call to that function you are left with s which will be populated with whatever the user typed into stdin. For illustration, let
say s contains "what the user typed". How that string is represented in memory, including the NULL terminator, can be depicted as follows:
|w|h|a|t| |t|h|e| |u|s|e|r| |t|y|p|e|d|0|
To rotate this by X, as you have stated, each character needs to be modified by adding the value X, in this case 1
Showing ASCII equivalent values:
|119|104|97|116|32|116|104|101|32|117|115|101|114|32|116|121|112|101|100|0|
The rotated string then would be each value + 1:
For the general case, assume rotation value is read in from command line argument and placed into int n = 0;
earlier in code:
if(isdigit(argv[2][0]))
{
n = atoi(argv[2])
}
The rotation: (the following is complete. It may need a little debugging around the corner case [where values wrap around the end of ASCII values when added to n.] Lines are commented indicating where I used ANSI C instead of CS50. [I do not have CS50])
int main(int argc, char *argv[])//note: command line requires a numeric
// argument be used: prog.exe 12 (for n == 12)
{
//char s[80];//used to test (I do not have cs50)
char *rotated = NULL;
int i;
if(argc != 2)
{
printf("Usage: prog.exe <positive int value.>\nExiting\n");
return 0;
}
if(isdigit(argv[1][0]) == 0)
{
printf("Usage: prog.exe <positive int value.>\nExiting\n");
return 0;
}
int n = atoi(argv[1]);
string s = get_string("plaintext: ");
// printf("Enter text to be rotated\n");//used to test (I do not have cs50)
// scanf("%[^\n]", s);
// string rotated = strdup(s); //preserve s by manipulating identical string
rotated = StrDup(s);
if(rotated)
{ //advance value by n
int origVal = 0;
int len = strlen(rotated);
for(i=0;i<len;i++)
{
origVal = rotated[i];
if((rotated[i] + n) <= 127)
{
rotated[i] += n;
}
else
{
rotated[i] = abs(127 - (origVal + n));
if(rotated[i] == 0) rotated[i] += 23; //skip to printable characters (http://facweb.cs.depaul.edu/sjost/it212/documents/ascii-pr.htm)
//if(rotated[i] == 0) rotated[i] += 1; //skip only ASCII NULL value
}
}
rotated[i] = 0; //adding NULL to end of string
printf("ciphertext: %s\n", rotated);
free(rotated);
}
return 0;
}
You can achieve this with typecasting:
char input = 'f';
char output = (char)((int)input + 1);
You should also handle edge cases, i.e. when the character value is near the end of the alphabet.

A program that prints even and odd characters from a string

This is for Homework
I have to write a program that asks the user to enter a string, then my program would separate the even and odd values from the entered string. Here is my program.
#include <stdio.h>
#include <string.h>
int main(void) {
char *str[41];
char odd[21];
char even[21];
int i = 0;
int j = 0;
int k = 0;
printf("Enter a string (40 characters maximum): ");
scanf("%s", &str);
while (&str[i] < 41) {
if (i % 2 == 0) {
odd[j++] = *str[i];
} else {
even[k++] = *str[i];
}
i++;
}
printf("The even string is:%s\n ", even);
printf("The odd string is:%s\n ", odd);
return 0;
}
When I try and compile my program I get two warnings:
For my scanf I get "format '%s' expects arguments of type char but argument has 'char * (*)[41]". I'm not sure what this means but I assume it's because of the array initialization.
On the while loop it gives me the warning that says comparison between pointer and integer. I'm not sure what that means either and I thought it was legal in C to make that comparison.
When I compile the program, I get random characters for both the even and odd string.
Any help would be appreciated!
this declaration is wrong:
char *str[41];
you're declaring 41 uninitialized strings. You want:
char str[41];
then, scanf("%40s" , str);, no & and limit the input size (safety)
then the loop (where your while (str[i]<41) is wrong, it probably ends at once since letters start at 65 (ascii code for "A"). You wanted to test i against 41 but test str[i] against \0 instead, else you get all the garbage after nul-termination char in one of odd or even strings if the string is not exactly 40 bytes long)
while (str[i]) {
if (i % 2 == 0) {
odd[j++] = str[i];
} else {
even[k++] = str[i];
}
i++;
}
if you want to use a pointer (assignement requirement), just define str as before:
char str[41];
scan the input value on it as indicated above, then point on it:
char *p = str;
And now that you defined a pointer on a buffer, if you're required to use deference instead of index access you can do:
while (*p) { // test end of string termination
if (i % 2 == 0) { // if ((p-str) % 2 == 0) { would allow to get rid of i
odd[j++] = *p;
} else {
even[k++] = *p;
}
p++;
i++;
}
(we have to increase i for the even/odd test, or we would have to test p-str evenness)
aaaand last classical mistake (thanks to last-minute comments), even & odd aren't null terminated so the risk of getting garbage at the end when printing them, you need:
even[k] = odd[j] = '\0';
(as another answer states, check the concept of even & odd, the expected result may be the other way round)
There are multiple problems in your code:
You define an array of pointers char *str[41], not an array of char.
You should pass the array to scanf instead of its address: When passed to a function, an array decays into a pointer to its first element.
You should limit the number of characters read by scanf.
You should iterate until the end of the string, not on all elements of the array, especially with (&str[i] < 41) that compares the address of the ith element with the value 41, which is meaningless. The end of the string is the null terminator which can be tested with (str[i] != '\0').
You should read the characters from str with str[i].
You should null terminate the even and odd arrays.
Here is a modified version:
#include <stdio.h>
int main(void) {
char str[41];
char odd[21];
char even[21];
int i = 0;
int j = 0;
int k = 0;
printf("Enter a string (40 characters maximum): ");
if (scanf("%40s", str) != 1)
return 1;
while (str[i] != '\0') {
if (i % 2 == 0) {
odd[j++] = str[i];
} else {
even[k++] = str[i];
}
i++;
}
odd[j] = even[k] = '\0';
printf("The even string is: %s\n", even);
printf("The odd string is: %s\n", odd);
return 0;
}
Note that your interpretation of even and odd characters assumes 1-based offsets, ie: the first character is an odd character. This is not consistent with the C approach where an even characters would be interpreted as having en even offset from the beginning of the string, starting at 0.
Many answers all ready point out the original code`s problems.
Below are some ideas to reduce memory usage as the 2 arrays odd[], even[] are not needed.
As the "even" characters are seen, print them out.
As the "odd" characters are seen, move them to the first part of the array.
Alternative print: If code used "%.*s", the array does not need a null character termination.
#include <stdio.h>
#include <string.h>
int main(void) {
char str[41];
printf("Enter a string (40 characters maximum): ");
fflush(stdout);
if (scanf("%40s", str) == 1) {
int i;
printf("The even string is:");
for (i = 0; str[i]; i++) {
if (i % 2 == 0) {
str[i / 2] = str[i]; // copy character to an earlier part of `str[]`
} else {
putchar(str[i]);
}
}
printf("\n");
printf("The odd string is:%.*s\n ", (i + 1) / 2, str);
}
return 0;
}
or simply
printf("The even string is:");
for (int i = 0; str[i]; i++) {
if (i % 2 != 0) {
putchar(str[i]);
}
}
printf("\n");
printf("The odd string is:");
for (int i = 0; str[i]; i++) {
if (i % 2 == 0) {
putchar(str[i]);
}
}
printf("\n");
here is your solution :)
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[41];
char odd[21];
char even[21];
int i = 0;
int j = 0;
int k = 0;
printf("Enter a string (40 characters maximum): ");
scanf("%s" , str);
while (i < strlen(str))
{
if (i % 2 == 0) {
odd[j++] = str[i];
} else {
even[k++] = str[i];
}
i++;
}
odd[j] = '\0';
even[k] = '\0';
printf("The even string is:%s\n " , even);
printf("The odd string is:%s\n " , odd);
return 0;
}
solved the mistake in the declaration, the scanning string value, condition of the while loop and assignment of element of array. :)

Simple String Encryption in C

I'm attempting to create a very simple encryption where a string is scanned, and the ascii code is increased by 5 (+5). When the letters reach the end of the alphabet it wraps back around to the beginning (same with numbers). punctuation and any other symbols are not encrypted. The encryption function seems to be working, however I'm having trouble passing the string back to the main function. What has gone wrong?
Here is my code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/*function definitions */
char encrypt(char input_string[]);
int main(void)
{
char input_string[25];
char new_string[25];
char encrypted_string[25];
int i = 0;
// scan word
printf("Enter word: ");
scanf(" %[^\n]", input_string);
fflush(stdin);
//check input
printf("%s\n", input_string);
// call encrypt function
encrypted_string[25] = encrypt(input_string);
// print encrypted version
printf("encrypted: %s\n\n", encrypted_string);
return 0;
}
char encrypt(char input_string[])
{
char new_string;
static char str[25];
int i;
for(i = 0; i < 25; i++)
{
if (input_string[i] > 96 && input_string[i] < 118) // for a - u
{
new_string = (input_string[i] + 5);
}
else if (input_string[i] > 117 && input_string[i] < 123) // for v - z
{
new_string = (input_string[i] - 21);
}
else if (input_string[i] > 64 && input_string[i] < 86) // for A - U
{
new_string = (input_string[i] + 5);
}
else if (input_string[i] > 85 && input_string[i] < 91) // for V - Z
{
new_string = (input_string[i] - 21);
}
else if (input_string[i] > 47 && input_string[i] < 53) // for 0 - 4
{
new_string = (input_string[i] + 5);
}
else if (input_string[i] > 52 && input_string[i] < 58) // for 5 - 9
{
new_string = (input_string[i] - 5);
}
else
{
new_string = input_string[i];
}
str[i] = new_string;
}
printf("\n\n");
i=0;
//check asccii codes
while(str[i])
{
printf("%d ", str[i++]);
}
//print string
printf("\n\n%s\n\n", str);
//return string to main
return(str[25]);
}
return(str[25]); means you're trying to return the value of the 26th element,[which is out-of-bound access], not the address of the string.
encrypted_string[25] = encrypt(input_string);
No, you cannot assign a string return like that. It's wrong in many ways.
To correct, follow the below steps.
change char encrypt(char input_string[]) to char* encrypt(char input_string[])
use return(str); instead of return(str[25]);
instead of char encrypted_string[25];, write char * encrypted_string;
NOTE:
using static array will do the job, but IMO, a better approach will be allocating the array dynamically and return the pointer after populating from encrypt() function. You can later free() the pointer from main().
Your function:
char encrypt(char input_string[])
literally says that it accepts a long string as input, and returns just a single character as output.
That seems very dubious.
Don't you want it to return a string as output, not just one letter?

Resources