跳转至

Week 2 Arrays

约 857 个字 322 行代码 预计阅读时间 7 分钟

Syllabus

Preprocessing. Compiling. Assembling. Linking. Debugging. Arrays. Strings. Command-Line Arguments. Cryptography.

Notes

Preprocessing

Motivation: reading levels

Recall a graph:

Source Code -> Compiler -> Machine Code

Compiling

What's actually happend in the process of translating?(Compiling)

Text Only
clang hello.c
./a.out
Text Only
clang -o hello hello.c -lcs50
//If you want to use the cs50 library

preprocessor preprocessing step

If you want to code C or other languages, the CPU will process the code from C into a low levlel language called assembly code.

And focus on compiling

Text Only
#include <cs50.h>
#include <stdio.h>

int main(void)
{
    printf(" ");
}

What's the definition of linking neatly?
A: the process of combining multiple object files (pieces of compiled code) generated by a compiler into a single executable program.

Four steps of compiling:
+ preprocessing
+ compile to assembly code
+ assembly code translates to machine code
+ linking: combining multiple object files (pieces of compiled code)

Debugging

  • Everyone will make mistakes while coding.
  • Debugging is the process of locating and removing bugs from your code.
Text Only
#include <stdio.h>

int main(void)
{
    for (int i = 0; i <  3; i++)
    {
        printf("i is %i\n", i);
        printf("#\n");
    }
}

Arrays

Motivation: We don't want so many variables for a same type(or same purpose)

Text Only
int scores[3];
scores[0] = 72;
scores[1] = 73;
scores[2] = 33;

So this tech has an advantage that we can change it easily and sustained.

Text Only
// Averages three numbers using an array, a constant, and a helper function

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

// Constant
const int N = 3;

// Prototype
float average(int length, int array[]);

int main(void)
{
    // Get scores
    int scores[N];
    for (int i = 0; i < N; i++)
    {
        scores[i] = get_int("Score: ");
    }

    // Print average
    printf("Average: %f\n", average(N, scores));
}

float average(int length, int array[])
{
    // Calculate average
    int sum = 0;
    for (int i = 0; i < length; i++)
    {
        sum += array[i];
    }
    return sum / (float) length;
}

Strings

  • string is simply an array of variables of type char: an array of characters.
  • To explore char and string, type code hi.c in the terminal window and write code as follows:
Text Only
    // Prints chars

    #include <stdio.h>

    int main(void)
    {
        char c1 = 'H';
        char c2 = 'I';
        char c3 = '!';

        printf("%c%c%c\n", c1, c2, c3);
    }

Notice that this will output a string of characters.

A string is a sequence of characters like a array of the type of characters.

NUL says string end here.

Text Only
#include <cs50.h>
#include <stdio.h>

int main(void)
{
    string words[2];

    words[0] = "HI!";
    words[1] = "BYE!";

    printf("%c%c%c\n", words[0][0], words[0][1], words[0][2]);
    printf("%c%c%c%c\n", words[1][0], words[1][1], words[1][2], words[1][3]);

// arrays of arrays so strings are actually arrays
}

strlen function include

Since this is such a common problem within programming, other programmers have created code in the string.h library to find the length of a string. You can find the length of a string by modifying your code as follows:

Text Only
// Determines the length of a string using a function

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

int main(void)
{
    // Prompt for user's name
    string name = get_string("Name: ");
    int length = strlen(name);
    printf("%i\n", length);
}

Notice that this code uses the string.h library, declared at the top of the file. Further, it uses a function from that library called strlen, which calculates the length of the string passed to it.

ctype.h

While the program does what we want, there is an easier way using the ctype.h library. Modify your program as follows:

Text Only
// Uppercases string using ctype library (and an unnecessary condition)

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

int main(void)
{
    string s = get_string("Before: ");
    printf("After:  ");
    for (int i = 0, n = strlen(s); i < n; i++)
    {
        if (islower(s[i]))
        {
            printf("%c", toupper(s[i]));
        }
        else
        {
            printf("%c", s[i]);
        }
    }
    printf("\n");
}

Notice that the program iterates through each character of the string. The toupper function is passed s[i]. Each character (if lowercase) is converted to uppercase.

Command-Line Arguments

  • Command-line arguments are those arguments that are passed to your program at the command line. For example, all those statements you typed after clang are considered command line arguments. You can use these arguments in your own programs!
  • In your terminal window, type code greet.c and write code as follows:

    Text Only
    // Uses get_string
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        string answer = get_string("What's your name? ");
        printf("hello, %s\n", answer);
    }
    

Notice that this says hello to the user.

  • Still, would it not be nice to be able to take arguments before the program even runs? Modify your code as follows:

    Text Only
    // Prints a command-line argument
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(int argc, string argv[])
    {
        if (argc == 2)
        {
            printf("hello, %s\n", argv[1]);
        }
        else
        {
            printf("hello, world\n");
        }
    }
    

Notice that this program knows both argc, the number of command line arguments, and argv, which is an array of the characters passed as arguments at the command line.

