for-loop does not terminate - c

I'm trying to figure out why my innermost for loop does not break.
I have implemented a Caesar cipher for a "list" of char arrays.
void caesar(char **list, size_t listsize, unsigned int offset) {
for (size_t i = 0; i < listsize; i++) {
for (size_t j = 0; list[i] != NULL && j < sizeof(list[i]); j++) {
for (unsigned int k = 0; k < offset && (list[i][j] >= 65 && list[i][j] <= 90) || (list[i][j] >= 97 && list[i][j] <= 122); k++) {
if(list[i][j] == 122)
list[i][j] = 97;
else if(list[i][j] == 90)
list[i][j] = 65;
else
list[i][j]++;
//works with the break condition
/* if(k == offset - 1)
break;*/
}
}
}
}
So without the last break statement I get an endless loop, even though I have a break condition in the head of my for loop.
Does anybody have an idea?

for (unsigned int k = 0; k < offset && (list[i][j] >= 65 && list[i][j] <= 90) || (list[i][j] >= 97 && list[i][j] <= 122); k++)
Using some placeholder identifiers, the loop condition is written in the form a && b || c, which due to operator precedence is parsed as (a && b) || c and not the a && (b || c) you intend. You can add some parentheses to fix it:
for (unsigned int k = 0; k < offset && ((list[i][j] >= 65 && list[i][j] <= 90) || (list[i][j] >= 97 && list[i][j] <= 122)); k++)
// ^ ^

Related

Changing string length

I had just started my journey of coding. I am trying the palindrome problem.
Here I am not getting why my string length is not getting modified.
#include <bits/stdc++.h>
using namespace std;
int main()
{
string s = "c1 O$d#eeD o1c";
int len = s.size();
int j = 0;
for(int i = 0 ; i < len ; i++){
if((s[i] >= 'a' && s[i] <= 'z') || (s[i] >= 'A' && s[i] <= 'Z') || (s[i]>='1' && s[i] <= '9')){
if(isupper(s[i])){
s[i] = tolower(s[i]);
}
s[j] = s[i];
j++;
}
}
cout<<endl;
cout<<len<<" "<<j<<endl;
s[j] = '\0'; ** //Not getting refleced in original string**
cout<<s.size()<<endl; **// same as original string though in my case is must be smaller.**
cout<<s<<endl;
}
Output :
14 10
14
c1odeedo1co1c

a c program that output a combation of 3 numbers, stocked in an array

