目录

Harvard CS50 学习笔记(一)

摘要
Harvard CS50 学习笔记(一)。

1 C

1.1 Lecture

1.1.1 IDE

  • CS50 用了网页版VS Code 解决学生的环境问题,非常聪明的做法。我觉得这可能是之后的趋势,云开发。因为本地开发也要连服务器,不如直接服务器开发,一个账户一个终端,互不影响,不担心代码丢失,也无所谓电脑,有浏览器就行,也不用下载不用安装,还省空间,甚至不用买高配电脑了,就像Windows的Office套件一样。搜了一下,现在不少人已经在做了。

1
2
3
4
5
6
7
8
9
//
// Created by Will Dufresne on 11/17/21.
//
#include <stdio.h>

int main(void) {
    printf("Hello, World!");
//    string answer = get_string();
}
  1. 要引入 Header Files (头文件),类似于Java中的包。#include <stdio.h> (stdio = standard io)
  2. 返回值,main() (主)方法(函数),参数,老生常谈。
  3. C 中默认没有 string (字符串)类型。
  4. Cget string() 函数输出变量需要使用 %s(占位符)。

1.1.2 Data Types

  • C 中的一些 data types (数据类型):
    1. bool
    2. char
    3. double
    4. float
    5. int
    6. long
    7. string

1.1.3 Formating Output

  • C 中的一些输出格式化:
    1. %c —— char
    2. %f —— float / double
    3. %i —— int
    4. %li —— long
    5. %s —— string

1.1.4 Operators

  • operators(运算符),variables(变量),syntactic sugars(语法糖),comment(注释),condition(条件),loop(循环),老生常谈。

  • 一个 int4 bytes(字节),即 32 bits(比特)。1 byte = 8 bits ,范围为-255到255。超出范围会导致溢出。

1.1.5 Conditionals

  • 使用大括号的 if,不是函数,是 programing construct,是 a feature of C language。

1.1.6 Key Words

  • C 中常量有关键字,const

  • 顺便想了一下Java中的常量如何定义,有接口,枚举,类变量三种方式。

    1. 接口:不要用!What is the use of interface constants?
    2. 枚举:可以用,维护要注意。有类内枚举这种方式,看情况。
    3. 类变量:可以专门搞一个final的类,感觉有点大材小用。

1.1.7 Other Thoughts

  • 从这两节课可以看出编程及其语言的关键部分:(I would call them cornerstones)
    1. 变量(数据类型)。
    2. 运算。
    3. 条件。
    4. 循环。

  • C 是面向过程的语言,编译从上到下执行,所以需要声明函数,如果该函数的实现在主函数之后。

  • 浮点数精度问题。

1.2 Shorts

1.2.1 Data Types and Variables

1.2.1.1 int
/images/Harvard_CS50/Lecture_1/int_1.png
int_1
  1. int 是用于储存整数的数据类型。
  2. int4 bytes (字节),即 32 bits(比特)。

/images/Harvard_CS50/Lecture_1/int_2.png
int_2
  1. unsigned 关键字可将正数范围扩大一倍,并舍弃负数范围。

1.2.1.2 char
/images/Harvard_CS50/Lecture_1/char.png
char
  1. char 是用于储存单个字符的数据类型。
  2. char1 byte (字节),即 8 bits(8比特),范围从 -128127

1.2.1.3 float
/images/Harvard_CS50/Lecture_1/float.png
float
  1. float 是用于储存浮点型的数据类型。
  2. float4 bytes (字节),即 32 bits (比特)。
  3. floatl类型有精度问题,因为其只占占 4 bytes (字节),即 32 bits (比特),如果整数部分很多,小数部分就变少,因为总的空间是有限的。

1.2.1.4 double
/images/Harvard_CS50/Lecture_1/double.png
double
  1. double 也是用于储存浮点型的数据类型,但范围更大。
  2. double8 bytes (字节),即 64 bits (比特)。

1.2.1.5 void
/images/Harvard_CS50/Lecture_1/void.png
void
  1. void 是一个类型,但不是数据类型。
  2. 函数可以返回void,也就是什么都不返回。
  3. 函数的参数也可以为void,也就是没有参数。

