[啃书] 第2篇 - 数组及其操作/函数/指针/引用
啃书部分已单独做成Gitbook了,后续不再更新。详情访问个人网站ccoding.cn
说在前面
既然周末,就再来一篇吧,之后隔一天更一篇。
本篇总结自《算法笔记》2.5-2.7
正文
知识点1:数组(一维)
数据类型 数组名[数组大小]
数组大小不能是变量,所以该定义要求定死了。
数组初始化
int a[10] = {5, 3, 2, 6, 8, 4};
上方数组没有全部初始化,未初始化部分默认值为很大的随机数或0(看编译器不同赋值不同)。
若完全不初始化如int a[10],数组中每个元素都可能是随机数,不一定默认为0。
所以需要整个数组都赋初值为0,则只要把第一个赋为0即可,如int a[10] = {0};
知识点2:冒泡排序
总体说:进行n-1轮排序,忽略上一轮的最大值,每轮都把最大的“沉底”后移,其它的数像“冒泡”一样前移。
具体说:
比如5个数,从第1位第2位开始两两比较,前者大则交换,一直到第4位第5位比较算一轮,得到第5位为最大;
继续下一轮两两比较不过到第3位和第4位比较完为止,因为第5位已经是最大值了;
继续下一轮两两比较到第2位第3位为止,因为第4位也已经确定了;
继续下一轮两两比较第1位第2位。
程序员不知道这个就有点说不过去了,直接上代码吧。
知识点3:数组(二维)
数据类型 数组名[第一维大小][第二维大小]
数组初始化
int a[5][6] = {{3, 1, 2}, {8, 4}, {}, {1, 2, 3, 4, 5}};
代码示例:两个数组对应值累加,存入第一个数组。
输入两个3×3的矩阵,输出一个3×3的结果矩阵。
注:如果数组长度过大超过106则将其定义在主函数外(静态存储区),否则在主函数中申明(系统栈)程序会异常退出。
三维数组及更高维度的数组与二维类似,定义要三个[],赋值要三层循环。如int a[3][3][3];
这一部分比较简单就不自己敲了,后面难的部分手打代码。
知识点4:memset对数组每个元素赋相同值(需string.h头文件)
memset(数组名, 值, sizeof(数组名));
建议只赋值为0或-1(因为memset按字节赋值,组成int的四个字节会被赋成相同值,而0和-1的二进制补码全为0和1,不容易出错,如果赋别的数则会不准确,所以别的数用fill函数,后面会讲)
memset速度快于fill
知识点5:字符数组
字符数组定义和初始化和普通数组一样
char str[15] = {'G', 'o', 'o', 'd', '', 's', 't', 'o', 'r', 'y', '!'};
也可以直接初始化字符串给字符数组(仅限初始化,其他地方赋值不行)
char str[15] = "Good Story!"
输入输出:
scanf,getchar,gets
printf,putchar,puts
gets
用来输入一行字符串;
gets识别换行符\n作为输入结束;
若前面有整数等输入则要先使用getchar把换行符接受掉,再进行gets,否则读空了;
存放于一维数组/二维数组的一维中;
puts
用来输出一行字符串;
将一维数组/二维数组的一维输出在界面上(并拼上一个换行符);
说明:二维字符数组可以当作字符串数组
字符数组的存放方式
一维字符数组/二维字符数组的二维末尾都有一个空字符\0表示存放字符串的结尾(作用是给puts和printf识别输出结束)
特别注意1:
\0的ASCII码为0即空字符NULL,占用一个字符位,因此开字符串数组要记得比字符串长度至少多1
\0不是空格,空格的ASCII码是32,切勿混淆。
特别注意2:
scanf和gets输入的字符串自带\0,而getchar等其它方法得到的字符数组一定要自己在末尾加上\0否则乱码。
知识点6:字符数组常用方法(string.h头文件)
strlen(字符数组);
返回第一个\0前字符的个数
strcmp(字符数组1,字符数组2);
返回两个字符串大小比较结果(字典序。正数:前者大,负数:前者小,零:一样大)
strcpy(字符数组1,字符数组2);
2复制给1,包括\0也复制了
strcat(字符数组1,字符数组2);
2接到1后面
知识点7:sscanf和sprintf(stdio.h)
sscanf(str, "%d", &n);
把字符数组str中的内容以%d的格式写到n中(从左到右)
sprintf(str, "%d", n);
把n以%d的格式写入到str字符数组中(从右到左)
类比记忆:scanf相当于scanf(screen, "%d", &n)把屏幕内容输到n中,而sscanf则把screen变成了字符串,同样的类比printf。
应用
int n;
double db; char str[100] = "2048:3.14,hello", str2[100] sscanf(str, "%d:%lf,%s", &n, &db, str2);
//分别取到n=2048,db=3.14,str2=hello
int n = 12; double db = 3.1415; char str[100], str2[100] = "good"; sprintf(str, "%d:%.2f,%s", n, db, str2); //str得到12:3.14,good
字符串处理问题的利器。
知识点8:函数
返回类型 函数名称(参数类型 参数){
函数主体
}
局部变量、形参只是实参的副本、int main()返回0在于告诉系统程序正常结束
数组作为参数 void change(int a[], int b[][5]){ }
函数的嵌套、递归
知识点9:指针
计算机中每个字节都有一个地址,指针可以理解为变量的地址。
变量的地址一般是它占用的字节中第一个字节的地址(比如一个int占了四个字节,其中第一个字节的地址)
对变量进行&取地址操作即可获得变量的地址。
指针是一个unsigned类型的整数。
知识点10:指针变量
用专门定义的类型来存放指针,
int* p; double* p; char* p; int* p1, p2; //p1是int*型,p2是int型 int *p1, *p2, *p3; //都是int*型 int a; int* p = &a; //指针类型赋值 int a; int* p; p = &a;//效果同上 int a,b; int *p1 = &a, *p2 = &b;//多个指针变量初始化 //星号可以跟着类型也可以贴着变量,除了多个定义时跟着变量,但具体是p存储了指针而不是*p //*p中的星号是找到p地址对应的变量,是一个对p的操作。 //int* p中的星号则是和int构成一个int*指针类型,不是独立的。 //所以为什么int* p更有利于理解而不是int *p
//改变变量内容不影响地址
对于int*型的指针变量p来说,p+1是指p所指的int型变量的下一个int型变量地址(跨越了一整个int型即4Byte,如果是double*型的指针变量p则跨越了一整个double型是8Byte)
知识点11:指针与数组
数组名称也作为数组的首地址使用,即 a==&a[0]
*a表示数组第一个数
*(a+1)表示数组第二个数(因为指针后移了再用星号取地址)
*(a+i)表示数组第i-1个数
示例代码:读取一整个数组并输出
遍历数组(指针方法)
指针的减法
因为是int*型,所以距离以int为单位,输出5而不是20。
知识点12:指针作函数参数
#include<stdio.h> void change(int* p){ *p = 233; } int main(){ int a = 1; int* p = &a; change(p); printf("%d\n", a); return 0; }
输出结果:233
传入了指针(p),通过指针把找到它所指向内容,把该内容(变量a)改变了。
交换两个数(指针做参数)
#include<stdio.h> void swap(int* a, int* b){ int temp = *a; //temp是存放指针指向的内容的,所以是int型而不是int*型 *a = *b; *b = temp; } int main(){ int a = 1, b = 2; int *p1 = &a, *p2 = &b; swap(p1, p2); printf("a = %d, b = %d\n", *p1, *p2); return 0; }
常见错误1:int* temp没赋初值直接*temp = *a,错在没赋初值的指针可能会指向系统工作区。
如果定义int* temp指针作为中间值,则使用*temp来暂存内容。
则一定记得要指针变量要赋初值,写成如下
常见错误2:int* temp直接暂存地址,把两个地址进行交换。
错在传过来的指针类型(也就是变量的地址)是一个副本,我们现在拿到的是变量a,b的地址,所以我们可以去找到他们修改,就可以实现交换;而我们不知道地址的地址,即我们只知道地址的内容(副本),无法找到地址,也就无法修改地址,不能修改就无法交换地址。
知识点13:引用
给某变量起个别名,该别名同样能访问到这个变量。
代替指针实现修改传入参数的功能,引用在实战中很常用。
#include<stdio.h> void change(int &x){ //可以贴着int,也可贴着x(注意区分取地址符&) x=1; } int main(){ int x = 10; change(x); printf("%d\n", x); return 0; }
输出结果:1
应用:交换地址
对指针的引用(对地址的引用,也就是给地址起个别名,可以直接访问到地址)
#include<stdio.h> void swap(int* &p1, int* &p2){ //对int*型指针的引用 int* temp = p1; p1 = p2; p2 = temp; } int main(){ int a = 1, b = 2; int *p1 = &a, *p2 = &b; swap(p1, p2); printf("a = %d, b = %d\n", *p1, *p2); return 0; }
原理是因为,int*可以理解为unsigned int型,所以就像一个普通变量取地址一样,而这里的普通变量碰巧也是地址,也就是地址的地址。
有点长了,分开再写一篇吧。
第二章剩下结构体、补充内容及黑盒测试,下一篇继续。
加油。
个人博客地址 cnblogs.com/cc1997;
个人网站地址 Ccoding.cn