Trying to write a c programm that writes a combination of 3 set of numbers and avoid reputation, not very good in C language. Something is wrong with one of my condition
int ft_putchar(char c){
write(1, &c, 1);
}
void ft_print_comb(void){
int numbers[3] = {48, 48, 48};
while(numbers[0] <= 55){
if((numbers[0] < numbers[1]) && (numbers[1] < numbers[2])){
ft_putchar(numbers[0]);
ft_putchar(numbers[1]);
ft_putchar(numbers[2]);
if(numbers[0] != 55){
ft_putchar(',');
}
if(numbers[0] != 55){
ft_putchar(' ');
}
if(numbers[2]++ >= 57){
numbers[2] = 48;
numbers[1]++;
}
if(numbers[1] >= 57){
numbers[1] = 48;
numbers[0]++;
}
}
}
}
int main(void){
ft_print_comb();
}
The issue is you create an infinite loop. Since all 3 numbers start out equal, the condition
if((numbers[0] < numbers[1]) && (numbers[1] < numbers[2]))
is false, so the body of the if never gets entered, numbers[0] never gets incremented, and while(numbers[0] <= 55){ is always true.
You can get (what I think is) the desired output with nested for loops:
int numbers[3] = {'0', '0', '0'};
for (int i = numbers[0]; i <= '9'; i++) {
for (int j = numbers[1]; j <= '9'; j++) {
for (int k = numbers[2]; k <= '9'; k++) {
if (i < j && j < k) {
printf("%c%c%c, ", i, j, k);
}
}
}
}

How to repeat characters in array

I am trying to implement Vigenere's Cipher in C but the problem is that when I try to repeat the key used in the array it is in, it breaks after the 4th letter. So if the key is ABC and the plaintext is HELLO, it returns HFNLO instead of HFNLP. When I look at my code it logically makes sense but it seems to just not work. Can anybody see the problem?
Here is the code:
int main(int argc, string argv[])
{
if(argc != 2)
{
printf("usage: ./vigenere k\n");
return 1;
}
//asks for plain text
printf("plaintext: ");
string text = get_string();
string k = argv[1];
printf("ciphertext: ");
//checks to see if length of key is shorter than length of plaintext and duplicates it.
int count = 0;
while(strlen(k) <= strlen(text))
{
k[strlen(k + count)] = k[count];
count++;
}
//changes key to be within 0 - 25 and encrypts plaintext
for(int i = 0; i < strlen(text); i++)
{
if(k[i] >= 'A' && k[i] <= 'Z')
{
k[i] = k[i] - 65;
}
else if (k[i] >= 'a' && k[i] <= 'z')
{
k[i] = k[i] - 97;
}
//if statement for plaintext capital letters
if(text[i] >= 'A' && text[i] <= 'Z')
{
text[i] = text[i] - 64;
text[i] = ((text[i] + k[i]) % 26) + 64;
}
//if statement for plaintext lowercase letters
else if(text[i] >= 'a' && text[i] <= 'z')
{
text[i] = text[i] - 96;
text[i] = ((text[i] + k[i]) % 26) + 96;
}
//prints final cipher
printf("%c", text[i]);
}
printf("\n");
return 0;
}
You should use the modulo operator to compute the offset into the key.
Here is a modified version:
#include <stdio.h>
#include <string.h>
#include <cs50.h>
int main(int argc, string argv[]) {
if (argc != 2) {
printf("usage: ./vigenere k\n");
return 1;
}
string k = argv[1];
size_t klen = strlen(k);
if (klen == 0) {
fprintf(stderr, "vigenere: key must not be empty\n");
return 1;
}
printf("plaintext: ");
string text = get_string();
printf("ciphertext: ");
for (size_t i = 0; text[i] != '\0'; i++) {
int d = (unsigned char)k[i % klen];
if (d >= 'A' && d <= 'Z') {
d -= 'A';
} else
if (d >= 'a' && d <= 'z') {
d -= 'a';
} else {
d = 0;
}
int c = (unsigned char)text[i];
if (c >= 'A' && c <= 'Z') {
c = 'A' + (c - 'A' + d) % 26;
} else
if (c >= 'a' && c <= 'z') {
c = 'a' + (c - 'a' + d) % 26;
}
putchar(c);
}
putchar('\n');
return 0;
}

Caesar Cipher Code Flaw

I have coded a Caesar cipher that seem to work in most tests but fails on a few cases. More on the test details are https://www.hackerrank.com/challenges/caesar-cipher-1
Basic info: The cipher only encrypts letters, symbols etc stay unencrypted.
Fails on this case:
90
!m-rB`-oN!.W`cLAcVbN/CqSoolII!SImji.!w/`Xu`uZa1TWPRq`uRBtok`xPT`lL-zPTc.BSRIhu..-!.!tcl!-U
62
Where 90 is n (characters in string), second line is string in array s, and 62 is k (amount of letter rotations)
Any insight into the flaw of my code will be highly appreciated
Code:
int main(){
int n;
scanf("%d",&n);
char* s = (char *)malloc(10240 * sizeof(char));
scanf("%s",s);
int k;
scanf("%d",&k);
if (k>26) {
k%=26;
}
int rotation;
for(int i = 0; i<n; i++) {
if (s[i] >= 'a' && s[i] <= 'z') {
if((s[i] + k) > 'z' ) {
rotation = (s[i] - 26) + k;
s[i] = rotation;
} else {
s[i] = s[i]+k;
}
} else if (s[i] >= 'A' && s[i] <= 'Z') {
if((s[i] + k) >= 'Z' ) {
rotation = (s[i] - 26) + k;
s[i] = rotation;
} else {
s[i] = s[i]+k;
}
}
}
for(int i=0; i<n; i++) {
printf("%c", s[i]);
}
return 0;
}
Ok guys, so I've figured it out.
Old Code:
if((s[i] + k) >= 'Z' )
New Code:
if((s[i] + k) > 'Z' )
It messed up when given a P(ascii 80), it should have stopped at Z(ascii 90) but instead did this calculation:
s[i] - 26 + k = 64
80 - 26 + 10 = 64 (ascii for #) and thus '#' was returned instead of Z

CS50 - floating point exception arises when methods are called

I'm a new programmer trying to teach myself by doing the psets for CS50. I wrote the following bit of code, which works without a problem.
#include <stdio.h>
#include <cs50.h>
#include <stdlib.h>
#include <string.h>
string vencipher(string text, string key)
{
for (int i=0, j=0, n =strlen(text); i < n; i++,j++)
{
int m = strlen(key);
if (text[i] >= 65 && text[i] <= 90 && key[j % m] >= 65 && key[j % m] <= 90)
{
text[i] = 65 + ((text[i] - 65) + (key[j % m] - 65)) % 26;
}
else if (text[i] >= 65 && text[i] <= 90 && key[j % m] >= 97 && key[j % m] <= 123)
{
text[i] = 65 + ((text[i] - 65) + (key[j % m] - 97)) % 26;
}
else if (text[i] >= 97 && text[i] <= 123 && key[j % m] >= 65 && key[j % m] <= 90)
{
text[i] = 97 + ((text[i] - 97) + (key[j % m] - 65)) % 26;
}
else if (text[i] >= 97 && text[i] <= 123 && key[j % m] >= 97 && key[j % m] <= 123)
{
text[i] = 97 + ((text[i] - 97) + (key[j % m] - 97)) % 26;
}
else
{
text[i] = text[i];
j = j - 1;
}
}
return text;
}
int keyvalidator(string text)
{
int alphalen = 0;
for (int i=0, n=strlen(text); i < n; i++)
{
if ((text[i] >= 97 && text[i] <= 123) || (text[i] >= 65 && text[i] <= 90))
{
alphalen = alphalen + 1;
}
}
if (alphalen == strlen(text))
{
return 1;
}
else
{
return 0;
}
}
int main(int argc, string argv[])
{
if (argc != 2 || keyvalidator(argv[1]) != 1)
{
printf("That is not a valid secret key!\n");
return 1;
}
if (argc == 2)
{
string secretKey = argv[1];
string plainText = GetString();
printf("%s\n", vencipher(plainText, secretKey));
}
return 0;
}
I wanted to try and split up vencipher into some different methods to try and improve the code's readability. This is what I did
#include <stdio.h>
#include <cs50.h>
#include <stdlib.h>
#include <string.h>
string keycaseID(string key)
{
for (int i=0, n=strlen(key); i < n; i++)
{
if (key[i] >= 65 && key[i] <= 90)
{
key[i] = 1;
}
else
{
key[i] = 0;
}
}
return key;
}
string setkeycase(string key)
{
for (int i=0, n=strlen(key); i < n; i++)
{
if (keycaseID(key)[i] == 1)
{
key[i] = key [i] - 65;
}
else if (keycaseID(key)[i] == 0)
{
key[i] = key [i] - 97;
}
}
return key;
}
string vencipher(string text, string key)
{
for (int i=0, j=0, n =strlen(text); i < n; i++,j++)
{
int m = strlen(key);
if (text[i] >= 65 && text[i] <= 90 && keycaseID(key)[j % m] == 1)
{
text[i] = 65 + ((text[i] - 65) + setkeycase(key)[j % m]) % 26;
}
else if (text[i] >= 65 && text[i] <= 90 && keycaseID(key)[j % m] == 0)
{
text[i] = 65 + ((text[i] - 65) + setkeycase(key)[j % m]) % 26;
}
else if (text[i] >= 97 && text[i] <= 123 && keycaseID(key)[j % m] == 1)
{
text[i] = 97 + ((text[i] - 97) + setkeycase(key)[j % m]) % 26;
}
else if (text[i] >= 97 && text[i] <= 123 && keycaseID(key)[j % m] == 0)
{
text[i] = 97 + ((text[i] - 97) + setkeycase(key)[j % m]) % 26;
}
else
{
text[i] = text[i];
j = j - 1;
}
}
return text;
}
int keyvalidator(string text)
{
int alphalen = 0;
for (int i=0, n=strlen(text); i < n; i++)
{
if ((text[i] >= 97 && text[i] <= 123) || (text[i] >= 65 && text[i] <= 90))
{
alphalen = alphalen + 1;
}
}
if (alphalen == strlen(text))
{
return 1;
}
else
{
return 0;
}
}
int main(int argc, string argv[])
{
if (argc != 2 || keyvalidator(argv[1]) != 1)
{
printf("That is not a valid secret key!\n");
return 1;
}
if (argc == 2)
{
string secretKey = argv[1];
string plainText = GetString();
printf("%s\n", vencipher(plainText, secretKey));
}
return 0;
}
The newer code compiles but when I run it I get a "floating point exception," which according to my research is the result of modulo divison by 0. I searched my code several times, and I can't find any instance of division by 0. I was wondering if someone could help me find my error and explain to me what's causing the floating point exception here.
I've also seen floating point exceptions on memory corruptions so that's the more likely scenario given there's not a / character anywhere in your code.
I will tell you something that you're doing wrong. By calling keycaseID() on the same string more than once (as you do in setkeycase(), you're guaranteed to end up with a string of all zeros (0 rather than '0').
The first time it will convert all elements to either 1 or 0 depending on their case (and you therefore lose their original value). The second time, because they're either all 0 or 1, they'll be less than 65 and therefore all set to 0.
Assuming your key is all alpha characters (upper or lower case), you can just use something like this to convert it into values 0 through 25:
for (int i = strlen (key) - 1; i >= 0; i--) // needs string.h
if (isupper (key[i])) // needs ctype.h
key[i] -= 'A';
else
key[i] -= 'a';
or, even shorter:
for (int i = strlen (key) - 1; i >= 0; i--) // needs string.h
key[i] = toupper (key[i]) - 'A'; // needs ctype.h
Neither of those is perfectly portable since C doesn't mandate that A-Z are contiguous code points but, as long as you steer clear of weird non-ASCII environments, you should be fine :-)
The problem turned out to be due to a problem with pointers, which we hadn't yet covered in class. What's going on here is string is defined by a pointer to the address in memory of the first char, and so when I tried to create a copy of string by initializing a new variable, I was instead creating a copy of that address and thereby editing both the original input string and the new copy since they share the same memory address.
Because of this the length of m was indeed 0, since keycaseID was modifying the values at the memory address of key, thus wiping key and causing setkeycase to return the empty string.

Resources