Remark that one of CLAs is the name of the program!

  • Therefore, using the syntax of this program, executing ./greet David would result in the program saying hello, David.
  • You can print each of the command-line arguments with the following:
Text Only
    // Prints command-line arguments

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

    int main(int argc, string argv[])
    {
        for (int i = 0; i < argc; i++)
        {
            printf("%s\n", argv[i]);
        }
    }

Exit Status

  • When a program ends, a special exit code is provided to the computer.
  • When a program exits without error, a status code of 0 is provided to the computer. Often, when an error occurs that results in the program ending, a status of 1 is provided by the computer.
  • You could write a program as follows that illustrates this by typing code status.c and writing code as follows:

    Text Only
    // Returns explicit value from main
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(int argc, string argv[])
    {
        if (argc != 2)
        {
            printf("Missing command-line argument\n");
            return 1;
        }
        printf("hello, %s\n", argv[1]);
        return 0;
    }
    

Notice that if you fail to provide ./status David, you will get an exit status of 1. However, if you do provide ./status David, you will get an exit status of 0.

  • You can type echo $? in the terminal to see the exit status of the last run command.
  • You can imagine how you might use portions of the above program to check if a user provided the correct number of command-line arguments.

Cryptography

  • Cryptography is the art of ciphering and deciphering a message.
  • Now, with the building block of arrays, chars, and strings, you can cipher and decipher a message.
  • plaintext and a key are provided to a cipher, resulting in ciphered text.

Key and Plaintext -> Cipher -> Ciphertext

Summing Up

In this lesson, you learned more details about compiling and how data is stored within a computer. Specifically, you learned…

  • Generally, how a compiler works.
  • How to debug your code using four methods.
  • How to utilize arrays within your code.
  • How arrays store data in back to back portions of memory.
  • How strings are simply arrays of characters.
  • How to interact with arrays in your code.
  • How command-line arguments can be passed to your programs.
  • The basic building-blocks of cryptography.

Sections

  • Arrays
    • Initialize
    • assignment
    • change
  • Strings
    • get_string
    • Connection with arrays
    • ASCII code(Alphabetical Exercise)
  • Command Line Arguments aka. CLA

A program about CLAs:

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

int main(int argc, string argv[])
{
    // Get user's input
    if (argc != 2)
    {
        printf("Please provide a word.\n");
        return 1;
    }

    string text = argv[1];
    int len = strlen(text);

    for (int i = 0; i < len; i++)
    {
        if (!isalpha(text[i]))
        {
            printf("This is not a letter.\n");
            return 2;
        }
    }

    // Iterate through each element in the string
    for (int i = 1; i < len; i++)
    {
        if (text[i] < text[i - 1])
        {
            printf("No\n");
            return 0;
        }
    }
    // Print out yes
    printf("Yes\n");
}

Problem Set 2

Scrabble

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

// Points assigned to each letter of the alphabet
int POINTS[] = {1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10};

int compute_score(string word);

int main(void)
{
    // Prompt the user for two words
    string word1 = get_string("Player 1: ");
    string word2 = get_string("Player 2: ");

    // Compute the score of each word
    int score1 = compute_score(word1);
    int score2 = compute_score(word2);

    // Print the winner
    if (score1 > score2)
    {
        printf("Player 1 wins!\n");
    }
    else if (score1 < score2)
    {
        printf("Player 2 wins!\n");
    }
    else
    {
        printf("Tie!\n");
    }
}

int compute_score(string word)
{
    // Keep track of score
    int score = 0;

    // Compute score for each character
    for (int i = 0, len = strlen(word); i < len; i++)
    {
        if (isupper(word[i]))
        {
            score += POINTS[word[i] - 'A'];
        }
        else if (islower(word[i]))
        {
            score += POINTS[word[i] - 'a'];
        }
    }

    return score;
}

Readability

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

int count_letters(string text);
int count_words(string text);
int count_sentences(string text);

int main(void)
{
    // Prompt the user for some text
    string text = get_string("Text: ");

    // Count the number of letters, words, and sentences in the text
    int letters = count_letters(text);
    int words = count_words(text);
    int sentences = count_sentences(text);

    // Compute the Coleman-Liau index
    float L = (float)letters / words * 100; // Average letters per 100 words
    float S = (float)sentences / words * 100; // Average sentences per 100 words
    float index_original = 0.0588 * L - 0.296 * S - 15.8; // Coleman-Liau index formula
    // Print the grade level
    int index = round(index_original);
    if (index < 1)
    {
        printf("Before Grade 1\n");
    }
    else if (index >= 16)
    {
        printf("Grade 16+\n");
    }
    else
    {
        printf("Grade %d\n", index);
    }
}

int count_letters(string text)
{
    // Return the number of letters in text
    int letter_count = 0;
    for (int i = 0, n = strlen(text); i < n; i++)
    {
        if (isalpha(text[i]))
        {
            letter_count++;
        }
    }
    return letter_count; // Return the count of letters
}

int count_words(string text)
{
    // Return the number of words in text
    int word_count = 0;
    for (int i = 0, n = strlen(text); i < n; i++)
    {
        if (isspace(text[i]) || i == n - 1) // Count words by spaces or end of text
        {
            word_count++;
        }
    }
    return word_count; // Return the count of words
}

