0

I've been having major trouble with Vigenere. I've worked on it every day for the passed two weeks and each day, I solve one problem but encounter another. I was able to get the program with the function definition to work when a single character was entered (ie. int shift char c) but not when the key involved multiple characters.

I think using pointers is the answer to this but I keep getting pretty confused with the syntax - when should I use * vs & and [ ]. I've watched the content for week 3 and have spent hours googling the topic and I'm still unclear on the matter.

I know there are other problems in my code (such as not cycling through the key again if p > key), but I think I can probably figure them out later. I just want to understand what I'm doing wrong with pointers and defining this function and I'd appreciate any hints to get me going in the right direction. Thanks!!

#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>

int shift(char* c); 

int main(int argc, string argv[2])
{
    //Checks that there are only two command-line arguments
    if (argc != 2)
    {
        printf("Usage: ./vigenere key\n");
        return 1;
    }
    int l = strlen(argv[1]);
    char* k[l] = (argv[1]); 
    //Checks that only alpha have been entered
    for (int i = 0; i < l; i++) 
    {
        if (isalpha (k[i]) == 0)
        {
            printf("Usage: ./vigenere key\n"); 
            return 1;
        }
    }
    // implement shift function, store in key
    int i = 0;
    int* key[i][l] = shift(&k[i][l]);
    char* p = get_string("plainttext: ");
    printf("ciphertext: ");
    //Iterate through p to generate ciphertext 
    for (int j = 0; j < strlen(p); j++) 
    {
        char c = p[j];
        if (isalpha (c) == 1)
        {
            for (int n = 0; n < strlen(&key); n++) 
            {
                // changes lower case c
                if (96 < c && c < 123) 
                {
                    printf("%c", (((c - 97) + key[n]) % 26 + 97));
                }
                // changes upper case c
                if (64 < c && c < 91)
                {
                    printf("%c", (((c - 65) + key[n]) % 26 + 65));
                }
            }
        }
        //if c != alpha, don't change
        else
        {
            printf("%c", c);
        }
    }
    printf("\n");
    return 0;
}

int shift(char* c)
{
     // convert c to be A/a = 0 etc
    int* k;
     if (64 < c && c < 91) // Upper case A == 0, etc
        {
           k = (c - 65);
        }
     if (96 < c && c < 123) // Lower case a == 0, etc
        {
            k = (c - 97);
        }

    return k;
}
1

* after a type is used to make a pointer. A pointer stores a memory address, and the pointer having that particular type will tell the compiler how to interpret the memory content when you dereference the pointer (you can cast pointers to different types, interpreting same memory differently, like Quake interpreting 4 bytes sometimes as float, sometimes as long, which back then always was 4 bytes). string is a typedef (alias) for char* I don't particularly like, as it hides the fact that the variable does not contain the actual characters.

* before a variable name is dereferencing a pointer, referring to the pointee.

& refers to the memory address of a variable. Can be assigned to a pointer variable.

[] in a variable declaration describes an array, which can have space for multiple instances of the base type.

[] is also often used to dereference a variable. arr[i] is equivalent to *(arr + i). The memory address is incremented in steps of the base type, e.g. an int[] would increment in steps of 4 bytes. If an int arr[5] pointed to address 1232, arr[0] would reside at that address, arr[1] would be found at 1236, and so on.

This for example means that *p is same as p[0], or *&a is same as a.

Function parameters are passed by value, copied into completely independent variables valid only within that function. You would pass pointers in cases where you

  1. need to modify the passed variable in-place (modifying the parameter variable does not change the variable of the caller, but modifying a common pointee would work)
  2. want to pass a large structure, or one of variable size (a pointer is just 4 or 8 bytes, and for example strlen can be used with any string length)

Your shift matches neither of them. Also, you accidentally do pointer arithmetics (- applied to pointers), while you intended to work on the characters instead.

Also, the return value of shift would be undefined if the character fell into neither of the two ranges, which is bad design.

BTW, Vigenère ciphers do not require nested loops. For any (alphabetic) character to encode, the keyword character to use is a single one, determined from the keyword length and number of (alphabetic) characters encoded so far.

Also doesn't exactly require any pointer variables other than the one for the plaintext and maybe one for the keyword (being used as an alias for argv[1]).

1
  • Thank you!! I really appreciate the time you took to explain this all so clearly - it has really helped me :) Feb 4 '19 at 10:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .