Node.js 学习笔记
早期在学习 node 时候,做的笔记。
早期在学习 node 时候,做的笔记。
也称ASCII码文件,每字符占一个字节存储,每字节保存的是字符的ASCII码。可用文本编辑器打开查看,例如.txt、代码源程序.c、等等
以二进制编码的形式保存,例如可执行文件(.exe)、位图文件(.bmp)、word文件(.docx)等都属于二进制文件。这种文件不能用文本编辑器打开查看,强行打开会乱码,无法读懂。
优点:
“全部看作二进制”,输入输出的开始和结束由程序控制而不受换行、空格等字符的限制;比文本文件一般体积小,节省存储空间。
1 | // 定义文件指针 |
如果文件事先已经存在,则会被删除重;如不存在,则直接新建。
fprintf()为写入文件
1 | #include <stdio.h> |
fopen(char *filename, char *mode)filename:要打开的文件名称
mode:文件访问模式
| mode | introduce | remark |
|---|---|---|
r | 允许读文件(read) | 文件必须存在,否则出错 |
w | 允许覆盖写文件(write) | 文件必须被新建(如文件已存在会删除源文件,然后新建) |
a | 允许追加写文件(append) | 文件不存在时才新建,否则只在源文件末尾添加数据 |
+ | 即允许读也允许写文件 | |
b | 以二进制格式打开文件(binary) | |
t | 以文本格式打开文件(text) |
| 函数 | 功能 | 用法 |
|---|---|---|
| fgetc或getc | 从当前位置指针处读取文件中的一个字符(1个字符占1个字节),读取后,读写位置指针自动后移1字节 | 字符变量=fgetc(fp); |
| fputc或putc | 在当前位置指针处向文件中写入一个字符(1个字符占1个字节),写入后,读写位置指针自动后移1字节 | fputc(字符, fp); |
| fgets | 读取文件中的一个字符串,读写位置指针自动后移 | fgets(字符数组名, n, fp); |
| fputs | 在当前位置指针处向文件中写入一个字符串(不写入’\0’字符,最后也不自动加’\n’);写入后,位置指针自动后移该字符串长度的字节。 | fputs(字符串首地址, fp); |
| fscanf | 从当前位置指针处按格式读取文件中的多个数据,类似于scanf,只不过不是从键盘输入,而是从文件中读取;读取后,位置指针自动后移 | fscanf(fp, “格式控制字符串”, 变量1的地址, 变量2的地址, …); |
| fprintf | 在当前位置指针处按格式向文件中写入多个数据,类似于printf,只不过不是显示到屏幕上,而是写入到文件中;写入后,位置指针自动后移 | fprintf(fp, “格式控制字符串”, 数据1, 数据2, …); |
| 函数 | 功能 | 用法 |
|---|---|---|
| fread | 从当前位置指针处读取文件中的一批字节,这批字节由count个数据块、每数据块长size个字节组成,共size*count个字节。所读取的字节存入参数buffer地址开始的一段内存空间。读取后,文件位置指针跟随后移实际读取的字节数。函数返回实际读取的数据块数(如读到文件尾或出错,实际读取的数据块数可能小于count) | fread(buffer, size, count, fp); |
| fwrite | 在当前位置指针处向文件中写入一批字节,这批字节位于内存中参数buffer地址开始的一段内存空间,由count个数据块、每数据块长size个字节组成,共size*count个字节。写入后,文件位置指针跟随后移实际写入的字节数。函数返回实际写入的数据块数(如写入出错,实际写入的数据块数可能小于count) | fwrite(buffer, size, count, fp); |
| 函数 | 功能 | 用法 |
|---|---|---|
| rewind | 把文件位置指针移到文件开头 | rewind(fp); |
| fseek | 把文件位置指针从ori开始的位置,向文件尾部(n>0时)或文件首部(n<0时)移动n个字节。ori可有3种取值: 0、1、2分别表示从文件首、当前位置和文件尾开始移动, 0、1、2也可分别写为符号常量SEEK_SET、SEEK_CUR、SEEK_END | fseek(fp, n, ori); 一般n为long型,常量加字母后缀L(l) |
| ftell | 若执行成功,函数返回当前文件位置指针的位置(文件中第一个字节的位置为0);若执行失败,函数返回-1 | n = ftell(fp); |
| feof | 判断读文件是否已越过了文件末尾 | if (feof(fp) ) … |
结构体类型,不占内存,不能保存数据;
内存空间,可以保存数据
1 | struct stu |
sizeof对类型和变量均可使用
1 | sizeof(boy1); |
1 | struct stu |
1 | struct stu |
因无类型名,无法再重新定义其他变量
1 | struct |
1 | struct stu |
1 | struct stu ss[3]; |
结构体变量用
.结构体指针变量用
->(
*结构体指针变量)用.
1 | struct stu boy1; |
用法与传普通变量类似,
结构体类型变量做函数参数,不会改变原来的实参的值。
结构体指针做函数参数,可以修改原来实参的值。
1 | struct date |
第二种1
2
3
4
5
6
7
8
9
10
11
12
13struct stu
{
int num;
char name[10];
char sex;
struct
{
int year;
int month;
int day;
} birthday;
float score;
};
typedef为类型起“绰号”,并不产生新的数据类型,只是给已有的类型增加新名
1 | typedef double real; |
stdlib.h)| 函数名 | 介绍 | 备注 |
|---|---|---|
| malloc | 分配1块长度为size字节的连续内存空间(不清零),函数返回该空间的首地址;如分配失败函数返回0 | (类型说明符*)malloc(size) |
| calloc | 分配n块、每块长度为size字节的连续内存空间(共size×n字节),并将该空间中的内容全部清零,函数返回该空间的首地址;如分配失败函数返回0 | (类型说明符*)calloc(n,size) |
| free | 释放ptr所指向的一块内存空间,ptr是由malloc或calloc函数所分配空间的地址,即是这两个函数的返回值(或类型转换后的返回值) | free(ptr)(ptr为任意基类型的指针) |
以链接方式存储的线性表,链表各元素的逻辑结构与存储结构一般不一致。
链表类型:单向链表,双向链表,循环链表。。。
一个单向链表的节点分为两个部分,第一部分保存或者显示节点的相关信息,第二部分存储下一个节点的地址。单向链表只向一个方向遍历。
![]()
链表的一种,特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始;列表有节点构成,head指针指向第一个称为表头节点,而终止于最后一个指向NULL的指针。
比单向链表多一个的是,双向链表每个节点有两个链接,一个指向前一个节点,而另一个指向下一个节点,当为最后一个节点时,指向空值或空列表。
![]()
在一个循环链表中,首节点和末节点被连接在一起。这种方式,单向和双向都可实现。
![]()
1 | #include <stdio.h> |
1 | p = new struct node; |
1 | q->next = p->next; |
如果要改变a, b的值,要使用指针指向a,b
输入两个数,如果第一个数大于第二个数,则交换两个数,否则直接输出1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17int main()
{
int a,b;
printf("请输入两个数字\n");
scanf("%d%d", &a, &b);
if(a<b) swap(&a, &b);
printf("%d, %d\n", a, b);
return 0;
}
void swap(int *p, int *q)
{
int temp;
temp = *p;
*p = *q;
*q = temp;
}
1 | int a[] == int *a; |
1 | int main() |
字符串结尾会有
\0来标识当前字符串的结束初值个数不得超过数组大小
1 | char ch[20] = "HelloWorld"; |
以char *型指针变量保存字符串首地址1
2
3
4
5
6
7
8
9char *ps = "iPhone";
// 只是将ps的指向地址改变,而不保存字符串内容本身。
ps = "iPad";
char s[] = "iTouch";
// 可以
ps = s;
// 错误,s的值不可以改变
s = "iMac";
printf(“%s”, 一级地址);
其含义时从改地址开始逐个字符输出,直到遇到
'\0'为止(\0不输出,也不自动换行)输出字符串其他略有不同,后面要接地址,而不是直接接数据
1 | printf("%d", 数据); |
其含义是从地址开始逐个字符输出,直至
\0为止(\0不输出);然后自动换行(即最后再多输出一个'\n')
1 | puts(ps); |
其含义是:读入键盘键入的一个字符串(最后要键入回车表示结束,但不键入’\0’),存入“一级地址”开始的一顿内存空间(回车符不存入),并自动在最后一个字符的后面添加
'\0'。如果字符串中含有空格或Tab符,只能读入空格或Tab符之前的部分(不读空格和Tab符)。
1 | char a[30]; |
其含义是:读入从键盘键入的一行字符(最后一定要键入回车表示结束,但不键入
'\0',存入“一级地址”开始的一顿内存空间(回车符不存入),并自动在最后一个字符的后面添加'\0'。如果字符串中含有空格、Tab符也一起读入并不中断。)
1 | char b[30]; |
1 | // 第一种 |
1 | char *p; |
'\0'和末尾字符定位让p指向字符串的最后一个字符1
2
3while(*p)
p++;
p--;
1 | char s1[20] = "Hello"; |
1 | void reverse(char *s) |
使用以下函数要包含头文件
#include <string.h>
| 函数 | 含义 | 功能详细说明 |
|---|---|---|
| strlen(地址) | 求字符串长度 | 函数返回值为字符串的长度,即从地址开始到’\0’的字符个数(不计’\0’,但其中空格、Tab符、回车符等都计数) |
| strcat(串1地址, 串2地址) | 字符串连接 “串1=串1+串2” | 把从串2地址开始到’\0’的内容,连接到串1的后面(删去串1最后的’\0’),结果仍存入串1地址开始的空间中,并在结果字符串末尾自动添加新’\0’(串1地址的空间应足够大) |
| strcpy(串1地址, 串2地址) | 字符串拷贝 “串1=串2” | 把从串2地址开始到’\0’的内容,拷贝到串1地址开始的空间中,’\0’也一同拷贝(串1地址的空间应足够大) |
| strcmp(串1地址, 串2地址) | 字符串比较 “串1>串2” “串1<串2” “串1==串2” | 两个字符串的大小结果由函数返回值说明: 若函数返回值 > 0,说明 串1 > 串2 若函数返回值 < 0,说明 串1 < 串2 若函数返回值 == 0,说明 串1 = = 串2 |
求占用多少字节,结果为整数
sizeof 是运算符,不是函数,同时也是关键字
sizeof(类型 或表达式)
需要包含头文件 #include
<ctype.h>
| isalpha(ch) | 检查ch是否为字母字符,是返回1,否返回0 |
|---|---|
| isdigit(ch) | 检查ch是否为数字字符,是返回1,否返回0 |
| isspace(ch) | 检查ch是否为空白分隔符,即是否为空格、跳格(Tab, ‘\t’)、换行符(‘\n’)、回车符(‘\r’)、换页符(‘\f’)5种之一。 如是这些字符返回1,否则返回0 |
| islower(ch) | 检查ch是否为小写字母字符,是返回1,否返回0 |
| isupper(ch) | 检查ch是否为大写字母字符,是返回1,否返回0 |
| tolower(ch) | 将ch转换为小写字母,函数返回转换后的字符 |
| toupper(ch) | 将ch转换为大写字母,函数返回转换后的字符 |
内容可变,地址不可变
1 | // 指针变量 |
指针变量所指向的变量的类型1
2
3
4
5
6
7
8
9
10
11
12
13
14int a = 1;
double b = 8.2;
int *p;
double *q;
void *r;
// p为int *型,只能指向int型
p = &a;
// q为double *型,只能指向double型
q = &b;
// r为void *型,可以指向任何类型
r = &a;
r = &b;
指针变量值为0(地址值为0)时,表示什么也不指向 - 空指针。【地址值为NULL也表示空指针】
1 | int a = 1; |
赋值语句的方法1
p = &a;
定义指针变量时初始化(定义时赋初值)1
2int b = 2;
int *q = &b;
允许指针变量之间赋值,但另两个指针变量必须基类型相同1
2
3q = p;
int *r;
r = q;
1 | int *p; |
&, *)&取地址运算符获取变量的地址,用法:&变量名
*指针运算符(间接访问运算符)获取或改写以p为地址的内存内容
*p,*不是获得或改变指针变量本身的值,而是获得或改写它所指向单元的值。
int *p,*运算只能用于指针变量,不能用于普通变量(如*a错误);
&和*互为逆运算
1 | int *p = &a; |
指向向前或向后移动n个单元,
p+n=p中的地址编号 + (每元素字节数*n)
p-n=p中的地址编号 - (每元素字节数*n)
概运算应只针对
指向数组的指针变量进行,否则毫无意义
1 | int a[5] = {1,2,3,4,5}; |
每个
char型变量占1个字节,对基类型为char的指针变量+n,恰好是地址+n
1 | char c[4] = {'a', 'b', 'c', 'd'} |
void 类型的指针不能做+(-)n的运算(也不能++, –的运算)
两指针变量的加减乘除运算时没有意义的。
p1 - p2 = (p1中的地址编号 - p2中的地址编号)/镁元素字节数
该运算一般只对指向同一数组的元素的两个指针变量进项,表示他们所指向元素的先后位置1
2
3
4
5
6
7
8// 表示p1和p2指向数组的同一个元素
p1 == p2
// 表示p1所指元素位于p2所指元素之后;
p1 > p2
// 表示p1所指元素位于p2所指元素之前;
p1 < p21
2
3
4
5
6
7
8
9// 与0和NULL比较
// 表示p为空指针
p == 0;
p == NULL;
// 表示p不是空指针
p != 0;
p != NULL;
1 | int a[5] = {1,2,3,4,5}; |
编译时编译系统将
a[i]全部变为*(a+i).
a[i]就是语法糖,便于我们理解而设计的成为语法糖。
1 | int a[5] = {1,2,3,4,5}; |
1 | int *p = x[0]; |
1 | int a[5]; |
&, *的优先级和++, --相同,都非常高,仅次于()
与++, --统计运算时,从右至左结合
1 | // 行指针 |
| 草稿写法 | ±1效果 | 定义 | |
|---|---|---|---|
| 二级指针 | [[]] | 移动一行 | int (*q)[4]; int b[3][4]; 中的b |
| 二级指针 | [[]] | 必移动4字节 | int **r; int *s[3]; 中的s |
| 一级指针 | [] | 移动一个单位(int型4字节,char型1字节…) | int *p; int a[2]; 中的a |
| 普通变量 | 变量值±1 | int x; |
法则:
*或一个[]升一级*或一个[]降一级;一个&升一级二维数组元素的写法
a[i][j]是*(*(a+i)+j)的语法糖
1 | x[i] == *(x+i); |
C源程序有函数组成:一个main函数和若干个其他函数组成,main函数调用其他函数
0多个,有的函数有1多个参数,有的函数没有参数
0~1个,有的函数有1个返回值,有的函数没有返回值
又称为标准函数或库函数,如sqrt(), fabs(), rand()…
需要包含对应的头文件才能调用: #include <xxx.h>
我们自己编写的函数,需要定义后才能调用
1 | 函数的返回值类型 函数名(参数类型1 参数1, 参数类型2 参数2 ...) |
调用就使用,对有参数的函数,还要同时给出参数
1 | #include <stdio.h> |
在函数定义头部中的参数,在函数未被调用时,此参数无具体值。1
2
3
4
5// 这里的a,b就是形参
void max(int a, int b)
{
...
}
调用函数时给出的具体参数,是有具体的值。1
2// 这里的1,2就属于实参
max(1,2);
return调用返回
程序功能被细分为若干函数,每个函数负责一个小功能,main函数负责调度、指挥个函数的工作。
| 有return语句 | 无return语句 | |
|---|---|---|
| 有返回值的函数(名前非void) | 返回表达式的值,函数结束,返回系统默认值,函数结束 | 函数中语句执行结 束后(执行到最后的}),返回系统默认 值,函数结束 |
| 无返回值的函数(名前有void) | 函数结束(无返回值) ;return 表达式 这是错误的 | 函数中语句执行结 束后(执行到最后 的 } ),函数结束(无返回值) |
C语言是按照顺序编译,所以要将其他函数放在main函数前面
1 | void fun() |
也可提前定义函数,避免出错,注意:在文件头部声明函数的时候,只可省略形参名,
;不可省
1 | #include <stdio.h> |
函数直接或间接的自己调用自己,称为递归。
递归程序,结构简练,可读性强。但执行时其时间和空间上的开销都比较大。
为防止自调用无休止的进行,在函数内必须设置调用终止的条件,否则程序将无法终止,被称为死递归。
与嵌套调用类似。
尽管每次调用的都是资深(同一函数),弹药把每次所调用的函数都看作是不同的函数,这些函数都具有相同的参数、返回值和语句。
求阶乘1
2
3
4
5int recursion(int loop)
{
if(loop == 1 || loop == 0)return 1;
return loop*recursion(loop-1);
}
只在本函数内有效
若未赋初值,值为随机数
不同函数中可使用同名变量,形参和实参也可同名(形参也是函数内的变量)
在复合语句中定义变量(只在复合语句“块”内有效)
全局变量在函数外定义的变量、
作用域:从变量定义处开始,到本程序文件末尾均有效(其中所有函数都能用)。
初始值自动为0。
全局变量可在多个函数中同时起作用,应尽量少用或不用全局变量。
| 变量定义前加 | auto | static | register |
|---|---|---|---|
| 存储位置 | 内存动态存储区 | 内存静态存储区 | CPU寄存器(没在内存) |
| 作用域 | 所在函数内,或所在复合语句 { } 内有效 | 同auto | 同auto |
| 生存期 | 离开函数或{ }就消失 | 永久保留 | 同auto |
| 初值 | 随机数,初值重新赋值 | 值为0,初值只赋一次 | 同auto |
宏定义,只会替换文本,不会计算
1 | #define N 13 |
先预处理,再编译;预处理部分不编译
预处理阶段将宏定义字符串替换1
2
3
4
5
6
7#define M (y*y+3*y)
语句为
s=3*M + 4*M;
预处理后
s=3*(y*y+3*y) + 4*(y*y+3*y)1
2
3
4
5
6
7
8
9
10
11
12
13
14#include <stdio.h>
#define N 3+5
main()
{
printf("%d", 2*N);
}
// 预处理后
#include <stdio.h>
main()
{
printf("%d", 2*3+5 );
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 此例为错误示例
#include <stdio.h>
#define N 3+5;
main()
{
printf("%d", 2*N);
}
// 预处理后,这种情况是错的,不能这么写
#include <stdio.h>
main()
{
printf("%d", 2*3+5; );
}1
2
3
4
5
6
7
8
9
10
11
12#include <stdio.h>
#define PRINT printf("*");
main()
{ PRINT
}
// 预处理后
#include <stdio.h>
main()
{ printf("*");
}
预处理后会将头文件替换掉
1 | #include <头文件名.h> |
c源程序 -> 预处理 -> 编译 -> 运行
1 | // 定义数组的长度 |
已知10名同学的考试成绩,请编程统计及格人数,并计算10名同学成绩的平均分。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#inlcude <stdio.h>
#include <stdlib.h>
#define N 5
int main()
{
float b[N] = {58.0, 69.0, 92.1, 33.3, 50.1, 66.5, 12.5, 63.4, 68.4, 10.1}, sum, avg;
int i, pass;
sum = 0; pass=0;
for(i=0; i<N; i++)
{
sum += b[i];
if(b[i]>= 60)
pass++;
}
avg = sum / N;
printf("通过考试人数为: %d\n", pass);
printf("平均分数为: %5.1f", avg);
}1
printf("%5.1f", fa);
5表示输出数字长度为五位
1表示输出数字的小数后1位
1 | int a[5];int i=3; |
1 | int a[5] = {5, 10}; |
1 | 1. 编程删除数组b中下标为2的元素 |
1 | 2. 编程在数组b中的下标为2的元素之前,插入新元素100。 |
1 | 3. 选择排序,将数组b从小到大排序 |
二维数组又为特殊的一维数组,它的每个元素又是一维数组
1 | 二维数组的定义 |
二维数组在内存中的存储形式
硬件存储器(内存)是连续的和线性的,二维表形式的二维数组在内存中是
线性存储和按行排列。注意:
任何情况下,都不能省略列数(即不能省略第二个[] 内的数);
在能判断出行数时,可省略行数(即可省略第一个[]内的数);
1 | int a[2][3]; |
You are more powerful than any other person.
你比任何人都强大。
顺序结构
选择结构
循环结构
先判断后执行,表达式为真,
注意:如果表达式的值一直为真,没有改变,并且没有
break;,则会陷入死循环
1 | while(表达式) |
先执行后判断
1 | do{ |
for 语句中的3个表达式均可省略,但
;不可省略。
1 | for(表达式1; 表达式2; 表达式3) |
执行顺序
[括号内为循环]
表达式1 -> 表达式2 -> [语句 -> 表达式2 -> 表达式3 ->]
1 | int sum, i; |
1 | #include <stdio.h> |
break:跳出整个循环。
continue:结束本次循环,转到循环的开始判断是否执行下一次循环。
穷举法也成为了枚举法,将在此范围内的所有可能情况逐一验证,知道全部情况验证完毕。
输入一个数,判断其是否为素数
1 | #include <stdio.h> |
Everthing ending is just a new beginning.
每次结束都是新的开始
1 | < > <= >= == != |
1 | && 逻辑与(且) |
1 | // 1为真 0为假 |
1 | // 优先级 从高到低 |
1 | 5 && 3; |
1 | ? 三目运算符 |
1 | // 表达式值为非0,执行语句;0不执行语句 |
1 | t = a; |
1 | switch(表达式) |
You only get one shot, do not miss your chance to blow.
你只有一发子弹,不要错过引爆全场的机会。
1 | // 大括号是程序块的分界符,后不跟分号(;) |
C语言程序的
输入/输出都要通过库函数完成库函数,就是“仓库“里的函数
在C语言要使用“库函数”,首先要用
#include将一些文件包含到程序中,文件后缀一般为.h,称为头文件。
1 | #include <stdio.h> |
单字符输出
1 | putchar('a'); |
单字符输入
1 | getchar(); |
格式输出函数
1 | #include <stdio.h> |
格式字符串控全体,
数据替换百分比
(%)字符c,正数d
小数f,指数e
欧
(o)八叉(x)六u无号字符串
s要牢记间数全宽点小数
负号表示左对齐
格式输入函数
1 | scanf("格式控制字符串", 变量1的地址, 变量2的地址, ...) |
1 | #include <stdio.h> |
scanf, 键盘输入
后为地址,不能输出。
间数宽度,%c全读。
非格式符,麻烦用户。
| 函数 | 功能 | 用法举例 |
|---|---|---|
| sqrt(x) | 求x的算术平方根,x≥0 | sqrt(2) |
| abs(x) | 求x(整数)的绝对值 | abs(-5) |
| fabs(x) | 求x(实数)的绝对值 | fabs(-2.5) |
| log(x) | 求自然对数ln(x) | log(2) |
| exp(x) | 求ex的值 | exp(2) |
| pow(x,y) | 求xy的值,注意x^y是“按位异或”不是求xy | pow(2, 3) |
| sin(x) | 求x的正弦值,x单位为弧度 | sin(30*3.14/180) |
| cos(x) | 求x的余弦值,x单位为弧度 | cos(3.14) |
| tan(x) | 求x的正切值,x单位为弧度 | tan(1.3) |
| asin(x) | 求sin-1(x)的值(弧度),-1≤x≤1 | asin(1) |
| acos(x) | 求cos-1(x)的值(弧度),-1≤x≤1 | acos(0) |
| atan(x) | 求tan-1(x)的值(弧度) | atan(-82.24) |