1.2.1.6 Primitive Types
/images/Harvard_CS50/Lecture_1/five_primitive_data_types.png
five_primitive_data_types
  1. 以上就是 C 中的 5primitive types (基本类型)。

1.2.1.7 bool
/images/Harvard_CS50/Lecture_1/bool.png
bool
  1. 布尔型,只有 truefalse 两个值。
  2. 非基本类型,要手动处理,课程已处理好。

1.2.1.8 string
/images/Harvard_CS50/Lecture_1/string.png
string
  1. 字符串,用于储存一串字符的类型。
  2. 非基本类型,要手动处理,课程已处理好。

1.2.1.9 Other Types
/images/Harvard_CS50/Lecture_1/other_types.png
other_types
  1. 还有其他类型,例如structstypedefs等。

1.2.1.10 Declare Variables
/images/Harvard_CS50/Lecture_1/declare_variables.png
declare_variables
  1. 类型 +名字 ——> 声明变量。
  2. 多个变量可以一起声明(不要这么做)。
  3. 需要的时候再声明。

1.2.1.11 Using Variables
/images/Harvard_CS50/Lecture_1/using_variables.png
using_variables
  1. 给变量赋值。
  2. 声明 + 赋值 ——> 初始化变量。

1.2.2 Operators

1.2.2.1 Arithmetic Operators
/images/Harvard_CS50/Lecture_1/arithmetic_operators_1.png
arithmetic_operators_1
  1. 数学运算符。

/images/Harvard_CS50/Lecture_1/arithmetic_operators_2.png
arithmetic_operators_2
  1. 加减乘除取余。

/images/Harvard_CS50/Lecture_1/arithmetic_operators_3.png
arithmetic_operators_3
  1. 简写。

1.2.2.2 Boolean Expressions
/images/Harvard_CS50/Lecture_1/boolean_expressions_1.png
boolean_expressions_1
  1. 布尔表达式。

/images/Harvard_CS50/Lecture_1/boolean_expressions_2.png
boolean_expressions_2
  1. 非零为true,零为false。
  2. 逻辑运算符与关系运算符。

1.2.2.3 Logical Operators
/images/Harvard_CS50/Lecture_1/logical_operators_1.png
logical_operators_1
  1. 与。

/images/Harvard_CS50/Lecture_1/logical_operators_2.png
logical_operators_2
  1. 或。

/images/Harvard_CS50/Lecture_1/logical_operators_2.png
logical_operators_3
  1. 非。

1.2.2.4 Relational Operators
/images/Harvard_CS50/Lecture_1/relational_operators_1.png
relational_operators_1
  1. 大于、小于、等于、大于等于、小于等于。

/images/Harvard_CS50/Lecture_1/relational_operators_2.png
relational_operators_2
  1. 等于,不等于。

1.2.3 Conditional Statements

/images/Harvard_CS50/Lecture_1/conditionals_1.png
conditionals_1
  1. 条件表达式。

1.2.3.1 If
/images/Harvard_CS50/Lecture_1/if.png
if
/images/Harvard_CS50/Lecture_1/if-else.png
if-else
/images/Harvard_CS50/Lecture_1/if-else-if.png
if-else-if
/images/Harvard_CS50/Lecture_1/if-if.png
if-if

1.2.3.2 Switch
/images/Harvard_CS50/Lecture_1/switch_1.png
switch_1
  1. 注意类型与 break

/images/Harvard_CS50/Lecture_1/switch_2.png
switch_2
  1. 没有 break,之后的会全部执行。

1.2.3.3 Ternary Operator
/images/Harvard_CS50/Lecture_1/ternary_operator.png
ternary_operator
  1. 三元表达式(少用,不好阅读,JDK源码出现过三元表达式错误)。

1.2.3.4 Summary
/images/Harvard_CS50/Lecture_1/conditionals_2.png
conditionals_2
  1. 三种条件表达式。

1.2.4 Loops

