C语言数组和指针

本文详细介绍了C语言中一维数组、二维数组的创建、初始化、应用以及存储方式,包括指针的概念、赋值、运算和注意事项,以及数组和指针的结合,如数组指针和指针数组。
摘要由CSDN通过智能技术生成

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成为了野指针。
如何避免野指针:

  1. 指针初始化
  2. 小心指针越界
  3. 指针指向空间释放,及时置NULL(动态内存申请时容易出现)
  4. 避免返回局部变量的地址——也即是避免返回栈空间上的地址
  5. 指针使用之前检查有效性

4、数组和指针的结合

接下来我们看一下数组指针和指针数组,数组指针和指针数组是大家比较容易混淆的知识点,通过本节的讲解,希望对大家有所帮助。

4.1 数组指针

整型指针是一个指向整型变量的指针,字符指针是一个指向字符变量的指针,所以数组指针是指向数组的指针,形如例子:

char gu='m';
char *pc=&ch;

int num=4;
int* pi=&num;

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、总结

综上所述,数组和指针是比较重要的知识点,上述的总结还有许多没有到位的地方,还望大家指正,共同进步!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值