数组和指针(一)

本文详细介绍了C语言中数组的初始化,包括当初始化列表长度小于或大于数组长度时的情况。还讨论了指定初始化器,以及数组与指针的关系,指出数组名是首元素的地址。此外,解释了指针的使用,包括指针加减运算、指针比较以及指针作为函数参数时的注意事项。最后,提到了数组在函数参数传递中的行为,以及sizeof运算符在不同场景下的应用。
摘要由CSDN通过智能技术生成

数组

初始化

 int some_data[4]={14,10};
其中some_data[2]和some_data[3]的值为0

但初始化列表中的值少于数组元素个数时,编译器会把剩余的元素都初始化为0。
如果初始化列表中的值多于数组元素个数时,编译器会无情地将其视为错误

 const int days[]={31,28,31};
其实,可以省略方括号中的数字,让编译器自动匹配数组大小和初始化列表中的项数。

初始化指定器

int arr[6]={[5]=212}; // 把arr[5]初始化为212

对于一般的初始化,在初始化一个元素后,未初始化的元素都会被设置为0。

#define MONTHS 12;
int days[MONTHS]={31,28,[4]=31,30,31,[1]=29};
int i;
for(i=0;i<MONTHS;i++)
    printf("%2d %d\n" ,i+1,day[i];

输出为:
1  31
2  29
3  0
4  0
5  31
6  30
7  31
8  0
9  0
10 0
11 0
12 0

第一,如该例中:[4]=31,30,31,那么后面这些值将被用于后面的元素,也就是说days[5]和days[6]将被初始化为30和31。
第二,如果再次初始化指定的元素,那么最后的初始化将会取代之前的初始化,如days[1]。

第1条:c不允许把数组作为一个单元赋给另一个数组;
第2条:除初始化以外也不允许使用花括号列表的形式赋值
下面演示一些错误的赋值形式:

#define SIZE 5
int main(void)
{
   int oxen[SIZE]={5,3,2,8};  //这样初始化没问题
   int yaks[SIZE];            
   yaks=oxen;                 //不允许,违反了第1条
   yaks[SIZE]=oxen[SIZE];     //数组下标越界,解释在下面
   yaks[SIZE]={5,3,2,8};      //不起作用,违反第2条

oxen数组的最后一个元素是oxen[SIZE-1],所以oxen[SIZE]和yaks[SIZE]都超出了两个数组的末尾。
此外,数组元素下标是从0开始。最好在声明数组时使用符号常量来表示数组的大小。

二维数组

二维数组在计算机内部是按顺序存储的

在这里插入图片描述
rain是一个含有5个元素,每个元素又含有12个float类型元素的数组。

初始化二维数组(可以使用两个嵌套的for循环)

在这里插入图片描述

  1. 用逗号分隔5个数值列表,每个数值列表都用花括号括起来。
    第1个列表的数据用于初始化第一行,第2个列表用于初始化第2行,以此类推。
  2. 此外,前面讨论的数据个数和数组大小不匹配问题同样适用于这里的每一行。
    也就是说,如果第1列表中只有10个数,则只会初始化第1行前10个元素,而最后两个元素被默认初始化为0。 如果列表中的数值个数超出了数组每行的元素个数,则会出错,但不会影响其他行。
  3. 初始化也可以省略内部的花括号,但会按照先后顺序逐行初始化。

下面演示了初始化二维数组的两种方法:
在这里插入图片描述

其他多维数组

int box[10][20][30];

可以把一维数组想象成一行数据;二维数组想象成数据表;把三维数组想象成一叠数据表。(可以把box想象成由10个二维数组(每个二维数组都是20行30列)堆叠起来的)。

指针和数组

  1. 数组变量是特殊的指针
    例子:数组名是数组首元素的地址。flizny是一个数组
flizny==&flizny[0];   //数组名是该数组首元素的地址
int a[10]; int*p=a;   //无需用&取地址`

2.*运算符(间接运算符或解引号运算符)用于访问那个地址上的变量
可以做右值也可以做左值。

int k=*p;
*p=k+1;
  1. 指针是const
    表示一旦得到了某个变量的地址,不能再指向其他变量
int*const q=&i; //q是const
*q=26//OK
 q++;         //ERROR

所指是const
表示不能通过这个指针去修改那个变量(并不能使得那个变量成为const)

const int*p=&;
*p=26;         //ERROR!(*p)是const
i=26;          //OK
p=&j;          //OK

指针运算

  1. 指针加上一个整数或递增指针,指针的值以所指向对象的大小为单位(字节为度量)改变。也就是说,如果pd指向一个数组的8字节double类型值,那么pd加1意味着其值加8,以便它指向该数组的下一个元素。
short *pti;
double*ptf;
printf("%d   %d\n",pti,ptf);
printf("%d   %d",pti+1,ptf+1);
输出:
0x7fff5fbff8dc    0x7fff5fbff8a0
0x7fffafbff8de    0x7fff5fbff8a8

0x7fff5fbff8dc+1是否是 0x7fff5fbff8de?
0x7fff5bff8a0+1是否是0x7fff5bff8a8?
在系统里,地址按字节编址,short类型占用2字节,double类型占用8字节。
在指针加1指的是增加一个存储单元。对数组而言,加1后的地址是下一个元素的地址,而不是下一个字节的地址。
在这里插入图片描述

dates+2==&date[2]       //相同的地址
*(dates+2==dates[2]  //相同的值
再次强调一下,数组名是该数组首元素的地址

ar[n]的意思是*(ar+n)。可以认为 *(ar+n)的意思是“到内存的ar位置,然后移动n个单元,检索存储在那里的值。”

*(dates+2//dates第3个元素的值
*date+2       //dates第1个元素的值加2

在这里插入图片描述
2. *p++
意思是:取出p所指的那个数据来,完事之后顺便把p移到下一个位置去
常用于数组类的连续空间操作。

3. 指针比较
<,<=,==,>,>=,!=都可以对指针做
比较它们在内存中的地址
数组中的单元的地址肯定是线性递增的
4. 0地址
0地址通常是个不能随便碰的地址。
可以用0地址来表示特殊的事情:返回的指针是无效的;指针没有被真正初始化(先初始化为0)。
NULL是一个预定定义的符号,表示0地址。

函数、数组和指针

  1. int*ar形式和int ar[ ]形式都表示ar是一个指向int类型的指针。但是,int ar[ ]只能用于声明形式参数。第2种形式提醒读者指针ar指向的不仅仅一个int类型值,还是一个int类型数组的元素。

编写一个处理数组marbles的函数,该函数返回数组中所有元素之和。
让函数获得数组元素个数的第一种方法:在函数代码中写上固定的数组大小:

int sum(int*ar); //对应的函数原型
sum(marbles);    /*可能的函数调用;sum()从实参获得了该数组首元素的地址
                    知道要在这个位置找出一个整数*/
int sum(int*ar)   //函数定义
{
  int i;
  int total=0;
  for(i=0;i<10;i++) //假设数组有10个元素
  {
    total+=ar[i];    //ar[i]和*(ar+1)相同
    return total;
  }
}

第二种:把数组大小作为第2个形参:

int sum(int*ar); //对应的函数原型
sum(marbles);    /*可能的函数调用;sum()从实参获得了该数组首元素的地址
                    知道要在这个位置找出一个整数*/
int sum(int*ar,int n)   //函数定义
{
  int i;
  int total=0;
  for(i=0;i<n;i++) //假设数组有10个元素
  {
    total+=ar[i];    //ar[i]和*(ar+1)相同
    return total;
  }
}
int sum(int *ar);
int marbles[10]={0};
answer=sum(marble);
printf("%zd bytes",size marbles);
int sum(int *ar)
{
  printf("%zd bytes",sizeof ar);
}
输出:
8 bytes
40 bytes

int类型的值占4bytes,所以marbles的大小是40字节;
ar是一个指向marbles数组首元素的指针。我们的系统用8字节存储地址,所以ar的大小为8字节。

使用指针形参

在这里插入图片描述
. c保证在给数组分配空间时,指向数组后面第一个位置的指针仍是有效的指针。
使用这种“越界”指针的函数调用更为简洁。

为什么数组传进函数后的sizeof不对了?

  1. 函数参数表中的数组实际上是指针。
    sizeof(a)==sizeof(int*)。
    但是可以用数组的运算符[ ]进行运算。
    示例:
//这里的knownPrimes[]实际上是指针。
int isPrimes(int x, int knownPrimes[], int numberofKnownPrimes)
{
	int ret = 1;
	int i;
	for (i = 0; i < numberofKnownPrimes; i++)
	{
		if (x % knownPrimes[1] == 0)
		{
			ret = 0;
			break;
		}
	}
	return 0;
}

数组参数

以下四种函数原型是等价的:

1. int sum(int*ar,int n);
2. int sum(int*,int);
3. int sum(int ar[],int n);
4. int sum(int[],int);

指针常见的错误

定义了指针变量,还没有指向任何变量,就开始使用指针。

指针的类型

无论指向什么类型,所有的指针的大小都是一样的,因为都是地址。
但是指向不同类型的指针是不能直接互相赋值的。

数组的大小

sizeof(a)给出整个数组所占据的内容的大小,单位是字节。
sizeof(a[0])给出数组中的单个元素的大小,于是相除就得到了数组的单元个数。
优点:这样的代码,一旦修改数组中初识的数据,不需要修改遍历的代码。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

啊桂分呐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值