/images/Harvard_CS50/Lecture_1/loops_1.png
loops_1
  1. 循环。

1.2.4.1 While
/images/Harvard_CS50/Lecture_1/infinite_loop.png
infinite_loop
  1. infinite loop (死循环)。

/images/Harvard_CS50/Lecture_1/while_loop.png
while_loop
  1. while 循环。

1.2.4.2 Do While
/images/Harvard_CS50/Lecture_1/do_while_loop.png
do_while_loop
  1. 先做一次再判断循环。

1.2.4.5 For
/images/Harvard_CS50/Lecture_1/for_loop_1.png
for_loop_1
/images/Harvard_CS50/Lecture_1/for_loop_2.png
for_loop_2
  1. for 循环。

1.2.4.6 Summary
/images/Harvard_CS50/Lecture_1/loops_2.png
loops_2
  1. 一般来说,while 循环用于未知次数;do while 循环用于先做一次的情况;for 循环用于已纸次数。

1.2.5 Command Line

/images/Harvard_CS50/Lecture_1/using_linux_command_line_1.png
using_linux_command_line_1
/images/Harvard_CS50/Lecture_1/using_linux_command_line_2.png
using_linux_command_line_2
/images/Harvard_CS50/Lecture_1/cd.png
cd
/images/Harvard_CS50/Lecture_1/mkdir.png
mkdir
/images/Harvard_CS50/Lecture_1/cp.png
cp
  1. 拷贝含子文件夹的文件夹,需要加 -r。

/images/Harvard_CS50/Lecture_1/rm.png
rm
/images/Harvard_CS50/Lecture_1/mv.png
mv
/images/Harvard_CS50/Lecture_1/using_linux_command_line_3.png
using_linux_command_line_3

1.3 Lab1

1.3.1 Hello

1
2
3
4
5
6
7
8
#include <stdio.h>
#include <cs50.h>

int main(void)
{
    string input = get_string("What is your name?\n");
    printf("Hello, %s\n", input);
}

1.3.2 Population

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <stdio.h>
#include <cs50.h>
#include <math.h>

int startThreshold = 9;

int main(void)
{
    // declare function
    void validateInputs();

    // do the business
    validateInputs();
}

void validateInputs() {
    // declare functions
    int getStartPopulation();
    int getEndPopulation(int start_population);
    void calculateYears(int start_population, int end_population);

    // get inputs
    int start_population = getStartPopulation();
    int end_population = getEndPopulation(start_population);

    // do the business
    calculateYears(start_population, end_population);
}

int getStartPopulation() {
    int start_population = get_int("Start size: \n");
    while (start_population < startThreshold) {
        start_population = get_int("Start size: \n");
    }
    return start_population;
}

int getEndPopulation(int start_population) {
    int end_population = get_int("End size: \n");
    while (end_population < start_population) {
        end_population = get_int("End size: \n");
    }
    return end_population;
}

void calculateYears(int start_population, int end_population) {
    int srartSize = start_population;
    int year = 0;
    while (start_population < end_population) {
        int newborns = round(start_population / 3);
        int deads = round(start_population / 4);
        start_population = start_population + newborns - deads;
        year++;
    }
    printf("Years: %i\n", year);
}

1.4 Problem Set 1

1.4.1 Mario

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>
#include <cs50.h>

int main(void){
    // declare functions
    int getHeight();
    void buildBlocks(int height);

    // get input
    int height = getHeight();

    // do the business
    buildBlocks(height);
}

int getHeight() {
    int height = get_int("Please enter the height: \n");
    while (height <=0 || height > 8) {
        height = get_int("Please enter the height: \n");
    }
    return height;
}

void buildBlocks(int height) {

    for (int i=1; i<=height; i++) {
        for (int j = 1; j<=height - i; j++) {
            printf(" ");
        }
        for (int k = 1; k<=i; k++) {
            printf("#");
        }
        printf("  ");
        for (int k = 1; k<=i; k++) {
            printf("#");
        }
        printf("\n");
    }
}

