数组与指针

数组与指针

本章介绍以下内容

  • 关键字——static
  • 运算符——& *
  • 如何创建并初始化数组
  • 指针、指针和数组的关系
  • 编写处理数组的函数
  • 二维数组

数组

定义:数组由数据类型相同的一系列元素组成,普通变量可以使用的类型,数组元素都可以使用。注意: 索引是从0开始

初始化数组

  • 直接在定义时初始化数组,如果不初始化,会直接使用内存中的垃圾数据

  • 当初始化列表中的项数与数组中的大小不一致时
    1.项数大于数组大小,报错
    2.项数小于数组大小,未初始化自动赋值为0.
    **注意:**部分初始化数组,剩余元素就会被初始化为0.且字符串常量后面才会自动加 \0。

  • 在使用时我们可以省略方括号中的数字,让编译器根据列表中的项数来确定数组的大小

  • size of也可用来查看数组占了多大的字节 元素个数=size of(数组名)/数组类型

指定初始化器

  • 定义:利用该特性可以初始化指定的数组元素。

    int days[5]={1,2,[4]=31,32,33} ;

  • 初始化器的两个重要特性

    1. 如果指定初始化器重有很多的值,如上诉代码,则后面的值将用于初始化指定元素后面的元素
    2. 如果再次初始化指定的元素,那么最后的初始化将会取代之前的初始化。

给数组元素赋值

  • 借助数组下标或索引给元素赋值 **注意:**C不允许把数组元素作为一个单元赋给另一个数组,除初始化以外也不允许使用花括号列表的形式赋值。

数组边界

**注意:**编译器不会检查数组下标是否使用得当
**注意:**当定义一个20范围的数组,结果只能使用19个,因为最后一个存储空间存储\0

指定数组的大小

数组的大小只能为整型常量表达式

多维数组

float rain[5][12];//内含5个元素的数组,每个数组元素内含12个float类型的元素

初始化二维数组

直接用花括号进行初始,可以省略内部的花括号,只保留最外面的一对花括号

指针与数组

数组其实就是变相地使用指针,数组名是数组首元素的地址
filzny==&flizny[0];

dates+2==&dates[2];//相同的地址
*(dates+2)==dates[2];//相同的值

**注意:**指针的值加一,指针的值递增它所指向类型的大小,也就是下一个地址

实例,利用指针访问数组中的值

#include<stdio.h>
#define SIZE 4
int main()
{
	int dates[SIZE];
	int* pi;
	double bills[SIZE];
	double* pt;
	pi = dates;//把数组地址赋给指针
	pt = bills;
	for (int i = 0;i < SIZE;i++)
	{
		scanf_s("%d", &dates[i]);
	}
	for (int i = 0;i < SIZE;i++)
	{
		printf("数值:%d=%d\n", dates[i], *pi);
		printf("地址:%p=%p\n", &dates[i], pi);
		pi++;
	}
}

函数、数组和指针

编写处理数组的函数:对C语言而言,不能把整个数组作为参数传递给函数,但是可以传递数组的地址。

**注意:**关于函数的形参,要注意一点,只有在函数原型或函数定义头重,才可以用a[]代替*a,int *ar形式和int ar[]形式都表示ar是一个指向int的指针,但是int ar[]只能用于声明形式参数。

下面四种原型是等价的 **注意:**在函数定义中不能省略参数名

int sum(int *ar,int n);
int sum(int *,int );
int sum(int ar[],int n);
int sum(int [],int );

使用指针参数

传递两个指针表示数组的开始与结尾

#include<stdio.h>
#define SIZE 4
int sump(int* start, int* end);
int main()
{
	
	int m[] = { 1,2,3,4 };
	int answer;
	answer = sump(m, m + SIZE);//因为下标从零开始,所以这里的end指的是数组
	//最后一个元素的后一项
	printf("the total number of marbles is %ld.\n", answer);
	printf("%p\n", &m[3]);
	return 0;
}

int sump(int* start, int* end)
{
	int total = 0;
	while (start <end)
	{
		printf("%d,%d,%p\n", *start,* end,end);
		total += *start;
		start++;
	}
	return total;
}

优先级问题

  • 一元运算符*和++的优先级相同,但结合律是从右往左

    total+=*start++;//指针start先递增后指向,意味着先把指针指向位置的值加到total上,然后再递增指针
    total+=*++start;//先递增指针,再使用指针指向位置上的值。
    total+=(*start)++;//先使用start指向的值,然后再递增该值,注意不是指针,这里的指针指在同一个地方
    

