上一章节:
一、重学C++—C语言基础-CSDN博客https://blog.csdn.net/weixin_36323170/article/details/146002496?spm=1001.2014.3001.5502
本章节代码:
cPart2 · CuiQingCheng/cppstudy - 码云 - 开源中国https://gitee.com/cuiqingcheng/cppstudy/tree/master/cPart2
一、数组
数组:可以形象理解为一个整齐放置盒子的架子,每个盒子里放置了一种定义好类型的数据,每个盒子有自己的编号,编号为整型,从0依次往上递增。
1、数组的定义与使用
类型 数组名[数组大小]
int number[5];
1.1关于数组大小的三点说明:
(1)、数组若在定义时,不给数组赋值,则必须要指定其数组大小,且数组大小为常量,可以是数值常量,也可以是普通常量,这里就可以联系上面例子,列举放盒子的架子,一样,如果都不知道有多少个盒子,那么数组这个架子该如何创建大小。
(2)、数组若定义时,就给初始化赋值了,如下:
int number[]={1,2,3,4,5};
这里就已经指定了多少个放数据的盒子,所以这里就可以不需要对数组大小进行说明。
(3)、除了上述两种,当数组作为形参时,不需要指定大小。(形参,可以先记下,后续函数中会讲解)
void printArray(int arr[]) {// 函数体
}
1.2、二维数组和多维数组
二维数组:一维数组构成的架子垒起来,按照行进行存储数据;
二维数组中可以把 行数省略,列数不能省略
int a[4][4];
int b[][4] = {1,2,3,4,21,22,23,24};
多维数组:a[][3][4]....:只能省略第一个[]中的数
// 数组int a[10];for(int i = 0; i < 10; ++i){a[i] = i*10;}printf("arr a size : %d\n", sizeof(a));// 获取数组所占字符数int b[SIZE];for(int j = 0; j < SIZE ; j++){b[j] = a[j] / 2;}float fArr[] = {3.4, 4.5, 6.6};int num[2][4];int nn[][2] = {1,2,3,4,21,22,23,24}; // 多维数组初始化后可以不指定,第一维大小
二、指针
指针可以理解成是一个特殊的 “身份证”,这个身份证上记录着某个东西存放的位置,以及这个位置的名称。
指针的本质
在计算机的内存里,每个存储单元都有一个独一无二的地址,就好像每个房间都有一个门牌号一样。指针就是用来存放这些地址的变量。
例如:
int num = 10;
int *p; // 指针的定义
p = # // '&'这个符号时取地址,将地址值付给指针P
printf("%d\n", *p); // '*'这个符号表示取值,表示将P这个地址下的值取出来。
printf("pointer size:%d\n", sizeof(p)); // 获取指针大小,32位系统,就是4个字节,64位系统,就是8个字节;
printf("pointer = %p\n",p);//%p表示按照16进制的形式输出
指针用来存放内存地址,因为在同一个系统下,指针的大小是固定的,比如在32位系统下,指针对应的大小就是4个字节。
指针相关的运算符
&:取地址运算符,运算结果为操作数的指针
*:指针运算符,运算结果为指针所指向的数据的引用
指针进行 +/-运算。
int *p; // 指针的定义p = # // '&'这个符号时取地址,将地址值付给指针Pprintf("%d\n", *p); // '*'这个符号表示取值,表示将P这个地址下的值取出来。printf("pointer size:%d\n", sizeof(p)); // 获取指针大小,32位系统,就是4个字节,64位系统,就是8个字节;printf("pointer = %p\n", p);//%p表示按照16进制的形式输出指针地址。int data[4] ={3,4,5,6};int *pNum = data; // 数组名为指针,数组名是指针常量,不能改变。pNum++;printf("pNum -> value: %d\n", *pNum); // 指针 ++,表示指针所直指向的内存地址,增加了一个int型数据的间隔。所以这里输出 4int *pData = &data[3]; // 将数组的第四个数据地址赋值给pData;pData -= 1;printf("pData -> value: %d\n", *pData); // 指针-1,表示指针所直指向的内存地址,减少了一个int型数据的间隔。所以这里输出 5// 指针在进行加减时,要注意不要访问越界。
指针常量(指针类型的常量:指针本身市场量):一旦定义就不能被赋值、改变,但是可以改变指针所指向的数据。
常量指针(常量的指针:指针指向的是常量):它被指向的一个数是一个常量(在指针看来,也就是说通过p无法去修改它所指向的地址的数据,但是p本身是变量,可以对他进行赋值,更改)
常量指针常量(指针本身是常量,指针指向的也是常量):定义的标识符是个常量的指针所以他的值不能任意改变,也就是他所指向的地址值不能变,同时由于他是一个指针型的常量,所以他对应的地址的值,不能是赋值,只能读取。
二级指针和多级指针
二级指针,指针的指针,即指针变量的地址;这里一般用于存放某个数据单元的地址的地址,这样要修改这个地址下创建的地址,可以这样使用,建议不要用太多级指针。一般到二级,就可以了,否则代码可读性会大大降低,从而增加调试理解难度。
三、函数
函数:可以形象的理解为一个个小的 “工作车间”,每个车间都有特定的任务,能帮你把复杂的大工作拆分成很多小工作,让程序更有条理。
函数定义:
上面形象的把函数比喻成一个“工作车间”,那么一个车间实现某个具体功能时,往往需要原材料,即函数参数-》形参,函数工作完了,会输出一个结果,或者是返回某个状态,这里就有返回值,跟出参的概念。
例如下,计算两数之和:
int add(int a, int b) {return a + b;
}
- int:这是函数的返回类型,说明这个函数做完工作后会返回一个整数。
- add:这是函数的名字,就像车间的名字,方便我们后面调用它。
- (int a, int b):这是函数的参数列表,也就是这个车间需要的材料。这里需要两个整数a和b。
- return a + b;:这是函数的核心工作内容,把两个数相加,然后把结果返回出去。
函数调用:
函数的声明定义,必须要在函数调用前,这里很好理解,若为进行声明定义,那么调用处怎么会知道这个函数的组成。
定义好函数后,就可以在其他地方调用它,就像在需要的时候去使用某个车间。比如:
#include <stdio.h>int add(int a, int b) {return a + b;
}
void addOutput(int n, int m, int *sum)
{*sum = n+m;n+=5;m+=10;if((*sum) < 400){addOutput(n,m,sum); // 递归调用}
}int main() {int result = add(3, 5);printf("两数之和是: %d\n", result);int sum = 0, k = 66;addOutput(result, k, &sum); // 这里的sum是出参, result/k,为入参 返回值为voidprintf("sum: %d, k = %d, result=%d \n", sum, k, result);int (*pAdd)(int, int); // 函数指针pAdd = add;int ret = pAdd(39,45);printf("ret: %d\n", ret);return 0;
}
函数的递归调用:即在函数执行代码段中的某一个环节,继续调用该函数,这里要注意,递归要设立退出机制,不能让函数一直处于递归调用循环的状态。
函数的作用
代码复用:如果有一段代码需要在不同的地方多次使用,就可以把它写成一个函数。这样每次需要用的时候,直接调用函数就行,不用重复写代码。比如上面的add函数,在很多地方要计算两数之和时,都能调用它。
模块化设计:把一个大的程序拆分成很多小的函数,每个函数负责一个小任务。这样程序的结构会更清晰,也更容易维护和调试。就像一个大工厂,分成很多小车间,每个车间负责一部分工作,管理起来更方便。
函数代码太长,要进行拆分成小的功能模块。
函数的参数和返回值
函数的参数:就是如上面的 int a, int b,这里为函数的形参,按照功能又可以分为,入参,跟出参,比如这里的sum就是出参,传递的是指针,在函数执行完后,指针指向的位置,值发生了修改,而另外两个参数的值不发生改变。
返回值:函数可以有返回值,也可以没有返回值(返回类型为
void)。返回值就像是车间产出的产品,函数做完工作后把结果返回出去。如果函数不需要返回结果,就用
void作为返回类型。
函数指针:函数名为函数的指针。
四、结构体
结构体就像是一个 “收纳盒”,可以把不同类型的数据收纳在一起,形成一个新的数据类型。
结构体的定义
定义结构体就像是设计一个收纳盒,要说明这个盒子里可以放哪些东西。比如,你想设计一个收纳盒来存放一个人的信息,包括姓名、年龄和身高,就可以这样定义:
struct Person {char name[20]; // 姓名int age; // 年龄float height; // 身高
};
结构体变量的定义和使用
定义好结构体后,就可以用它来创建具体的变量,就像按照设计好的收纳盒样式做出一个个实际的盒子。例如:
#include <stdio.h>
#include <string.h>struct Person {char name[20];int age;float height;
};int main() {// 定义一个结构体变量struct Person p1;// 给结构体变量的成员赋值strcpy(p1.name, "张三");p1.age = 20;p1.height = 1.75;// 输出结构体变量的成员值printf("姓名: %s\n", p1.name);printf("年龄: %d\n", p1.age);printf("身高: %.2f\n", p1.height);return 0;
}
结构体的作用
组织数据:当需要处理一组相关的数据时,把它们放在一个结构体里,能让数据的组织更有条理。比如上面的例子,把一个人的姓名、年龄和身高放在一个结构体里,方便对这个人的信息进行管理。
函数参数传递:可以把结构体作为函数的参数传递,这样就能一次性传递多个相关的数据。例如,写一个函数来打印辆车的信息:
#include <stdio.h>
#include <string.h>
struct Person {char name[20];int age;float height;
};typedef struct car{char name[20]; // 名称float wight; // 宽高float height;char colorChr[20]; //颜色
}CAR;void printCar(CAR C1) {printf("品牌: %s\n", C1.name);printf("颜色: %s\n", C1.colorChr);printf("高: %.2f\n", C1.height);printf("宽: %.2f\n", C1.height);
}int main() {// 定义一个结构体变量struct Person p1;// 给结构体变量的成员赋值strcpy(p1.name, "张三");p1.age = 20;p1.height = 1.75;// 输出结构体变量的成员值printf("姓名: %s\n", p1.name);printf("年龄: %d\n", p1.age);printf("身高: %.2f\n", p1.height);// 定义一个变量车CAR C1;strcpy(C1.name, "奔驰");strcpy(C1.colorChr, "red");C1.height = 1.7;C1.wight = 2.2;printCar(C1);return 0;
}
注意:
在暴露给外部调用时,尽量不要使用结构体传参数,这样针对只是少数值修改时,不需要创建整个结构体对象。
五、共用体(使用场景较少)
在 C 语言里,共用体(也叫联合体)就像是一个特殊的 “小房间”,这个房间每次只能住 “一个人”,也就是每次只能存放一种数据,但可以存放不同类型的数据。
共用体的定义
定义共用体就像是设计这个特殊的 “小房间”,要说明这个房间可以让哪些 “人” 住进来,也就是能存放哪些类型的数据。比如,你设计一个房间,它既可以让一个整数 “住”,也可以让一个浮点数 “住”,就可以这样定义:
union Data {int i;float f;
};
union:这是定义共用体的关键字。
Data:这是共用体的名字,就像房间的名字,方便后面使用。
大括号里的内容就是这个房间可以容纳的数据类型,这里有一个整数i和一个浮点数f。
共用体变量的定义和使用
定义好共用体后,就可以用它来创建具体的变量,就像按照设计好的房间样式造一个实际的房间。例如:
#include <stdio.h>union Data {int i;float f;
};int main() {// 定义一个共用体变量union Data d;// 给共用体变量的整数成员赋值d.i = 10;printf("整数的值: %d\n", d.i);// 给共用体变量的浮点数成员赋值d.f = 3.14;printf("浮点数的值: %.2f\n", d.f);printf("整数的值: %d\n", d.i);// 这里整型数据已经被覆盖return 0;
}
在这个例子中,union Data d;创建了一个名为d的共用体变量。然后通过.操作符来访问共用体变量的成员,给它们赋值并输出。要注意,每次给一个成员赋值后,之前存的数据就会被覆盖,因为这个 “小房间” 每次只能住 “一个人”。
共用体可以节省内存空间,现阶段,内存空间往往十分充裕,因而,共用体使用面就减少了。