1.4.2 Credit

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include <stdio.h>
#include <cs50.h>
#include <math.h>


int cardLength = 0;
int firstTwoDigits = 0;

int americanExpressLength = 15;
int americanExpressFirstTwoDigits_1 = 34;
int americanExpressFirstTwoDigits_2 = 37;

int masterCardLength = 16;
int masterCardFirstTwoDigits_1 = 51;
int masterCardFirstTwoDigits_2 = 52;
int masterCardFirstTwoDigits_3 = 53;
int masterCardFirstTwoDigits_4 = 54;
int masterCardFirstTwoDigits_5 = 55;

int visaLength = 13;
int visaFirstDigit = 4;

int main(void) {
    // declare functions
    long getInput();
    bool isValidCardNumber(long cardNumber);
    void decideCardType();

    // get card number
    long cardNumber = getInput();

    // handle card number
    if (!isValidCardNumber(cardNumber)) {
        printf("INVALID\n");
    } else {
        // judge card type
        decideCardType();
    }
}

long getInput() {
    long cardNumber = get_long("Number: \n");
    return cardNumber;
}

bool isValidCardNumber(long cardNumber) {
    // declare functions
    bool isNegative(long cardNumber);
    bool isLengthRight(long cardNumber);
    bool isNumberCorrect(long cardNumber);

    // judge negative
    if (isNegative(cardNumber)) {
        return false;
    }

    // judge length
    if (!isLengthRight(cardNumber)) {
        return false;
    }

    // judge number
    if (!isNumberCorrect(cardNumber)) {
        return false;
    }

    return true;
}

bool isNegative(long cardNumber) {
    if (cardNumber < 0) {
        return true;
    } else {
        return false;
    }
}

bool isLengthRight(long cardNumber) {
    int length = 0;

    while (cardNumber > 0) {
        cardNumber /= 10;
        length++;
    }
    if (length != americanExpressLength && length != masterCardLength && length != visaLength) {
        return false;
    } else {
        cardLength = length;
        return true;
    }
}

bool isNumberCorrect(long cardNumber) {
    int sum = 0;
    int i = 1;

    while(cardNumber > 0) {
        int reminder = cardNumber % 10;
        if (i % 2 == 0) {
            reminder = reminder * 2;
            if (reminder >= 10) {
                reminder = (reminder % 10) + (reminder / 10);
            }
        }
        sum += reminder;
        cardNumber /= 10;
        i++;
        if (cardNumber < 100 && cardNumber > 10) {
            firstTwoDigits = cardNumber;
        }
    }

    if (sum % 10 == 0) {
        return true;
    } else {
        return false;
    }
}

void decideCardType() {
    // declare function
    void decideVisaType(int cardLength);

    if (cardLength == americanExpressLength) {
        if (firstTwoDigits == americanExpressFirstTwoDigits_1 || firstTwoDigits == americanExpressFirstTwoDigits_2) {
            printf("AMEX\n");
        } else {
            printf("INVALID\n");
        }

    } else if (cardLength == masterCardLength) {
        if (firstTwoDigits == masterCardFirstTwoDigits_1 || firstTwoDigits == masterCardFirstTwoDigits_2
            || firstTwoDigits == masterCardFirstTwoDigits_3 || firstTwoDigits == masterCardFirstTwoDigits_4
            || firstTwoDigits == masterCardFirstTwoDigits_5) {
                printf("MASTERCARD\n");
            } else {
                decideVisaType(firstTwoDigits);
            }
    } else if (cardLength == visaLength) {
        decideVisaType(firstTwoDigits);
    } else {
        printf("INVALID\n");
    }
}

void decideVisaType(int twoDigits) {
    twoDigits /= 10;
    if (twoDigits == visaFirstDigit) {
        printf("VISA\n");
    } else {
        printf("INVALID\n");
    }
}
  1. 这么一个简单的小程序,竟然写了一小时,还错了好几次,除了体现自己能力的不足,有几个地方要特别注意。
  2. 多考虑边界条件。
  3. 涉及逻辑判断的时候,要多想一下,再更仔细一点。