指针表示法和数组表示法

ar[i]==*(ar+i);//两种表达式是相等的

**注意:**只有当 ar 是指针变量时,才使用ar++这样的表达式。

指针操作

  • 赋值:将地址赋值给指针

  • 解引用:* 运算符给出指针指向地址上存储的值

  • 取址:&运算符取指针本身的地址

  • 指针和整数相加:整数会和指针所指向类型的大小相乘,然后把结果和初始地址相加,如果相加结果超出初始指针指向数组范围,计算结果未定义,除非正好超过数组末尾第一个位置,C保证该针指有效。

    ptrl+4==&urn[4];

  • 递增指针

  • 指针减去一个整数:和上面加类似,只不过变为减

    ptr3-2==&urn[2];

  • 递减指针

  • 指针求差:求差的两个指针你分别指向同一个数组的不同元素,通过计算求出两个元素之间的距离,数值的单位为指针的数据类型。

    ptr2-ptr1=2;//两个指针所指向的两个元素之间相隔两个int

  • 比较:前提是两个指针指向相同的类型

    **注意:**这里的减法有两种:一个指针减去另一个指针得到一个整数。一个指针减去一个整数得到另一个指针。

  • 解引用未初始化的指针:指针初始化的目的是为了让指针知道指向的哪个地址.

    int *ptr = &a;//第一种初始化方法,直接在声明时初始化
    int *ptr;
     ptr= &a;//第二种初始化方法,不在声明指针的时候初始化,而是把地址直接赋值给指针变量
    

    **注意:**不可以在指针不清楚指向地址的情况下给指针赋值,也就是不可以解引用未初始化指针。声明指针时系统只给指针本身创建内存。

    例题

    #include<stdio.h>
    
    	int main()
    	{
    		int urn[5] = { 100,200,300,400,500 };
    		int* ptr1, * ptr2, * ptr3;
    		ptr1 = urn;
    		ptr2 = &urn[1];
    		//指针加法
    		ptr3 = ptr1 + 4;
    		printf("ptr3=%d\n", *ptr3);
    		//递增指针
    		ptr1++;
    		printf("ptr1=%d\n", *ptr1);
    		//递减指针
    		ptr2--;
    		printf("ptr2=%d\n", *ptr2);
    		//一个指针减去另一个指针
    		printf("ptr1=%d,ptr2=%d,%d", *ptr1, *ptr2, ptr1 - ptr2);
    		return 0;
    	}
    

保护数组中的数据

创建一个函数,对数组进行一系列的操作

//使数组中的每个元素增加val
#include<stdio.h>
void add_to(int urn[], int n, double val)
{
	int i;
	for (i = 0;i < n;i++)
		urn[i] += val;
	}
	int main()
	{
		int urn[5] = { 100,200,300,400,500 };
		add_to(urn, 5, 100);
		for (int i = 0;i < 5;i++)
		{
			printf("%d\n", urn[i]);
		}
		
	}

对形式参数使用const

int sum(const int ar[],int n);
int sum(const int ar[],int n)
{
    int i;
    int total;
    for(i=0;i<n;i++)
        total+=ar[i];
    return 0;
}

当不需要改变数组中元素的值时,为了避免错误操作使值发生改变,使用const,保护数组中的数据不被更改

另外可以创建 const 数组、const指针和指向const的指针

  • 指向 const 的指针不能用于改变值,且通常用于函数形参中,表示该函数不会使用指针改变数据

  • 可以声明一个不能指向别处的指针

    double *const pc=rates;

  • 创建既不能更改指针的值也不能更改指针的位置

    const double *const pc=rates;

复合字面量

定义:复合字面量类似数组初始化列表,前面是用括号括起来的类型名

例如

(int [2]{10,20});//复合字面量

**注意:**因为符合字面量是匿名的,所以不能先创建然后再使用,必须在创建的同时使用它

用法

  • 使用指针记录地址

    int *pt1;
    pt1=(int []{10,20});//和一般数组一样,可以省略中括号中的元素数
    
  • 作为实际参数传递给带有匹配形式参数的函数

    int sum(const int ar[],int n);
    ...
    int total;
    total=sum((int []{10,20}),2)
    

    **注意:**复合字面量是只提供需要的值的一种手段,复合字面量具有块作用域,这意味着一旦离开定义符合字面量的块,程序将无法保证该字面量是否存在。

上一篇:小白带你学废5G入门知识


下一篇:不用AI的彩妆店不是好专柜?