int count_sentences(string text)
{
    // Return the number of sentences in text
    int sentence_count = 0;
    for (int i = 0, n = strlen(text); i < n; i++)
    {
        if (text[i] == '.' || text[i] == '!' || text[i] == '?')
        {
            sentence_count++;
        }
    }
    return sentence_count; // Return the count of sentences
}

Caesar

C
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, string argv[])
{
    // Make sure program was run with just one command-line argument
    if (argc != 2)
    {
        printf("Usage: ./caesar key\n");
        return 1;
    }
    // Make sure every character in argv[1] is a digit
    for (int i = 0, n = strlen(argv[1]); i < n; i++)
    {
        if (argv[1][i] < '0' || argv[1][i] > '9')
        {
            printf("Usage: ./caesar key\n");
            return 1;
        }
    }
    // Convert argv[1] from a `string` to an `int`
    int key = atoi(argv[1]) % 26; // Ensure key is within 0-25
    // Prompt user for plaintext
    string plaintext = get_string("plaintext: ");
    // For each character in the plaintext:
    printf("ciphertext: ");
    for (int i = 0, n = strlen(plaintext); i < n; i++)
    {
        // Check if the character is an uppercase letter
        if (plaintext[i] >= 'A' && plaintext[i] <= 'Z')
        {
            // Rotate the character
            printf("%c", ((plaintext[i] - 'A' + key) % 26) + 'A');
        }
        // Check if the character is a lowercase letter
        else if (plaintext[i] >= 'a' && plaintext[i] <= 'z')
        {
            // Rotate the character
            printf("%c", ((plaintext[i] - 'a' + key) % 26) + 'a');
        }
        else
        {
            // If it's not a letter, just print it as is
            printf("%c", plaintext[i]);
        }
    }
    printf("\n");
    return 0;
}

Substitution

C
#include <cs50.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

int main(int argc, string argv[])
{
    // Make sure program was run with just one command-line argument
    if (argc != 2)
    {
        printf("Usage: ./substitution key\n");
        return 1;
    }

    // Optimize: Calculate key length once
    int key_len = strlen(argv[1]);
    for (int i = 0; i < key_len; i++)
    {
        // Check if the key contains only alphabetic characters
        if (!isalpha(argv[1][i]))
        {
            printf("Key must contain 26 characters.\n");
            return 1;
        }
    }

    // Make sure argv[1] has 26 characters (Logic preserved as per original code)
    for (int i = 0; i < key_len; i++) // Use pre-calculated key_len
    {
        if ((argv[1][i] <= 'z' && argv[1][i] >= 'a') || (argv[1][i] <= 'Z' && argv[1][i] >= 'A'))
        {
            // Use pre-calculated key_len here as well
            if (key_len != 26) // Use pre-calculated key_len
            {
                printf("Key must contain 26 characters.\n");
                return 1;
            }
            else
            {   // Check for duplicate characters in the key
                for (int j = 0; j < key_len; j++) // Use pre-calculated key_len
                {
                    for (int k = j + 1; k < key_len; k++) // Use pre-calculated key_len
                    {
                        if (argv[1][j] == argv[1][k] || argv[1][j] + 32 == argv[1][k] || argv[1][j] - 32 == argv[1][k])
                        {
                            printf("Key must not contain repeated characters.\n");
                            return 1;
                        }
                    }
                }
                // Prompt user for plaintext
                string plaintext = get_string("plaintext: ");

                // Optimize: Calculate plaintext length once
                int plaintext_len = strlen(plaintext);

                // For each character in the plaintext:
                for (int j = 0; j < plaintext_len; j++) // Use pre-calculated plaintext_len
                {
                    if ((plaintext[j] - 'a') >= 0 && (plaintext[j] - 'a') <= 26) // Original condition preserved
                    {
                        // Convert lowercase letters
                        plaintext[j] = argv[1][plaintext[j] - 'a'];
                        if (plaintext[j] >= 'A' && plaintext[j] <= 'Z')
                        {
                            // Convert to lowercase if necessary
                            plaintext[j] += 32; // ASCII value adjustment
                        }
                    }
                    else if ((plaintext[j] - 'A') >= 0 && (plaintext[j] - 'A') <= 26) // Original condition preserved
                    {
                        // Convert uppercase letters
                        plaintext[j] = argv[1][plaintext[j] - 'A'];
                        if (plaintext[j] >= 'a' && plaintext[j] <= 'z')
                        {
                            // Convert to uppercase if necessary
                            plaintext[j] -= 32; // ASCII value adjustment
                        }
                    }
                }
                printf("ciphertext: %s\n", plaintext);
                return 0;
            }
        }
        else
        {
            printf("Usage: ./substitution key\n");
            return 1;
        }
    }
    // Note: The original code's control flow causes it to exit the main function within the first iteration
    // of the key validation loop (if conditions are met).
    // This final 'return 0' would only be reached if the key_len was 0 or the loop somehow finished without returning.
    return 0;
}

评论