初学c课程笔记整理7-->>二维数组、字符串、指针
是什么?有什么用?用在什么地方?(理解)
养成良好的编程习惯;
培养编程思想;
写代码之前应该先分析需求,分析完需求再开始写代码;(写注释)
1.二维数组
1.1 二维数组的基本概念:
所谓多维数组就是一个一维数组的每个元素又被声明为一 维数组,从而构成二维数组. 可以说二维数组是特殊的一维数组。
示例
int a[2][3]
可以看作由一维数组a[0]和一维数组a[1]组成,这两个一维数组都包含了3个int类型的元素
1.2 二维数组的定义:
数组中的每一个元素又是一个数组, 那么这个数组就称之为二维数组
元素类型 数组名称[一维数组的个数][每个一维数组的元素个数];
元素类型 数组名称[行数][列数];
元素类型: 说明二维数组中每个一维数组存储什么类型的数据
一维数组的个数: 说明二维数组有多少个元素
每个一维数组的元素个数 : 说明二维数组中每一个一维数组的元素个数
*/
int scoress[3][5] =
{
元素类型 数组名称[行数][列数];
元素类型: 说明二维数组中每个一维数组存储什么类型的数据
一维数组的个数: 说明二维数组有多少个元素
每个一维数组的元素个数 : 说明二维数组中每一个一维数组的元素个数
*/
int scoress[3][5] =
{
{1, 3, 5, 7, 9}, // 0
{2, 4, 6, 8, 10},// 1
{1, 2, 3, 4 , 5} // 2
{1, 2, 3, 4 , 5} // 2
}; // 每一个一维数组都是二维数组的一个元素
如何遍历二维数组
思路: 1.取出二维数组的每一个元素(取出二维数组中的一维数组)
2.遍历一维数组
1.3 二维数组的存储
存储方式:
1)计算机会给二维数组分配一块连续的存储空间
2)数组名代表数组的首地址,从首地址位置,依次存入第1行、第2行、.....
3)每一行存储方式,从行首地址还是,依次存储行的第1个元素、第2个元素、第3个元素......
4)每个元素占用相同的字节数(取决于数组类型)
5)并且数组中元素之间的地址是连续
二维数组的存放顺序是按行存放的,先存放第一行的元素,再存放第2行的元素
1.4 二维数组初始化及注意点:
1.3.1二维数组的多种初始化方式
1.定义的同时初始化
int names[2][3] =
{
{'l', 'n', 'j'},
{'x', 'm', 'g'}
};
{
{'l', 'n', 'j'},
{'x', 'm', 'g'}
};
2.先定义再初始化
int names2[2][3];
names2[0][0] = 'l';
names2[0][1] = 'n';
names2[0][2] = 'j';
names2[1][0] = 'x';
names2[1][1] = 'm';
names2[1][2] = 'g';
names2[0][0] = 'l';
names2[0][1] = 'n';
names2[0][2] = 'j';
names2[1][0] = 'x';
names2[1][1] = 'm';
names2[1][2] = 'g';
3.完全初始化
int names3[2][3] =
{
{'l', 'n', 'j'},
{'x', 'm', 'g'}
};
{
{'l', 'n', 'j'},
{'x', 'm', 'g'}
};
4.不完全初始化
int names4[2][3] =
{
{'l', 'n'},
};
{
{'l', 'n'},
};
5.省略元素个数的两种写法
明确的告诉二维数组, 我们有2个一维数组
char names5[2][3] =
{
'l', 'n', 'j',
'x', 'm', 'g'
};
char names15[2][3] =
{
'l', 'n', 'j',
'x', 'm'
};
{
'l', 'n', 'j',
'x', 'm', 'g'
};
char names15[2][3] =
{
'l', 'n', 'j',
'x', 'm'
};
没有告诉二维数组我们有几个一维数组
如果在"定义的同时"进行初始化, 那么一位数组的个数可以省略
系统会自动根据每一个一维数组能够存放多少个元素, 自动根据初始化的值推断出二维数组中一共有多少个元素(多少个一维数组)
char names6[][3] =
{
'l', 'n', 'j',
'x', 'm', 'g',
'n', 'b'
};
char names6[][3] =
{
'l', 'n', 'j',
'x', 'm', 'g',
'n', 'b'
};
6.错误写法
⚠️ 注意点: 每个一维数组的元素个数不能省略
int names7[2][] =
{
{'l', 'n', 'j'},
{'x', 'm', 'g'}
};
{
{'l', 'n', 'j'},
{'x', 'm', 'g'}
};
搞不清楚应该分配多大的存储空间, 以及搞不清楚应该把哪些数据赋值给第一个数组, 以及哪些数据赋值给第二个数组
int names7[2][] =
{
'l', 'n', 'j',
{
'l', 'n', 'j',
'x', 'm', 'g'
};//错误写法
1.5 二维数组与函数
1.二维数组的元素作为函数参数
二维数组的元素就相当于变量,作为函数参数与变量相同
2.二维数组中的一维数组作为函数
二维数组的一维数组实际上就是一个一维数组,作为函数参数与一维数组相同
3.二维数组作为函数参数
**二维数组作为函数参数是地址传递**
二维数组作为函数形参,参数中一维数组的元素个数不可以省略
void test(char cs[2][]) // 错误写法
{
printf("我被执行了\n");
}
void test(char cs[2][3]) // 正确写法
{
printf("我被执行了\n");
}
void test(char cs[][3]) // 正确写法
{
printf("我被执行了\n");
二维数组作为函数参数,在被调函数中不能获得其有多少行,需要通过参数传入。
```
void test(char cs[2][3])
{
int row = sizeof(cs);
printf("row = %zu\n", row);
}
输出结果:
row = 8
```
- 二维数组作为函数参数,在被调函数中可以计算出二维数组有多少列
```
void test(char cs[2][3])
{
size_t col = sizeof(cs[0]);
printf("col = %zd\n", col);
}
输出结果:
col = 3
```
以后只要看到函数的参数是一个数组, 那么就是地址传递
在函数中修改形参的值会影响到参数
2.字符串
1.字符串概念
字符串是位于双引号中的字符序列
在内存中以“\0”结束,所占字节比实际多一个
2.字符串的初始化
- 在C语言中没有专门的字符串变量,通常用一个字符数组来存放一个字符串。前面介绍字符串常量时,已说明字符串总是以‘\0’作为串的结束符。因此当把一个字符串存入一个数组时,也把结束符‘\0’存入数组,并以此作为该字符串是否结束的标志。有了‘\0’标志后,就不必再用字符数组 的长度来判断字符串的长度了。
- 初始化
```
char name[9] = "lnj"; //在内存中以“\0”结束, \0ASCII码值是0
char name1[9] = {'l','n','j','\0'};
char name2[9] = {'l','n','j',0};
char name3[9] = {'l','n','j'};//静态数组如果没有赋值默认就是0,0就相当于\0
```
- 错误的初始化方式
```
//不正确地写法,结尾没有\0 ,只是普通的字符数组
char name4[] = {'l','n','j'};
// \0的作用:字符串结束的标志
char name[] = "c\0ool";
printf("name = %s\n",name);
输出结果: c
3.字符串的输出、输入
使用的格式字符串为“%s”,表示输入、输出的是一个字符串
scant(“%s”,ch);//输入
printf(“%s”,ch);//输出
%s的原理, 从传入的"地址"开始逐个取出, 直到遇到"\0"位置
4.字符串的注意点
字符串注意点, 字符串的初始化
char str1[] = "lnj";
char str2[] = {'l', 'n', 'j', '\0'};
// 部分初始化, 部分初始化中, 没有被初始化的元素默认是0
// \0对应的ASCII 0
char str3[10] = {'l', 'n', 'j'}; // l n j 0000000
char str4[3] = {'l', 'n', 'j'}; // 该方式不是字符串, 而是字符数组, 因为没有\0
char str5[] = {'l', 'n', 'j'}; // 错误写法
// \0对应的ASCII 0
char str3[10] = {'l', 'n', 'j'}; // l n j 0000000
char str4[3] = {'l', 'n', 'j'}; // 该方式不是字符串, 而是字符数组, 因为没有\0
char str5[] = {'l', 'n', 'j'}; // 错误写法
需要明白的一点就是字符串以\0结尾, 没有\0就不是字符串
只要是用双引号括起来的都是字符串
字符串的本质就是数组
3.指针(重点)只能保存地址
3.1内存地址
地址与内存单元中的数据是两个完全不同的概念
地址如同房间编号
内存单元如同房间
内存单元中存放着数据
变量地址:
系统分配给"变量"的"内存单元"的起始地址
3.2 什么是指针
在计算机中所有数据都存储在内存单元中,而每个内存单元都有一个对应的地址, 只要通过这个地址就能找到对应单元中存储的数据. 由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”
内存单元的指针(地址)和内存单元的内容是两个不同的概念。
3.3 什么是指针变量
在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。因此,一个指针变量的值就是
某个内存单元的地址或称为某内存单元的指针。
+ 严格意义上说,指针是一个地址,是一个常量
+ 针变量是存放一个地址,是一个变量。
示例
3.4定义指针变量的格式
指针变量的定义包括两个内容:
指针类型说明,即定义变量为一个指针变量;
指针变量名;
> 其中,*表示这是一个指针变量,变量名即为定义的指针变量名,类型说明符表示本指针变量所指 向的变量的数据类型
char *p; // 一个用于指向字符形变量的指针
int *q; // 一个用于指向整形变量的指针
long *t; // 一个用于指向长整形变量的指针
float *r; // 一个用于指向单精度浮点形变量的指针
double *s; // 一个用于指向双精度浮点形变量的指针
⚠️注意
1)在定义指针时,“*”号表示定义的变量是指针变量,变量的值只能存放地址。
2)一个类型的指针只能指向同类型的变量,不能指向其他类型的变量。
3)指针也可以被声明为全局、静态局部和局部的。
3.5指针变量初始化及注意点
1.指针变量的初始化方法
指针变量初始化的方法有两种:定义的同时进行初始化和先定义后初始化
定义的同时进行初始化
int a = 5;
int *p = &a;
先定义后初始化
int a = 5;
int *p;
p=&a;
把指针初始化为NUL
int *p=NULL;
int *q=0;
不合法的初始化:
指针变量不能被赋值一个整数值(因为我们不知道这个整形常量是内存哪块地址
int *p;
p = 250; // 错误写法
int *p;
*p=&a; //错误写法
int *p;
*p = &a;//正确写法
⚠️注意:
多个指针变量可以指向同一个地址
指针的指向是可以改变
int a = 5;
int *p = &a;
int b = 10;
p = &b; // 修改指针指
指针没有初始化里面是一个垃圾值,这时候我们这是一个野指针
+ 野指针可能会导致程序崩溃
+ 野指针访问你不该访问数据
+ 所以指针必须初始化才可以访问其所指向存储区域
访问指针所指向的存储空间
int a = 5;
int *p = &a;
printf("a = %d", *p); // 访问指针变量
4.(多)二级指针
二级指针介绍
如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。也称为“二级指针”
char c = 'a';
char *cp;
cp = &c;
char **cp2;
cp2 = &cp;
printf("c = %c", **cp2);
多级指针介绍
int ***m1; 取值***m1
int *****m2 取值*****m2
未来有你才精彩,你有未来不是梦!