Checklist:
This program takes two strings and displays, without doubles, the
characters that appear in either one of the strings.
The display will be in the order characters appear in the command
line, and will be followed by a \n.
If the number of arguments is not 2, the program displays \n.
Code:
#include <unistd.h>
int main(int argc, char *argv[])
{
int used[255] = {0};
int i = 1, j = 0;
if(argc == 3)
{
while(i < 3)
{
j = 0;
while(argv[i][j])
{
if(!used[(unsigned char)argv[i][j]])
{
used[(unsigned char)argv[i][j]] = 1;
write(1, &argv[i][j], 1);
}
j++;
}
i++;
}
}
write(1, "\n", 1);
return (0);
}
I understand argument passing part that
argv[0] is the program name,argv[1] is the second string in the string array argv, and strings are character arrays so argv[1][0] is the first character in the second string, argv[1][1] is the second character in the second string and so on.
but how does the below part works how is it printing unique characters only once from both arguments?
j = 0;
while(argv[i][j])//while argv[i][j] != '\0'
{
if(!used[(unsigned char)argv[i][j]])
{
used[(unsigned char)argv[i][j]] = 1;
write(1, &argv[i][j], 1);
}
j++;
}
I cannot get the logic. Somebody, who is a C expert, please help me to understand the logic. Thanks for your help.
if(!used[(unsigned char)argv[i][j]])
This is using the char as an index in the array. So for example everytime you get the char 'e' it will check the index 101 in the array and see if it's been used already. Basically it's directly mapping ASCII values to an array. https://theasciicode.com.ar/
It's perhaps easier to understand if you break the code up a bit:
int index = argv[i][j]; //Use the character's ASCII value as an index.
if(used[index] == 0) //If this character has NOT appeared before
{
used[index] = 1; //Mark this index as "used"
//do stuff with unique character.
}
Related
My intention was to use the command line to read input and store it into an array and modify the characters of the array. If the character is '1', then turn it into '0', vice versa. I successfully store the input into an array, yet failed to modify the characters of the array. If I put 0000000000000000000000000000000(32bits) into my program, the output doesn't change.
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
char *a = argv[argc-1];
char arr[33];
size_t length = strlen(a);
for(size_t i=0;i<length;i++) {
arr[i]=a[i];
}
for(int j=0; j<32;j++) {
if(arr[j]=='0') {
arr[j]='1';
}
if(arr[j]=='1') {
arr[j]='0';
}
}
for(int k=0;k<32;k++) {
printf("%c",arr[k]);
}
}
If I put 0000000000000000000000000000000(32bits) into my program, the output doesn't change.
First of all, your program is not processing bits of input but it is processing the characters of input you are passing as command line argument to your program. If you want to process the bits of input, to start with, read about bitwise operators first.
Look at this for loop:
for(int j=0; j<32;j++) {
if(arr[j]=='0') {
arr[j]='1';
}
if(arr[j]=='1') {
arr[j]='0';
}
}
If current processing arr element value is '0', the second if condition in the for loop body will nullify the effect of first if condition on that arr element i.e. the arr element value will be unchanged and if the current processing arr element value is '1' than it will be set to '0'. So, after the loop, all the elements of array arr, which have original value as either '1' or '0', will set to '0' and that's the flaw in your logic.
Few suggestions:
If program is supposed to receive command line argument('s), make sure to put check on argc value. In your case, user is supposed to pass the string comprised of '0's and '1's, so you should check on argc value, something like:
if (argc != 2) {
printf ("Usage: <exe_name> <string_of_0s_and_1s>\n");
return 1;
}
Since, you do not have check on argc value in your program, if user do not pass any string from command line, the value of argc may be 1 and ,in this case, argv[argc-1] (or argv[0]) represents the program name or if the program name is not available from the host environment than argv[0][0] will be the null character. If argv[0] represents program name whose length is greater than 32 characters then your program will land up in undefined behaviour territory because there is no check on whether the command line input string length is <= 32 characters while copying it to arr buffer.
Since you are copying the input to char array (not as string) declare arr as array of 32 characters and not 33 characters. Looks like, you have assumed the input will be of size <= 32 characters. There is no problem with this assumption as long as program is running in a controlled environment where you are taking care of it. But still, by chance, if input string length is > 32 characters then your program behaviour will be undefined as it will end up processing array arr beyond it's size. You should add a check on length of input string as well:
if (length > 32) {
printf ("Input string length is >= 33 character.\n" \
"The permitted input string length is <= 32 characters.\nExiting..\n");
return 1;
}
If you want the input string to be of exact 32 characters, you can add check if (length != 32).
While copying the string, you can also add check on the characters of input string and if it contain character other than '1' and '0' then throw error message and exit.
Putting these altogether :
#include <stdio.h>
#include <string.h>
int main (int argc, char *argv[]) {
char *a = NULL;
char arr[32];
if (argc != 2) {
printf ("Usage: <exe_name> <string_of_0s_and_1s>\n");
return 1;
}
a = argv[argc-1];
size_t length = strlen(a);
if (length > 32) {
printf ("Input string length is > 32 characters.\n" \
"The permitted input string length is <= 32 characters.\nExiting..\n");
return 1;
}
for (size_t i = 0; i < length; i++) {
if ((a[i] != '1') && (a[i] != '0')) {
printf ("Found a character other than characters '1' and '0' in the input string.\n" \
"The input string should comprised of characters `1' and '0' only.\nExiting..");
return 1;
}
arr[i] = a[i];
}
for (size_t j = 0; j < length; j++) {
if (arr[j] == '0') {
arr[j] = '1';
} else {
arr[j] = '0';
}
}
for (size_t k = 0; k < length; k++) {
printf ("%c", arr[k]);
}
return 0;
}
you forget to give an else if condition in this part of the code.
for(int j=0; j<32;j++) {
if(arr[j]=='0') {
arr[j]='1';
}
else if(arr[j]=='1') { //This part should be else if
arr[j]='0';
}
}
In your code for the value of 0 it changed to 1. But for another if condition the value changed to 0 again.
To copy string you need to copy null character as well.
size_t length = strlen(a);
14 for(size_t i=0;i<=length;i++){
15 arr[i]=a[i];
But you do not need to traverse the string twice
size_t i=0;
while((arr[i]=a[i])) i++;
Your if is wrong. It should be
if(arr[j]=='0') arr[j]='1';
else arr[j]='0';
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);
}
I am trying to read multiple characters from an argument in c. So when the person rules the file like "./amazing_program qwertyyuiopasdfghjklzxcvbnm" it would read the qwerty characters and store the, into a array as a number (ASCII) like:
array[0] = 'q';
array[1] = 'w';
array[2] = 'e';
array[3] = 'r';
array[4] = 't';
array[5] = 'y';
and so on...
My goal: Is to separate the argument into each individual character and store each individual character into a different place in the array (like shown above).
I tried this way, but it didn't work.
int user_sub = 0;
int argument = 1;
while (argument < argc) {
user_sub = atoi(argv[argument]);
argument = argument + 1;
}
From reading your comments, I've come to understand you just want to be able to get to the characters so you can do a shift. Well, that's not so hard to do, so I've tried to show you how you can do it here without having to complete the Caesar logic for you.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SHIFT 13
int main (int argc, const char *argv[]) {
// Verify they gave exactly one input string.
if (argc != 2) {
fprintf(stderr, "Usage: %s <word>\n", argv[0]);
exit(EXIT_FAILURE);
}
// A string IS already an array of characters. So shift then and output.
int n = strlen(argv[1]);
for (int i = 0; i < n; i++) {
char c = argv[1][i];
// Shift logic here: putchar(...);
printf("%d: %c\n", i, c);
}
return EXIT_SUCCESS;
}
The key takeaway is that a string is already an array. You don't need to make a new array and stick all the characters in it. You already have one. What this program does is simply "extract" and print them for you so you can see this. It currently only writes the current argument string to output, and does no shifting. That's for you to do. It also doesn't take into account non-alphabetical characters. You'll have to think about them yourself.
You have serious lack :
1)
A string in C is an ARRAY of type char. We know where the end of the array is thank to a special value : '\0'.
Now, you have to deeply understand that each case of the array contain a NUMBER : since the type of the case is char, it will be a number in the range [-128, 127] (yeah, I know that char is special and can be signed or not, but let's keep it simple for the time being).
So if you acces each case of the array and print it, you will have a number between -128 and 127. So how the program know to print a letter instead of a number ? And how do he know which letter for which number ?
Thank to an internal table used for this uniq purpose. The most common is the ASCII table. So if a case of the array is 65, what will be printed is 'A'.
2) How can I go through each case of a string ? (which is an array of char terminated by '\0') ?
Simply with a for loop.
char str[] = "test example";
for (size_t i = 0; str[i] != '\0'; ++i) {
printf("The %d letter is '%c'\n", i, str[i]);
}
Again, since it's a number in str[i], how the program know how to print a letter ? Thank to the "%c" in printf, meaning "print the letter using the table (probably ASCII)". If you use "%s", it's the same thing, but you have to give the array itself instead of a case of the array.
So, what if I want to print the number instead of the letter ? Just use "%d" in printf.
char str[] = "test example";
for (size_t i = 0; str[i] != '\0'; ++i) {
printf("The %d letter is '%c' and it's real value is %d\n", i, str[i], str[i]);
}
Now, what if we increment all the value in each case of the string ?
char str[] = "test example";
for (size_t i = 0; str[i] != '\0'; ++i) {
str[i] = str[i] + 1; // Or ++str[i];
}
for (size_t i = 0; str[i] != '\0'; ++i) {
printf("The %d letter is '%c' and it's real value is %d\n", i, str[i], str[i]);
}
We have changed the string "test example" into "uftu fybnqmf".
Now, for your problem, you have to take the resolution step by step :
First, make a function that alter (cypher) a string given in argument by adding a shift.
void CesarCypherString(char *string);
Beware of "overflow" ! If I want to have a shift of 5, then 'a' will become 'f', but what happen for 'z' ? It should be 'e'.
But if you look at the ascii table, 'a' = 97, 'f' = 102 (and it make sense, since 'a' + 5 = 'f', 97 + 5 = 102), but 'z' is 122 and 'e' is 101. So you cannot directly do 'z' + 5 = 'e' since it's wrong.
Hint : use modulo operator (%).
Next, when you have finished to do the function CesarCypherString, do the function CesarDecypherString that will decypher a string.
When you have finished, then you can concentrate on how to read/duplicate a string from argv.
For the love of holy code, I am trying to compare hashes to find the correct password. I am given a hash as a command line argument, and I then hash words from "a" to "ZZZZ" until one of the hash pairs match.
void decipher(string hash)
{
//Set the password, and the salt.
char pass[4] = "a";
char salt[] ="50";
//Compare the crypted pass againts the hash until found.
while (strcmp(hash,crypt(pass, salt)) != 0)
{
//Use int i to hold position, and return next char
int i = 0;
pass[i] = get_next(pass[i]);
tick_over (pass, i);
//Hardcode in a fail safe max length: exit.
if (strlen(pass) > 4)
{
break;
}
}
printf("%s\n", pass);
}
The problem is that it will not 'catch' the correct password / comparison, when that password is 4 letters long. It works for 1,2 and 3 letter long words.
//Tick over casino style
string tick_over (string pass, int i)
{
//Once a char reaches 'Z', move the next char in line up one value.
char a[] = "a";
if (pass[i] == 'Z')
{
if (strlen(pass) < i+2)
{
strncat (pass, &a[0], 1);
return pass;
}
pass[i+1] = get_next(pass[i+1]);
//Recursively run again, moving along the string as necessary
tick_over (pass, i+1);
}
return pass;
}
//Give the next character in the sequence of available characters
char get_next (char y)
{
if (y == 'z')
{
return 'A';
}
else if (y == 'Z')
{
return 'a';
}
else
{
return y + 1;
}
}
It does iterate through the correct word, as I have found in debugging. I have tried moving the
strcmp(hash, crypt(pass, salt)) == 0
into a nested if statement among other things, but it doesn't seem to be the problem. Is c somehow 'forgetting' the command line value? When debugging the hash value seemed to have disappeared :/ Please help!
char pass[4] = "a"; you're defining a char array which can contain at most 3 chars + null terminator.
that's not coherent with your "safety" test: if (strlen(pass) > 4)
When strlen is 4 the array is already overwriting something in memory because of the null termination char: undefined behaviour.
Quickfix: char pass[5] ...
Here is the explanation of the function strncat:
Append characters from string
Appends the first num characters of source to destination, plus a terminating null-character.
with a size of 4 you are not considering the terminating null character of your four chars array.
For an assignment I am doing, I have to print out the initials from a string in C (or, an array of characters). To do this, I know that I need to find where the SPACE is, using (int)name[i] == 32 to find if the current character is a SPACE. My only issue with this is that I am having trouble figuring out how I can find the space, and then store the next character in the array of character. (e.g., user enters Mike Baggins, I have to print out MB). I will post my code below, to show you how far I've gotten. Please help, but please don't give me the full solution. Thank you!
#include <cs50.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
string name = get_string(); // gets the user's input
char firstI = name[0]; // stores the first character from the user's input
int len = strlen(name);
if((int)firstI >= 97 && (int)firstI <= 122) // checks if the character is lowercase
{
firstI -= 32; // makes the value uppercase
}
for(int i = 0; i < len; i++)
{
if((int)name[i] == 32) // checks if the character is SPACE
{
printf("I found a space!\n"); // prints out "I found a space"
}
}
printf("%c\n", firstI); // prints out the first initial
}
It's actually simple, see this for example
#include <stdio.h>
#include <ctype.h>
int
main(void)
{
char array[10];
char string[] = "Mike Baggins";
int j;
// Always put the first non-whitespace
// character (we should probably skip all
// spaces first
array[0] = string[0];
// Now start at the character following the first
j = 1;
for (int i = 1; ((string[i - 1] != '\0') && (j < sizeof(array) - 1)); ++i) {
if (string[i - 1] == ' ') {
array[j++] = toupper(string[i]);
}
}
array[j] = '\0';
puts(array);
return 0;
}
All I needed was to know that strings are simply arrays with a special value marking the end of them → '\0'. Of course, you can improve it a lot. For instance, you could count how many initials are there in the input string and allocate enough space to store them all.
Also, this will only work if the interesting character follows the space immediately, but knowing that it's just an array I am sure you can figure out how to extend it to make it ignore consecutive spaces.