C语言数组和指针
1、一维数组
1.1 一维数组的创建
(1)概述
数组可以实现多个相同类型数据的批量存储和处理,通过统一名称结合数字索引(下标)来区分各个数据,可以实现数据的高效访问。
(2)一维数组创建形式
type_t arr_name [const_m];
//type_t 指数组的元素类型
//arr_name 数组的名字
//const_m 是一个常量表达式,用来指定数组的大小
也即是:
类型说明符 数组名[长度]
例如:
//代码
int arr1[3];
char arr2[3];
float arr3[3];
1.2 一维数组的初始化
数组的初始化是指数组在定义时为数组元素赋上初始值,方法是用‘{}’将逗号分隔的值包含起来,具体包含如下形式。
(1)将全部数组进行初始化
int arr1[3]={1,2,3};
char arr2[3]={'a','b','c'};
(2)将部分数组进行初始化
int arr3[5]={1,2,3};//从3后面的数字都补充为0,即是{1,2,3,0,0}。
(3)不指定数组的大小
int arr1[]={1,2,3};//该数组会自动识别数组个数为3
char arr2[]={'a','b','c'};
(4)注意
利用宏进行定义:
/********正确的********/
#define n 5
int arr1[n];
/********错误的********/
int n=5;
int arr1[n];
数组中[]必须是常量,或者利用宏定义
数组越界:
int arr[2]={1,2,3,4,5,6};//出现报错,编译不过
字符和字符串数组:
char arr1[] = { 'w','q','r' };
char arr2[] = "wqr";
//其中arr1为'w','q','r',有3个元素。
//其中arr2为 'w','q','r','\0',有4个元素,存在一个转义符。
(5)求取整个数组长度:
sizeof(arr) / sizeof(arr[0]);
/**************************************
数组占用内存空间大小:sizeof(arr)
数组单个元素占用内存空间大小:sizeof(arr[0])
数组长度:sizeof(arr) / sizeof(arr[0])
*****************************************/
1.3 一维数组的应用
下面是基本的应用:
int main()
{
int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int i;
for (i = 0; i < sizeof(a) / sizeof(a[0]); i++)
{
printf("%d\n",a[i]);
}
return 0;
}
//打印结果0,1,2,3,4,5,6,7,8,9
//注意数组的第一个索引是a[0];
1.4 一维数组的存储
通过打印数组的地址进行观察,以上面的代码为例。
int main()
{
int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int i;
for (i = 0; i < sizeof(a) / sizeof(a[0]); i++)
{
printf("%p\n",&a[i]);
}
return 0;
}
//打印出的地址为
000000766F4FF9A8
000000766F4FF9AC
000000766F4FF9B0
000000766F4FF9B4
000000766F4FF9B8
000000766F4FF9BC
000000766F4FF9C0
000000766F4FF9C4
000000766F4FF9C8
000000766F4FF9CC
//相邻数组之间的地址间隔为4个字节大小,地址由低到高。
2、二维数组
二维数组与一维数组很相似,二维数组元素需要两个下标,其数组类型与一维数组一样。
2.1二维数组的创建
(1)二维数组创建
//前面是行,后面是列
int a[2][3];//2行3列的数组
char b[3][3];
double c[2][4];
(2)二维数组初始化
//数组初始化
int a[2][3] = {{1,2},{3,4},{5,6}};
int arr[3][4] = {{1,2,3},{4,5,5},{7,8,9},{2,5,8}};
int arr[2][4] = {{2,3,8,9},{4,5,7,2}};
注意:花括号中的一个花括号代表一个一维数组的初始化,也即是一行,当里面无花括号分组时,按照顺序从第一个开始逐个进行初始化,余下的未赋值的元素用0初始化,在进行初始化时候尽量用{}进行括上这样可以增加代码的可读性。
2.2 二维数组的使用
#include <stdio.h>
#include <string.h>
int main()
{
int a[2][3] = { {1,2,3},{4,5,6} };
int i;
int j;
for (i = 0; i < 2; i++)
{
for (j = 0; j < 3; j++)
{
printf("%-2d",a[i][j]);
}
printf("\n");
}
return 0;
}
//输出结果
1 2 3
4 5 6
在通常的使用中常使用双层嵌套的方式进行访问。
2.3 二维数组的存储
#include <stdio.h>
#include <string.h>
int main()
{
int a[2][3] = { {1,2,3},{4,5,6} };
int i;
int j;
for (i = 0; i < 2; i++)
{
for (j = 0; j < 3; j++)
{
printf("a[%d][%d]:%p\n",i,j,&a[i][j]);
}
printf("\n");
}
return 0;
}
//输出结果
a[0][0]:0000001A1C6FF818
a[0][1]:0000001A1C6FF81C
a[0][2]:0000001A1C6FF820
a[1][0]:0000001A1C6FF824
a[1][1]:0000001A1C6FF828
a[1][2]:0000001A1C6FF82C
可以看出二维数组的存储是连续的每个相邻元素之间的大小是sizeof(int)
。
3、指针
指针是C语言的重点,当然也是大家认为的难点,使用指针可以使我们的代码更加的灵巧、简介,同时也可以提高程序的编译效率和执行速度,下面我们一起看看有关指针的一些知识。
3.1 指针概念
计算机内存储器以字节为基本存储单元连续编排,形成一个巨大的线性存储空间。如果把每个字节比作宾馆的一个房间,连续的存储空间就好比排列的大量房间。管理房间最简单的方法就是按照顺序给每个房间编号,通过登记房间号及其入住信息就能随时掌握房间的分配状况。
程序中通过引用变量名,可以直接访问该变量分配的存储单元。如果获得了该变量的地址,通过这个指针值同样可以访问该存储单元。用来保存对象地址的变量称为指针变量,它具有变量名、变量的地址和值三个要素。
(1)定义指针
类型说明符 *变量名
“*”是类型说明符号,不是运算符号,它定义的变量为指针类型的变量。星号左边为“类型说明符”是指针变量所指向的类型。例如:
int *ip //指针变量ip的类型是int*,其类型是int,保存int型变量的地址。
float *fp //指针变量fp的类型是float*,其类型是float,保存float型变量的地址。
ip和fp的指针类型不同,ip保存的值只会当做int型变量地址看待,按照int型数据访问。fp保存的值只会当做float型变量地址看待,按照float型数据访问。
且记:指针变量中存放的不是普通的数值,而是一个对象的地址,是代表内存单元地址的无符号整数。
3.2 指针变量赋值
指针变量未初始化之前值是不确定的,此时它没有确定的指向,不能进行操作,否则会带来系统风险。C语言中提供了地址运算符"&",可以在程序运行中获取对象的地址。例如:
指针变量=&变量名;
代码如下:
int main()
{
int b=100;//声明一个变量并赋值
int *pa;//声明一个指针变量
pa = &b;//取变量b的地址
printf("%d\n",b);//打印变量b的值
printf("%p\n",&b);//打印变量b的地址
printf("%d\n",*pa);//打印指针指向的内容
printf("%p\n", pa);//打印指针指向的地址
return 0;
}
//运行结果如下
100
0000000FDC8FFCA4
100
0000000FDC8FFCA4
3.3 指针的运算和部分知识
下面讲解一下有关指针的部分知识。
(1)二级指针
下面代码如下:
int main()
{
int a = 10, * p, ** q;
p = &a;
q = &p;
printf("%p\n",p);//p为a的地址(&a)
printf("%d\n",*p);//*p为指针所指向的内容(a)
printf("%p\n",q);//q为p的地址(&P)
printf("%p\n", *q);//*q为指针所指向的内容(&a)
printf("%d\n",**q);//**q为指针的指针所指向的内容(a)
return 0;
}
//运算结果如下
000000CA1FFDF954
10
000000CA1FFDF978
000000CA1FFDF954
10
上述只对二级指针进行了讲述,其中还有多级(>2)指针就不逐一进行讲述了。
(2)"*“和”&"的结合
代码如下:
int main()
{
int a=10;
int* pa;
pa = &a;
printf("%p\n", &a);//a的地址
printf("%d\n",*&a);//a的值
printf("%p\n", &*pa);//pa指向的变量的地址(&a)
return 0;
}
//运算结果如下
0000001FC011FC94
10
0000001FC011FC94
指针中注意"*“和”&"的结合,以及符号的优先级。
3.4 野指针
(1)指针没有进行初始化
代码如下:
int main()
{
int* pa;//定义了一个指针,但是没有指向的地址,可能会随意指向一个地址
pa = 20;//将20随意找个空间填充进去,可能会破坏先有的程序运行
return 0;
}
(2)指针的越界访问
代码如下:
int main()
{
int a[10] = {1,2,3,4,5,6,7,8,9,10};
int* pa;
pa = a;
printf("%d\n",*pa);//数组第1个元素
printf("%d\n", *(pa+2));//数组第3个元素
printf("%d\n", *(pa + 9));//数组第10个元素
printf("%d\n", *(pa + 10));//越界访问
}
//运行结果如下
1
3
10
-858993460
(3)指针指向的空间释放
代码如下:
int* test()
{
int b = 5;
return &b;
}
int main()
{
int* p = test();
*p = 10;
return 0;
}
当获取常量b的地址赋值给p后,因为b是在局部变量中,当出了局部函数后b的空间地址被释放,导致p成为了野指针。
如何避免野指针:
- 指针初始化
- 小心指针越界
- 指针指向空间释放,及时置NULL(动态内存申请时容易出现)
- 避免返回局部变量的地址——也即是避免返回栈空间上的地址
- 指针使用之前检查有效性
4、数组和指针的结合
接下来我们看一下数组指针和指针数组,数组指针和指针数组是大家比较容易混淆的知识点,通过本节的讲解,希望对大家有所帮助。
4.1 数组指针
整型指针是一个指向整型变量的指针,字符指针是一个指向字符变量的指针,所以数组指针是指向数组的指针,形如例子:
char gu='m';
char *pc=&ch;
int num=4;
int* pi=#
int ary[11];
int (*pa)[11]=&ary;
接下来大家要注意一下几个运算符号的顺序:()>[]>,则先与pa结合形成(*pa),形成数组指针int(*pa)[11],也即是指针变量为pa,int修饰的是数组的内容,即数组的每个元素,也就是说,pa是一个指针,它指向一个包含11个int类型数据的数组。
#include <stdio.h>
#include <string.h>
int main()
{
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
int(*pa)[10] = a;//这里也可以写成&a[0]
int i;
for (i = 0; i < (sizeof(a) / sizeof(int)); i++)
{
printf("(*pa)[%d]=%d\n",i,(*pa)[i]);
}
return 0;
}
//运行结果为:
(*pa)[0]=1
(*pa)[1]=2
(*pa)[2]=3
(*pa)[3]=4
(*pa)[4]=5
(*pa)[5]=6
(*pa)[6]=7
(*pa)[7]=8
(*pa)[8]=9
(*pa)[9]=10
接下来看下面一个例子:
#include <stdio.h>
#include <string.h>
int main()
{
int a[5] = {1,2,3,4,5};
int(*pa)[5];
pa = a;
printf("%p\n",a);//输出数组名,一般用数组首元素地址标识一个数组,数组的首地址
printf("%p\n",&a);//数组的首地址
printf("%p\n",&a[0]);//数组的首地址
printf("%p\n",pa);//数组a的地址
printf("%p\n",*pa);//*pa表示数组a本身,也即是数组a的首地址
printf("%d\n",a[0]);//数组的首元素值
printf("%d\n",**pa);//数组的首元素值
return 0;
}
//输出的结果为:
0000004251F3FA88
0000004251F3FA88
0000004251F3FA88
0000004251F3FA88
0000004251F3FA88
1
1
4.2 指针数组
指针数组,字面上讲装着指针的数组,也就是说指针数组是存放指针的数组,例如:
int *pa[10];
char *pa[10];
接下来举个简单的例子:
int main()
{
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int* arr[4] = {&a,&b,&c,&d};//讲定义的a,b,c,d四个类型的地址存放到指针数组里面。
return 0;
}
再看下面一个例子:
int main()
{
int a = 2;
int b = 3;
int* pa[2];
pa[0] = &a;
pa[1] = &b;
printf("%p\n",&a);//a的地址
printf("%p\n", pa[0]);//a的地址
printf("%p\n", &b);//b的地址
printf("%p\n", pa[1]);//b的地址
printf("%d\n", a);//a的值
printf("%d\n", *pa[0]);//a的值
printf("%d\n", b);//b的值
printf("%d\n", *pa[1]);//b的值
return 0;
}
//运行结果
00000056A792F4E4
00000056A792F4E4
00000056A792F504
00000056A792F504
2
2
3
3
5、总结
综上所述,数组和指针是比较重要的知识点,上述的总结还有许多没有到位的地方,还望大家指正,共同进步!