[啃书] 第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型,所以就像一个普通变量取地址一样,而这里的普通变量碰巧也是地址,也就是地址的地址。


 

 

有点长了,分开再写一篇吧。

第二章剩下结构体、补充内容及黑盒测试,下一篇继续。

加油。

 

posted @ 2020-05-24 10:59  ChaosBlog  阅读(190)  评论(0编辑  收藏  举报