ACM金牌带你零基础直达C语言精通-课程资料
本笔记属于船说系列课程之一,课程链接:ACM金牌带你零基础直达C语言精通https://www.bilibili.com/cheese/play/ep159068?csource=private_space_class_null&spm_id_from=333.999.0.0
你也可以选择购买『船说系列课程-年度会员』产品『船票』,畅享一年内无限制学习已上线的所有船说系列课程:船票购买入口https://www.bilibili.com/cheese/pages/packageCourseDetail?productId=598
做题网站OJ:HZOJ - Online Judge
Leetcode :力扣 (LeetCode) 全球极客挚爱的技术成长平台
1.地址
(重点)可以发现,对应字节的地址,他是连续起来的;所以可以之后得到了,变量的起始地址,也可以获得后面字节的地址;
代码理解:
#include<stdio.h> int main() { int a = 1; //%p是地址的格式占位符 //&a就是对变量a进行取地址 //取的地址也是对应字节的起始地址 printf("&a = %p\n", &a); return 0; }
执行结果:
这里的结果一般来说是不一样的,但是只要是0x开头的16进制就说明没有问题
16进制
在程序中十六进制的表示,以及使用:
#include <stdio.h> #include <limits.h> int main() { //如果要用十六进制对变量赋值 //需要在赋值前加上0x int a = 0x6f; //打印a变量对应10进制的值 printf("a(10) = %d\n", a); //打印a变量对应16进制的值 //%x是十六进制对应的格式占位符 printf("a(16) = %x\n", a); //打印a变量对应16进制的值 //%X是十六进制对应的格式占位符 //但是对应小写和大写的作用是 //打印时十六进制不是会有字母吗 //然后这里的大小写对应就是把那些字母规定打印时是小写还是大写 printf("a(16) = %X\n", a); //用16进制表示整形最大值和最小值 printf("MAX_INT = %d\n", INT_MAX); printf("MIN_INT = %d\n", INT_MIN); int max_int = 0x7fffffff; int min_int = 0x80000000; printf("max_int = %d\n", max_int); printf("min_int = %d\n", min_int); return 0; }
执行结果:
对应的最大值2进制转换的十六进制0x7fffffff如何来的:
通过这张图,也可以推出0x80000000如何来的
地址是一个几位二进制数据:
先看一段程序:
#include<stdio.h> int main() { int a; double b; char c; //获取每个类型的地址长度 //sizeof获取参数的对应字节长度 printf("sizeof(int &) = %lu\n", sizeof(&a)); printf("sizeof(double &) = %lu\n", sizeof(&b)); printf("sizeof(char &) = %lu\n", sizeof(&c)); return 0; }
执行结果:
可以发现每个类型对应的地址都是8位,那么一定就是每个类型对应的地址都是8位吗?
来看32位和64位系统:
通过对32位和64系统的理解后,可以发现地址不应的是8位的,要看操作系统是多少位有关系的。
2.数组
定义和使用:
程序中如何使用数组,以及概念对应的代码:
#include<stdio.h> //数组定义和使用 void test1() { int a[5] = {0}; for (int i = 0; i < 5; i++) { //a[i] 就可以访问数组对应下标为i的位置 //也可以进行赋值 a[i] = 2 * i; } for (int i = 0; i < 5; i++) { printf("a[%d] = %d\n", i, a[i]); } return ; } //动态数组的定义和使用 void test2() { //通过执行程序时来定义数组想要的大小 int n; scanf("%d", &n); //动态数组是无法初始化的 int a[2 * n]; for (int i = 0; i < 2 * n; i++) { a[i] = i * i; } for (int i = 0; i < 2 * n; i++) { printf("a[%d] = %d\n", i, a[i]) ; } return ; } //初始化进行对数组赋值 void test3() { // int a[5] = {0}; 这种初始化是将每个位置初始化为0 int a[5] = {1, 2, 3, 4, 5}; for (int i = 0; i < 5; i++) { printf("a[%d] = %d\n", i, a[i]); } return ; } //数组定义时不在[]中输入大小 void test4() { int a[] = {1, 2, 3, 4, 5, 6, 7}; //sizeof(a)获取数组字节大小 //sizeof(int)获取int类型字节大小 //相除就可以得到数组元素的个数 int len = sizeof(a) / sizeof(int); printf("sizeof(a) / sizeof(int) = %d\n", len); for (int i = 0; i < len; i++) { printf("a[%d] = %d\n", i, a[i]); } return ; } //数组的地址理解 void test5() { int a[5] = {1, 2, 3, 4, 5}; printf("a = %p\n", a); for (int i = 0; i < 5; i++) { printf("&a[%d] = %p\n", i, &a[i]); } return ; } int main() { printf("test1()\n"); test1(); printf("test2()\n"); test2(); printf("test3()\n"); test3(); printf("test4()\n"); test4(); printf("test5()\n"); test5(); return 0; }
执行结果:
通过test5()发现数组的地址也不是连续的啊,但是对于数组a的类型是整形,整形对应的字节是4字节,对于变量的地址就是他的起始地址,所以相差为4刚好满足连续条件,所以数组的元素是连续的。
素数筛:
代码实现:
#include<stdio.h> int prime[100] = {0}; void init_prime(int n) { //初始化0和1不是素数 prime[0] = prime[1] = 1; for (int i = 2; i <= n; i++) { if (prime[i]) continue;//如果被标记了,说明不是素数,就不用再去标记 for (int j = 2 * i; j <= n; j += i) { prime[j] = 1;//标记当前数为合数 } } return ; } int main() { //求1-n的素数 int n; printf("input n(0 < n && n <= 100) :"); scanf("%d", &n); init_prime(n); for (int i = 0; i <= n; i++) { if (prime[i]) continue;//被标记说明i是合数不打印 printf("%d\n", i); } return 0; }
执行结果:
优化后代码:
#include<stdio.h> int prime[100] = {0}; void init_prime(int n) { prime[0] = prime[1] = 1; //优化1 //证明:假如 n = a * b,a和b中一定有一个是小于等于根号下n的 //那么n也被小于等于根号n的素数给标记了 //举个例子100,根号下100就是10 //那么10下面的2,就已经把100标记了 for (int i = 2; i * i <= n; i++) { if (prime[i]) continue; //优化2 //证明: j = a * i, 当a < i时,j 已经被a这个素数标记了 //那么这里用a的话,就重复标记了 //例子a = 2 //j = 2 * i //当i = 2标记了6,循环是 j += i,标记顺序是4,6,8 //当i = 3时又标记了6,循环是 j += i,标记顺序是6, 9,12 for (int j = i * i; j <= n; j += i) { prime[j] = 1;//标记当前数为合数 } } return ; } int main() { //求1-n的素数 int n; printf("input n(0 < n && n <= 100) :"); scanf("%d", &n); init_prime(n); for (int i = 0; i <= n; i++) { if (prime[i]) continue;//被标记说明i是合数不打印 printf("%d\n", i); } return 0; }
执行结果:
这里为什么要去优化,因为这样循环的次数减少,那么程序的执行时间就会更少。在后续的算法与数据结构课中就是学习这个的重点.
二分查找算法:
二分查找是通过移动头指针和尾指针来进行查找的,当查找值确定不在另一个区域时,直接将头或尾指针进行移动来排除那块区域,进行下一次查找,而每次的位置就是头尾指针的中间,最后终止条件就是头指针的位置大于等于尾指针.
并且在使用二分查找算法时,一定要满足单调的条件!
代码演示:
#include <stdio.h> #include <time.h> #include <stdlib.h> //顺序查找 void find_1(int arr[], int x) { int cnt1 = 0; int flag1 = 0; for (int i = 0; i < 10; i++) { cnt1 += 1; if (arr[i] != x) continue; flag1 = 1; break; } if (flag1) { printf("Ok, find numbe, cnt = %d\n", cnt1); } else { printf("Not find cnt = %d\n", cnt1); } } //二分查找 void binary_search(int arr[], int n, int x) { int flag = 0; int cnt1 = 0; int head = 0, tail = n - 1;//定义头尾指针; while (head <= tail) {//当尾指针小于头指针时说明,没有找到结束循环 cnt1++; int mid = (head + tail) >> 1;//mid在头尾指针的中间 //数组是单调递增 //中间的位置都大于x说明在后半区域没有x,所以去掉 if (arr[mid] > x) tail = mid - 1;//因为mid位置也不等于x,可以把mid位置去掉,也就是-1 //中间的位置都小于x说明在前半区域没有x,所以去掉 else if (arr[mid] < x) head = mid + 1; else { //说明找到了 flag = 1; break; } } if (flag) { printf("Ok, find numbe, cnt = %d\n", cnt1); } else { printf("Not find cnt = %d\n", cnt1); } return ; } int main() { //产生一个随机种子 srand(time(0)); int arr[10] = {0}; for (int i = 1; i < 10; i++) { //rand()获取一个随机数 arr[i] = arr[i - 1] + (rand() % 10); } for (int i = 0; i < 10; i++) { i && printf(" "); printf("%d", arr[i]); } printf("\n"); int x; scanf("%d", &x); find_1(arr, x); binary_search(arr, 10, x); return 0; }
执行结果:
cnt表示查找次数,根据查找次数来看,顺序查找对比二分查找的效率要低.
多维数组:
代码演示:
#include<stdio.h> int main() { int b[3][4]; int cnt = 1; for (int i = 0; i < 3; i++) {//对行进行循环 for (int j = 0; j < 4; j++) {//对列进行循环 b[i][j] = cnt++; } } for (int i = 0; i < 3; i++) {//对行进行循环 for (int j = 0; j < 4; j++) {//对列进行循环 j && printf(" "); printf("%3d", b[i][j]); } printf("\n"); } return 0; }
执行结果:
字符数组:
重点:字符串是通过\0结尾!!
代码讲解:
#include<stdio.h> #include <string.h> int main() { char str1[10] = "abc";//只能在定义字符时使用 printf("str1 = %s\n", str1); //str1 = "def"这样是不行的 strcpy(str1, "def"); printf("str1 = %s\n", str1); char str2[] = "hello\0 world"; //用strlen判断str2的长度 //通过执行结果发现,strlen是统计到\0字符为止 //打印字符串也打印到\0字符结束 printf("strlen(str2) = %lu\n", strlen(str2)); printf("str2 = %s\n", str2) ; //通过发现\0也占用位置,str2的长度为13位 printf("sizeof(str2) = %lu\n", sizeof(str2)); str2[5] = 'a'; printf("str2 = %s\n", str2) ;//通过修改后发现,没有\0,可以打印完str2 char str3[] = "abcdef", str4[] = "abc"; //执行结果为100 //因为strcmp通过比较,str3和str4发现前3位相同 //第4位分别为'd'和'\0', 不相同返回的就是'd' - '\0'的值 //'d'对应的ASCLL值是100,\0对应的是0,那么结果就是100 printf("strcmp(str3, str4) = %d\n", strcmp(str3, str4)); str3[3] = '\0';// abc\0ef //通过这个比较发现,strcmp比较也是在\0结束 printf("strcmp(str3, str4) = %d\n", strcmp(str3, str4)); int arr[10]; for (int i = 0; i < 10; i++) { arr[i] = i * i; } for (int i = 0; i < 10 ; i++) { i && printf(" "); printf("%d", arr[i]); } printf("\n"); //第一个参数放置地址 //第二个参数放置初始化的值 //第三个参数放置数组的字节大小 memset(arr, 0, sizeof(int) * 10); for (int i = 0; i < 10 ; i++) { i && printf(" "); printf("%d", arr[i]); } printf("\n"); //全赋值为-1 memset(arr, -1, sizeof(int) * 10); for (int i = 0; i < 10 ; i++) { i && printf(" "); printf("%d", arr[i]); } printf("\n"); //全赋值为2 memset(arr, 2, sizeof(int) * 10); for (int i = 0; i < 10 ; i++) { i && printf(" "); printf("%d", arr[i]); } printf("\n"); //通过执行发现,结果和预想的不一样,因为memset是通过对每个字节赋值 //对一个元素进行分析用二进制表示,int 类型有4字节,一个字节8位 //00000010 00000010 00000010 00000010 //拿结果就是2 + 2 ^ 9 + 2 ^ 17 + 2 ^ 25 = 33686018 //和输出的结果是一样的 return 0; }
执行结果:
指针
1.指针变量也是变量
指针p里面存储的值是变量a的地址,但是指针p也有自己的地址;
对于指针p进行取值,对应的值就是变量a的值;
进行代码讲解:
#include<stdio.h> int main() { int *p1; double *p2; char *p3; int a = 123; double b = 45.6; char c = 'h'; //p1中存存储着a的地址 //例如只要有地址,你不会找不到这个位置 //可以理解位p1指向a p1 = &a; p2 = &b; p3 = &c; //查看指针是否指向对应的地址 printf("p1 = %p, &a = %p\n", p1, &a); printf("p2 = %p, &b = %p\n", p2, &b); printf("p3 = %p, &c = %p\n", p3, &c); //查看指针变量的地址 printf("&p1 = %p\n", &p1); printf("&p2 = %p\n", &p2); printf("&p3 = %p\n", &p3); //取值运算符 //是取对应指针里面存储的地址里面所存储的值 printf("*p1 = %d\n", *p1); printf("*p2 = %lf\n", *p2); printf("*p3 = %c\n", *p3); return 0; }
代码执行结果:
另一份代码,指针的3种使用:
1.将相应的实参的地址进行传入,想修改参数的值
2.在设计程序时,需要传出参数时,也就是运行完调用函数时,参数得到传出
3.指针可以用来接受数组
#include<stdio.h> void add_once(int x) { x += 1; return ; } //这里参数是指针 void add_two(int *p) { *p += 1;//那么这里进制修改的是指针指向地址的值,那么传出函数地址里的值也是改变后的 return ; } //当需要函数进行对参数传出时,可以使用指针来进行实现 void f(int n, int *sum_addr) { *sum_addr = (1 + n) * n / 2; return ; } //在这p可以当作数组来使用 void output(int *p, int n) { //[]表示运算符,p[i] = *(p + i); for (int i = 0; i < n; i++) { printf("p[%d] = %d\n", i, p[i]); } return ; } int main() { int a = 123; //这里用到了实参和形参 //形参的改变是不会影响实参的值 printf("a = %d\n", a); add_once(a); printf("a = %d\n", a); //这里用到了取地址 //参数传入的是地址 //因为指针存储的值是地址 add_two(&a); printf("a = %d\n", a); //传出参数 int b = 10, sum; f(b, &sum); printf("sum = %d\n", sum); int arr[10] = {9, 8, 5, 3, 6, 2, 1, 0, 4, 7}; //arr数组名表示数组的首地址 output(arr, 10); return 0; }
2.理解p + 1(p为指针)
在程序中理解一下:
#include<stdio.h> int main() { //1 int a, *p1 = &a; double b, *p2 = &b; //可以发现地址+1,是根据类型对应字节大小进行+的 printf("&a = %p\n", &a); printf("p1 + 0 = %p\n", p1 + 1); printf("p1 + 1 = %p\n", p1 + 2); printf("p1 + 2 = %p\n", p1 + 3); printf("p1 + 3 = %p\n", p1 + 4); printf("&b = %p\n", &b); printf("p2 + 0 = %p\n", p2 + 1); printf("p2 + 1 = %p\n", p2 + 2); printf("p2 + 2 = %p\n", p2 + 3); printf("-------------------\n"); //2 int arr[4] = {1, 2, 3, 4}; int *p3 = arr; for (int i = 0; i < 4; i++) { //同通过测试发现,p3 + i 和 &arr[i] + i的地址一样 printf("p3 + %d = %p\n", i, p3 + i); printf("&arr[0] + %d = %p\n", i, &arr[i] + i); //通过测试会发现,p3[i]和*(p3 + 1)效果一样 printf("p3[%d] = %d\n", i, p3[i]); printf("*(p3 + %d) = %d\n", i, *(p3 + i)); } printf("-------------------\n"); //3 int *p4[10];//指针数组, 每个位置可以存储一个地址 int arr1[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; for (int i = 0; i < 10; i++) { p4[i] = &arr1[i]; printf("*p[%d] = %d, arr1[%d] = %d\n", i, *p4[i], i, arr1[i]); } printf("-------------------\n"); //4 int (*p5)[10] = 0x0;//表示p4指向10个整形元素的指针 int arr2[30][10];//arr[i]代表10个整形元素 p5 = arr2;//p4 + 1也相当于跳10个整形元素,就相当于p4 + 4 == arr[1]; printf("p5 + 0 = %p\n", p5 + 0); printf("p5 + 1 = %p\n", p5 + 1); printf("p5 + 2 = %p\n", p5 + 2); return 0; }
执行结果:
1.可以发现,对应指针+1,那么地址就会加上相应类型字节大小,int 就+4,double 就+8
2.可以发现指针和数组首地址相加是一样的,并且p[i]和*(p + i)的结果也是一样的
3.通过测试发现,指针数组每个位置都可以存地址,并且使用和正常数组一样
4.int (*p)[10] 是指针p指向10个整形元素大小的指针,如果发生偏移的10个整形的字节,也就是40
3.理解*p(p为指针)
假如这里的type就是int 类型,那么指针p取值的字节长度就是橙色部分对应的值,那么现在将指针p转换为char类型的指针,在对p进行取值,那么就只取1字节,就是橙色部分的1/4,那对应的值;
例子:
代码解释:
#include<stdio.h> int main() { int n = 0x61626364; int *int_p = &n; char *p = (char *)&n; printf("*int_p = %d\n", *int_p); printf("*(p + 0) = %c\n", *(p + 0)); printf("*(p + 1) = %c\n", *(p + 1)); printf("*(p + 2) = %c\n", *(p + 2)); printf("*(p + 3) = %c\n", *(p + 3)); return 0; }
图中是变量n的二进制存储方式,如果指针是int类型就取4个字节, 如果指针为char类型那么取值就一个字节一个字节的取;为什么阅读顺序和电脑顺序不同,这就是操作系统的知识,由于现在的计算机大部分都是小端系统,数字的地位存储在内存的低位,就是比如数字地位在阅读时是64,那么就存在内存的地位,也就是指针的首地址,然后依次往后存储;
也可以这样来理解,数字的地位在我们阅读时是从左向右,高位在左的,比如123,1是高位;而计算中读取方式是321这样;
代码执行结果:
根据执行结果来看,可以得到内存的地位存储的是数字的地位, 也可以证明根据指针取值是根据类型进行取值的,什么类型取什么类型对应字节大小;
例子:
代码实现:
#include<stdio.h> int main() { int a, b, c, d; scanf("%d.%d.%d.%d", &a, &b, &c, &d); //unsigned int代表无符号整形,那么就没有符号位了 unsigned int n; //由于n是int类型,所以对他的地址需要强转为char *类型 char *p = (char *)&n; p[3] = a; p[2] = b; p[1] = c; p[0] = d; //%u为无符号整形的占位符 printf("n = %u\n", n); return 0; }
执行结果:
4.指针的几种等价形式(重要):
等价形式转换:
1-5每条形式中,都是等价互换的,如果理解不了其他的形式,可以记住自己能理解的
下面是代码演示:
#include<stdio.h> int main() { int arr[] = {1, 2, 3, 4, 5, 6, 7}; int *p = arr; for (int i = 0; i < 3; i++) { //(i + 5)[&p[1] - 2] //第一次转换(i + 5)[p + 1 - 2] //第二次转换*(i + 5 + p + 1 -2), 这里用到了[]运算符规则 //最终结果p[i + 4]; //所以下面代码等价于printf("%d\n", (i + 5)[&p[1] - 2]); printf("%d\n", (i + 5)[&p[1] - 2]); //所以最终打印的是p[4], p[5], p[6]的值; } return 0; }
执行结果:
5.数组指针与函数指针
指针理解:
两句话理解指针:
数组指针:
第四种就是容易搞错的,这个指针指向的是有10个int元素的指针,不是指针数组;
函数指针:
这里的*写在函数名前面,并且要加上小括号;
根据上面的定义得到了一个函数类型的指针,add就是指针,那么它指向的类型是返回值为int,并且两个参数都是int类型的函数;
代码演示:
#include <stdio.h> #include <stdlib.h> #include <time.h> void test1() { printf("function test1\n"); return ; } void test2() { printf("function test2\n"); return ; } void test3() { printf("function test3\n"); return ; } //这里的函数中指针p,指向的就是返回值为void,参数列表为空的函数 void (*p)(); int main() { srand(time(0));//产生随机种子 p = test1;//让指针指向test1 p(); p = test2; p(); p = test3; p(); printf("---------------\n"); //定义一个函数指针数组,初始化0,1,2位置分别为test1,test2,test3 void (*arr[3])() = {test1, test2, test3}; for (int i = 0; i < 10; i++) { //rand() 取随机数,%3取随机数0,1,2 arr[rand() % 3](); } return 0; }
6.内存管理方法(常用)
下面是对每个函数的使用:
#include<stdio.h> #include <time.h> #include <stdlib.h> #include <string.h> void get_value(int *arr) { for (int i = 0; i < 10; i++) { arr[i] = rand() % 100; } return ; } void output(int *arr, int n) { for (int i = 0; i < 10; i++) { if (i == 5) printf("\n"); else i && printf(" "); printf("arr%d[%d] = %.2d", n, i, arr[i]); } printf("\n-------------------------\n"); return ; } int main() { srand(time(0)); //利用malloc开辟40个字节大小的空间,int为4字节,乘10那就是40个字节大小 //由于malloc返回的是void *类型的地址然后 //需要强转为int *类型的地址 //让后赋值给一个指针变量 //然后指针变量就可以当作拥有10个int类型的元素进行使用 //也就是int类型的数组,然后大小为10 int *arr1 = (int *)malloc(sizeof(int) * 10); get_value(arr1); output(arr1, 1); //使用方法和malloc差不多 //但是calloc会将每个字节初始化为0 int *arr2 = (int *)calloc(sizeof(int), 10); output(arr2, 2); get_value(arr2); output(arr2, 2); //因为你是在堆区借用的一块区域 //所以在不使用后需要还回去 //因为你不还回去,系统就一直以为你在占用这块区域 //所以在使用后必须要换回 free(arr1); free(arr2); char s1[100] = "hello world"; char s2[100]; char s3[100]; //第一个参数目标地址 //第二个参数被copy地址 //第三个参数copy字节数量 //为什么要多一个字节,因为字符串还有一个\0 memcpy(s2, s1, 12); memmove(s3, s1, 12); printf("s2 = %s\n", s2); printf("s3 = %s\n", s3); //这里需要使用到copy功能,建议用memmove //因为在copy时内存重叠,memcpy可能会造成错误 //而memmove会避免这种情况,所以建议使用memmove memcpy(s2 + 4, s2, 12); memmove(s3 + 4, s3, 12); printf("s2 = %s\n", s2); printf("s3 = %s\n", s3); return 0; }
运行结果:
7.const常量
用const定义变量后,变量就是常量了,然后变量在定义时初始化的值之后是不能被改变的;
代码解释:
#include<stdio.h> int main() { const int a = 123; const int b = 456; const int *p1 = &a; //这样的定义方式是p1是一个指针指向整形常量 //const是指向a变量的 //意思就是p1指向的空间的值是不能改变的 //但是p1的值是可以改变的 //*p1 = 1;这局代码就是不可以的 printf("*p1 = %d\n", *p1); p1 = &b; printf("*p1 = %d\n", *p1); //这样定义方式是p2是一个指针指向常量整形 //可以改变p2的值,但是p2指向区域里存储的值是不能改变 int const *p2 = &a; printf("*p2 = %d\n", *p2); p2 = &b; printf("*p2 = %d\n", *p2); int n = 789, m = 1000; //p3是一个常量指针指向整形 //可以改变p3指向区域里存储的值 //但是p3的值是不能改变的 //比如p3 = &m,就不可以 int *const p3 = &n; printf("*p3 = %d\n", *p3); *p3 = 1000; printf("*p3 = %d\n", *p3); return 0; }
执行结果:
8.typedef:将变量名变成类型别名
蓝色是typedef关键字,然后绿色是类型,红色是变量别名
代码理解:
#include<stdio.h> //此处我将long long附上一个别名ll //那么我在这行代码下面定义long long 类型就可以使用ll来定义 typedef long long ll; //Arr2Dim10是类型名 typedef int (*Arr2Dim10)[10]; //Fucn就是类型名 typedef void (*Func)(); void add() { int a = 1, b = 1; printf("a + b = %d\n", a + b); return ; } int main() { //用ll定义一个long long 类型的变量 ll a = 123; //用sizeof测量是否定义的是long long 类型 printf("sizeof(a) = %lu\n", sizeof(a)); int arr[5][10]; arr[0][1] = 1231; //这里定义了p就是数组指针 //用p指向arr二维数组 Arr2Dim10 p = arr; printf("p[0][1] = %d\n", p[0][1]); //这里定义了Func类型定义了一个函数指针,指向了add函数 Func p2 = add; p2(); return 0; }
执行结果:
本章小结:
对于地址理解到他在计算机中的作用,以及是如何对应变量的;然后是理解数组的定义和使用,以及多维数组和字符串的使用,还有字符串的细节问题;最后理解指针是如何使用和定义,以及对于指针的各种方式是如何去理解。结合代码带去自己的疑问然后去实现,看代码实现的结果和你的想法有多少差异,这样对于疑问可以加深映像。
最后有些很多问题在笔记中没有写出来,只有百分之70左右的内容在笔记上,对于视频中的一些问题,可以在自己的笔记上添加。
加油,看到这里你已经超过百分之95的人了。