数组
数组的基本语法
什么是数组
- 数组是 C 语言中的一种数据结构,用于存储一组具有相同数据类型的数据。
- 数组中的每个元素可以通过一个索引(下标)来访问,索引从 0 开始,最大值为数组长度减 1。
数组的使用
类型 数组名[元素个数];
int arr[5];
演示
#include <stdio.h>
int main(){
// 声明一个长度 | 空间为 5 的数组
// 完全不初始化
int arr[5];
// 可以通过下标来获取数组的元素
printf("第0个元素:%d \n" , arr[0]);
printf("第1个元素:%d \n" , arr[1]);
printf("第2个元素:%d \n" , arr[2]);
printf("第3个元素:%d \n" , arr[3]);
printf("第4个元素:%d \n" , arr[4]);
//通过 [下标] 的方式修改数组中的元素值
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
arr[3] = 4;
arr[4] = 5;
printf("第0个元素:%d \n" , arr[0]);
printf("第1个元素:%d \n" , arr[1]);
printf("第2个元素:%d \n" , arr[2]);
printf("第3个元素:%d \n" , arr[3]);
printf("第4个元素:%d \n" , arr[4]);
return 0 ;
}
数组的初始化
- 在定义数组的同时进行赋值,称为初始化
- 全局数组若不初始化,编译器将其初始化为零
- 局部数组若不初始化,内容为随机值
演示
#include <stdio.h>
int main(){
//1. 声明数组并且初始化
// 完全初始化
int arr[5] = {10,20,30,40,50};
//看看数组的元素
printf("第0个元素:%d\n" , arr[0]);
printf("第1个元素:%d\n" , arr[1]);
printf("第2个元素:%d\n" , arr[2]);
printf("第3个元素:%d\n" , arr[3]);
printf("第4个元素:%d\n" , arr[4]);
printf("\n\n");
//-----------------------------------
//2. 声明数组并且初始化:: 检测是否有设置初始化值,如果有,剩下不赋值,也会自动赋值为0
// 未完全初始化
int arr2[5] = {10};
//看看数组的元素
printf("第0个元素:%d\n" , arr2[0]);
printf("第1个元素:%d\n" , arr2[1]);
printf("第2个元素:%d\n" , arr2[2]);
printf("第3个元素:%d\n" , arr2[3]);
printf("第4个元素:%d\n" , arr2[4]);
//3. 声明数组并且初始化 :: 也可以不声明数组的长度,由编译器检测元素的个数来取得数组的长度
int arr3[] = {10,20,30,40,50};
printf("\n\n");
//看看数组的元素
printf("第0个元素:%d\n" , arr3[0]);
printf("第1个元素:%d\n" , arr3[1]);
printf("第2个元素:%d\n" , arr3[2]);
printf("第3个元素:%d\n" , arr3[3]);
printf("第4个元素:%d\n" , arr3[4]);
return 0 ;
}
数组名
- 数组名是一个地址的常量,代表数组中首元素的地址
#include <stdio.h>
int main(){
//1.声明一个长度为5的数组,并且完成初始化: 10,20,30,40,50;
int arr[] = {10,20,30,40,50};
//2. 获取元素的地址
printf("第0个元素的地址:%#x\n" , &arr[0]);
printf("第1个元素的地址:%#x\n" , &arr[1]);
printf("第2个元素的地址:%#x\n" , &arr[2]);
printf("第3个元素的地址:%#x\n" , &arr[3]);
printf("第4个元素的地址:%#x\n" , &arr[4]);
//3. 有一个特殊的点。如果直接操作数组的名字,等于是拿到了首元素的地址。
printf("直接打印数组的名字:%#x\n" , arr);
return 0 ;
}
数组遍历
#include <stdio.h>
int main(){
//1.声明一个长度为5的数组,并且完成初始化: 10,20,30,40,50;
int arr[8] = {10,20,30,40,50};
//2. 打印数组的每一个元素
for(int i = 0 ; i < sizeof(arr) / sizeof(int) ; i++){
printf("第%d个元素的值是:%d \n" , i , arr[i]);
}
printf("\n");
//3. 计算出来数组的长度:
printf("计算数组的占用总空间:%d\n" , sizeof(arr));
printf("计算int的占用总空间:%d\n" , sizeof(int));
printf("数组的长度是:%d\n" , sizeof(arr) / sizeof(int));
return 0 ;
}
数组案例
数组的最大值
#include <stdio.h>
int main(){
//1.声明一个长度为5的数组,并且完成初始化
int arr[] = {10,2,33,114,25};
//2. 具体操作
//2.0 先定义一个数字,可以看成是一个标杆
int temp = arr[0];
//2.1 遍历每一个元素
for(int i=1;i<sizeof(arr) / sizeof(int);i++){
if(arr[i] > temp){
temp = arr[i];
}
}
printf("最大的数:%d \n" , temp);
return 0 ;
}
数组的逆置
#include <stdio.h>
int main(){
//1. 定义数组
int arr[] = {10,20,30,40,50,60};
//2. 数组的逆置
//2.1 定义左边【开始】 和 右边【结束】的下标|索引
int left = 0;
int right = (sizeof(arr) / sizeof(int)) - 1; // 4
while(left < right){
//交换
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
//变化下标
left++;
right--;
}
//3. 打印数组
for(int i=0;i<sizeof(arr) / sizeof(int);i++){
printf("第 %d 个元素是: %d \n" , i , arr[i]);
}
return 0;
}
数组和指针
通过指针操作数组元素
- 数组名字是数组的首元素地址,但它是一个常量
- * 和 [] 效果一样,都是操作指针所指向的内存
演示
#include <stdio.h>
int main(){
//1. 定义数组
int arr[] = {10,20,30,40,50,60};
//2. 简单:
int *p0 = &arr[0];
int *p1 = &arr[1];
int *p2 = &arr[2];
//3. 解引用
printf("p0解引用:%d\n" ,*p0); // 10
printf("p1解引用:%d\n" ,*p1); // 20
printf("p2解引用:%d\n" ,*p2); // 30
//========================================
//4. 数组名字其实就是代表首元素的地址
int * parr = arr; // int * parr = &arr[0]
printf("parr解引用:%d\n" ,*parr); // 10
printf("parr+1解引用:%d\n" ,*(parr+1)); // 20 此处进行指针偏移,让他指向下一个元素的地址,在对其解引用得到此处元素的值
printf("parr+2解引用:%d\n" ,*(parr+2)); // 30
printf("parr+3解引用:%d\n" ,*(parr+3)); // 40
printf("parr+4解引用:%d\n" ,*(parr+4)); // 50
//5. 是否可以直接操作数组的名字呢?【换一个角度看:把数组的名字当成是一个指针来对待!】
// printf("对arr解引用: %d \n", *arr );
/*int a = 10;
printf("a的值:%d\n" , a);
printf("a的值:%d\n" , 10);
int * pa = &a;
printf("pa解引用 %d \n" , *pa);
printf("pa解引用 %d \n" , *(&a));
*/
return 0;
}
#include <stdio.h>
int main(){
//1. 简单
int a = 10;
int * pa = &a;
printf("pa解引用:%d \n" , *pa);
printf("pa解引用:%d \n" , *(&a));
//=============================
printf("\n\n");
//1. 定义数组
int arr[] = {10,20,30,40,50,60};
//1.1 定义i一个指针指向首元素
int * p0 = &arr[0];
for(int i=0;i<6;i++){
printf("第 %d 个元素的值是: %d \n" ,i , * (p0+i) );
}
printf("\n\n");
// 数组名字就是首元素的地址,其实就把它看成一个指针即可。
for(int i=0;i<6;i++){
printf("第 %d 个元素的值是: %d \n" ,i , *(arr+i) );
}
/*printf("(p0+0)解引用: %d\n" , *(p0+0));
printf("(p0+1)解引用: %d\n" , *(p0+1));
printf("(p0+2)解引用: %d\n" , *(p0+2));
printf("(p0+3)解引用: %d\n" , *(p0+3));
printf("(p0+4)解引用: %d\n" , *(p0+4));
printf("(p0+5)解引用: %d\n" , *(p0+5));*/
return 0;
}
指针数组
- 指针数组,它是数组,数组的每个元素都是指针类型
演示
#include <stdio.h>
int main(){
int a = 10 ;
int b = 20;
int c = 30;
int * pa = &a;
int * pb = &b;
int * pc = &c;
//一个数组里面装好多个指针[地址]
int* arr[3] = {pa ,pb ,pc};
//遍历数组,得到是地址
for(int i=0;i< 3;i++){
printf("第 %d 元素是:%#x \n" , i , arr[i]);
}
printf("\n\n");
//此处对其解引用,得到的是地址对应的值
for(int i=0;i< 3;i++){
printf("第 %d 元素指向位置的值是:%d \n" , i , *arr[i]);
}
return 0;
}
数组名做函数参数
- 数组名做函数参数,函数的形参本质上就是指针
演示
#include <stdio.h>
//定义一个函数,用来遍历数组。三种方式都可以 传进来的就是一个地址
//void print_arr(int arr[5] , int size){
//void print_arr(int arr[] , int size){
void print_arr(int * arr , int size){
// 数组的名字单独使用即代表首元素的地址.. 传递进来的就是首元素的地址
//int size = sizeof(arr) / sizeof(int);
//printf("函数内部的size: %d \n" , size);
for(int i=0;i< size;i++){
printf("第 %d 元素是: %d \n" , i ,arr[i]);
}
}
int main(){
//数组在当成参数传递的时候,其实传递过去的是首元素的地址!!!
int arr[5] = {10,20,30,40,50};
int size = sizeof(arr) / sizeof(int); //在外面没有传递参数的时候是可以计算长度的
printf("函数外部的size: %d \n" , size);
print_arr(arr , size);
//print_arr(&arr[0]);
return 0;
}
注释:这里要注意的是,数组当成参数传递的时候,传递过去的是首元素的地址。计算数组长度的时候只能在函数外面计算,在函数里面计算就会失真,因为传进去的是地址。所以我们定义函数的时候还要给他一个参数size。
字符数组与字符串
字符串定义
#include <stdio.h>
int main(){
//1. C语言里面没有字符串的类型,只能通过char[] 来描述它。还需要在数组的最后一位写上 \0 才能表示它是字符串
char c[] = {'a' , 'b' , 'c' , '\0'};
printf("c=%s \n" , c);
//2. 当字符数组里面出现多个 \0 的时候,认为字符串就是到 第一个 \0 截止
char c2[]={'a','b','c','\0','d','e','\0'};
printf("c2=%s \n" , c2);
// 中文在GBK编码下占 2个字节, 在UTF-8的编码下占3个字节
char c3[]={'你','好','\0'};
printf("c3=%s \n" , c3);
// 字符串其实还有一种简写的写法:编译器会自动在后面加上 \0 我们不需要加
char c4[] = "abc";
printf("c4=%s \n" , c4);
char c5[] = "哈喽你好";
printf("c5=%s \n" , c5);
return 0;
}
字符串的输入输出
#include <stdio.h>
int main(){
//1.获取键盘的输入
printf("请输入您的名字:\n");
char name[20] ;
//scanf 在获取的内容中右空格的时候,就结束获取了。
//scanf("%s" , name);
// gets本身就是获取字符串的。所以不用写格式
gets(name);
printf("输入的内容是:%s \n" , name);
printf("\n");
//使用数组的方式,遍历出来每一个字符
for(int i=0;i< 20;i++){
if(name[i] == '\0'){
break;
}
printf("第 %d 个元素是: %c \n" , i , name[i] );
}
return 0;
}
字符指针
- 字符指针可直接赋值为字符串,保存的实际上是字符串的首地址
-
- 这时候,字符串指针所指向的内存不能修改,指针变量本身可以修改
#include <stdio.h>
int main() {
char *p = "hello"; // 和 const char *p = 'hello' 等价,有没有const都一样
// 指针变量所指向的内存不能修改
// *p = 'a'; // err
printf("p = %s\n", p);
// 指针变量可以修改
p = "world";
printf("p = %s\n", p);
return 0;
}
字符串常用库函数
strlen
strcyp
stecat
strcmp
四种函数演示
#include <stdio.h>
#include <string.h>
int main(){
//1. strlen :: 计算字符串的长度 【有几个字符】
char c1[] = "abc12";
printf("c1的长度是:%d \n" , strlen(c1));
//2. strcpy :: 拷贝字符串到目标字符串身上:
char c2[20] = "ab";
char c3[10] = "123456789";
// 把c3 拷贝到 c2身上 【会覆盖掉c2的字符串】
strcpy(c2 , c3);
printf("c2=%s\n" , c2);
//3. strcat:: 拼接字符串
char c4[20] = "ab";
char c5[10] = "123456789";
strcat(c4 , c5);
printf("c4=%s\n" , c4);
//4. strcmp:: 比较字符串
char c6[10] = "abc";
char c7[10] = "abc";
// strcmp : 返回值 0 表示相等 大于0 表示第一个字符串更大, 小于0 表示第一个字符串更小
if( strcmp(c6 ,c7 ) == 0 ){
printf("相等\n");
}else{
printf("不相等~!\n");
}
return 0;
}
复合类型
结构体
概述
- 有时我们需要将不同类型的数据组合成一个有机的整体,如:一个学生有学号/姓名/性别/年龄/地址等属性
-
- 这时候可通过结构体实现
- 结构体(struct)可以理解为用户自定义的特殊的复合的“数据类型”
声明结构体
#include <stdio.h>
//1. 定义结构体
struct student{
char name[20];
int age ;
char gender[5];
};
//2. 定义结构体:: 书 【书名、作者、价格】
struct book{
char name[50];
char author [20];
float price;
};
int main(){
return 0;
}
结构体指针
#include <stdio.h>
//1. 定义结构体
struct student{
char * name ;
int age ;
char * gender;
};
int main(){
char * a = "abc";
a = "ccc";
//1. 普通的结构体变量
struct student s ;
//1.1 后续赋值
s.name = "张三";
s.age = 18;
s.gender = "男";
//1.2 取值
printf("s.name=%s \n" , s.name);
printf("s.age=%d \n" , s.age);
printf("s.gender=%s \n" , s.gender);
printf("\n--------------------\n");
//2. 结构体指针
struct student * s2 = &s;
//2.1 赋值:: 对结构体指针解引用 ,然后就可以继续使用 . 的方式来操作成员了。
(*s2).name = "李四";
(*s2).age = 28;
(*s2).gender ="男";
//2.2 取值
printf("ss.name=%s \n" , (*s2).name);
printf("ss.age=%d \n" , (*s2).age);
printf("ss.gender=%s \n" , (*s2).gender);
printf("\n--------------------\n");
//3. 如果有结构体指针,那么操作结构体内部的成员,可以使用 -> 操作符号 来操作成员
s2->name = "王五";
s2->age = 39;
s2->gender = "女";
printf("s2.name=%s \n" , s2->name);
printf("s2.age=%d \n" , s2->age);
printf("s2.gender=%s \n" , s2->gender);
return 0;
}
结构体作函数参数
#include <stdio.h>
//1. 定义结构体
struct student{
char name[20] ;
int age ;
char gender[10];
};
void change_value(struct student * s){ // struct student s = s;
//s.age = 20;
s->age = 20;
}
int main(){
//1. 定义变量
struct student s = {"张三" , 19 , "男"};
//2. 把结构体变量s 传给函数,在函数内部修改年龄
change_value(&s);
printf("s.name=%s \n" , s.name);
printf("s.age=%d \n" , s.age);
printf("s.gender=%s \n" , s.gender);
return 0;
}
共用体(联合体)
共用体的语法
- 共用体union是一个能在同一个存储空间存储不同类型数据的类型
- 共用体所占的内存长度等于其最长成员的长度,也有叫做共用体
- 同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用
- 共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖
- 共用体变量的地址和它的各成员的地址都是同一地址
#include <stdio.h>
//1. 定义联合体
union Test{
char a;
int b;
short c;
};
int main(){
//1. 创建联合体的变量
union Test t;
//2. 把大家的地址打一下:
printf("t的地址:%#x \n" , &t);
printf("t.a的地址:%#x \n" , &(t.a));
printf("t.b的地址:%#x \n" , &(t.b));
printf("t.c的地址:%#x \n" , &(t.c));
//3. 计算联合体的大小。:: 联合体的大小以 最大的成员所占用的空间为准。
printf("Test所占用的空间:%d \n" , sizeof(union Test));
//4. 给占用空间最大的那个成员赋值,然后一会观察其他的成员有没有值?
t.b = 0x11223344;
//打印每一个成员的值。
printf("t.a的值:%#x \n" , t.a);
printf("t.b的值:%#x \n" , t.b);
printf("t.c的值:%#x \n" , t.c);
printf("\n\n");
//5. 修改a的值
t.b = 0x112233ff;
printf("t.a的值:%#x \n" , t.a);
printf("t.b的值:%#x \n" , t.b);
printf("t.c的值:%#x \n" , t.c);
return 0;
}
共用体和结构体区别
- 存储方式:
-
- 结构体:结构体中的每个成员都占据独立的内存空间,成员之间按照定义的顺序依次存储
- 共用体:共用体中的所有成员共享同一块内存空间,不同成员可以存储在同一个地址上
- 内存占用:
-
- 结构体:结构体的内存占用是成员变量占用空间之和,每个成员变量都有自己的内存地址
- 共用体:共用体的内存占用是最大成员变量所占用的空间大小,不同成员变量共享同一块内存地址
枚举
- 枚举:将变量的值一一列举出来,变量的值只限于列举出来的值的范围内
#include <stdio.h>
// 定义枚举
enum Light{
RED = 100 , GREEN =200 , YELLOW = 300
};
/*
// 枚举值默认对应的数值从0 开始
enum Light{
RED , GREEN , YELLOW
};
*/
int main(){
//定义枚举的变量
// 声明一个枚举的变量,但是没有初始化
enum Light light ;
// 声明一个枚举变量,同时初始化
enum Light light2 = YELLOW ;
printf("light2 = %d \n" , light2);
return 0;
}
typedef
- typedef为C语言的关键字,作用是为一种数据类型(基本类型或自定义数据类型)定义一个新名字,不能创建新类型。