数组与指针
本章介绍以下内容
- 关键字——static
- 运算符——& *
- 如何创建并初始化数组
- 指针、指针和数组的关系
- 编写处理数组的函数
- 二维数组
数组
定义:数组由数据类型相同的一系列元素组成,普通变量可以使用的类型,数组元素都可以使用。注意: 索引是从0开始
初始化数组
-
直接在定义时初始化数组,如果不初始化,会直接使用内存中的垃圾数据
-
当初始化列表中的项数与数组中的大小不一致时
1.项数大于数组大小,报错
2.项数小于数组大小,未初始化自动赋值为0.
**注意:**部分初始化数组,剩余元素就会被初始化为0.且字符串常量后面才会自动加 \0。 -
在使用时我们可以省略方括号中的数字,让编译器根据列表中的项数来确定数组的大小
-
size of也可用来查看数组占了多大的字节 元素个数=size of(数组名)/数组类型
指定初始化器
-
定义:利用该特性可以初始化指定的数组元素。
int days[5]={1,2,[4]=31,32,33} ;
-
初始化器的两个重要特性
- 如果指定初始化器重有很多的值,如上诉代码,则后面的值将用于初始化指定元素后面的元素
- 如果再次初始化指定的元素,那么最后的初始化将会取代之前的初始化。
给数组元素赋值
- 借助数组下标或索引给元素赋值 **注意:**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)
**注意:**复合字面量是只提供需要的值的一种手段,复合字面量具有块作用域,这意味着一旦离开定义符合字面量的块,程序将无法保证该字面量是否存在。