数组
由数据类型相同的一系列元素组成,派生类型
声明时必须说明元素个数和类型,按顺序存储,下标索引访问
可以高效处理大量相关数据
自动存储类别
声明、初始化、赋值、边界
type name[size]; 类型 数组名 [数组长度]
#define MONTHS 12 // 符号常量表示数组大小,方便修改数组大小
#define SIZE 4
// // 声明
// float candy [365];
// /* 内含365个float类型元素的数组
// []表明是数组,方括号中的数字表示数组元素个数
// 使用数组下标访问数组元素 下标从0开始*/
// char code [12];/*内含12个char类型元素的数组*/
// int states [50];/*内含50个int类型元素的数组*/
// 初始化
int days[MONTHS] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
/* 从ANSI C开始支持这种初始化
* 以花括号括起来,逗号分割的值列表来初始化数组,类型需匹配
* 31赋值给数组的首元素powers[0]
*
* 没有初始化就使用会是随机值
*
* 初始化列表的项的个数和数组大小应一致,
* 初始化列表的值更少,剩余会自动初始化为0
* 初始化列表的值更多,会编译报错,越界
*
* 方括号的数字可以省略,会根据初始化列表自动匹配数组大小
*
* 不支持ANSI的编译器会认此为语法错误,需要
* static days[MONTHS] = {31,28,31,30,31,30,31,31,30,31,30,31 };
*
* const int days[MONTHS] = {31,28,31,30,31,30,31,31,30,31,30,31 };
* 把数组设置为已读,声明时必须初始化,初始化之后只可读值,运行过程中不能修改*/
int index;
for (index = 0; index < MONTHS; index++)
printf("Month %2d has %2d days.\n", index + 1, days[index]);
/*printf("Month %2d has %d days.\n", index + 1, *(days + index));
(days+index)是days[index]的地址,*(days+index)是days[index]值相同*/
/*for(index = 0; index < sizeof(days)/sizeof(days[0]); index++)
* 不写死数组大小,求整个数组的大小除以数组一个元素的大小,
strlen(str)= sizeof(str)/sizeof(str[0]) */
// 赋值
int counter, evens[SIZE];
for (counter = 0; counter < SIZE; counter++)
evens[counter] = 2 * counter;
/*数组下标给数组元素赋值、下标范围0-(size-1)
* 下标不可越界 events[SIZE] error,越界可能引起数据错误或程序异常终止
* 初始化之外不可以用数组或列表赋值给数组 events[SIZE]={5,3,2,8} error
* int yaks[SIZE];yaks=events error*/
//边界
int value1 = 44;
int arr[SIZE];//长度为4,下标边界0-3
int value2 = 88;
int i;
printf("value1=%d,value2=%d\n", value1, value2);
for (i = -1; i <= SIZE; i++)
arr[i] = 2 * i + 1; //越界赋值导致其他变量被改变
for (i = -1; i < 7; i++)
printf("%2d %d\n", i, arr[i]);
printf("value1=%d,value2=%d\n" ,value1,value2);
printf("address of arr[-1]:%p\n", &arr[-1]); // 越界
printf("address of arr[4]:%p\n", &arr[4]);
printf("address of value1:%p\n", &value1);
printf("address of value2:%p\n", &value2);
指定化初始器 (C99)
C99可初始化指定的数组元素
// int arr[6]={[5]=212}; //把arr[5]初始化为212,未初始化元素置0
int days[MONTHS] = {31, 28, [4] = 31, 30, 31, [1] = 29};
/* 31,29,0,0,31,30,31,0,0,0,0,0
* 如果指定初始化器后有更多的值,[4]后面还有30,31,后面的值将被用于初始化指定元素后面的元素,即5,6
* 如果再次初始化指定的元素,后面的初始化会覆盖前面的
* 如果未指定数组大小,会把数组大小设置为足够装得下初始化的值
* eg:int stuff[ ] = { 1,[6] =23} ;数组长度为7
int staff[] = { 1,[6]= 4, 9,10};数组长度为9
*/
指定数组大小
C99之前 数组长度只能使用整型常量表达式,表达式必须大于0
int arr[SIZE]; // 整数符号常量
double lots[144]; // 整数字面常量
int n = 5;
int m = 8;
float a1[5]; // 可以
float a2[5 * 2 + 1]; // 可以
float a3[sizeof(int) + 1]; // 可以,sizeof视为整型常量
// float a4[-4]; // 不可以,数组大小必须大于0
// float a5[0]; // 不可以,数组大小必须大于0
// float a6[2.5]; // 不可以,数组大小必须是整数
float a7[(int)2.5]; // 可以,已被强制转换为整型常量
// float a8[n]; // c99之前不允许,变长数组VLA
// float a9[m]; // c99之前不允许
多维数组
一般使用二维数组
二维数组
先看中间:
float rain[5] [12] ;//rain是一个内含5个元素的数组
每个元素的情况,要查看声明的其余部分:
float rain [5] [12] ;//一个内含12个float类型元素的数组
rain二维数组,有5个元素,eg:rain[0],每个元素都是数组;
rain[0]是一个数组,由12个float类型的元素组成,首元素为rain[0][0];
三维数组
int box[10][20][30];
内含10个元素,每个元素都是一个包含20个数组,每个数组内包含30个元素的表格,遍历需要3重循环
四维数组:4重循环
初始化
#define YEARS 5 //年
// 用2010~2014年的降水量数据初始化数组
const float rain[YEARS][MONTHS] ={
{4.3, 4.3, 4.3, 3.0, 2.0, 1.2, 0.2, 0.2, 0.4, 2.4, 3.5, 6.6},
{8.5, 8.2, 1.2, 1.6, 2.4, 0.0, 5.2, 0.9, 0.3, 0.9, 1.4, 7.3},
{9.1, 8.5, 6.7, 4.3, 2.1, 0.8, 0.2, 0.2, 1.1, 2.3, 6.1, 8.4},
{7.2, 9.9, 8.4, 3.3, 1.2, 0.8, 0.4, 0.0, 0.6, 1.7, 4.3, 6.2},
{7.6, 5.6, 3.8, 2.8, 3.8, 0.2, 0.0, 0.0, 0.0, 1.3, 2.6, 5.2}
}; /*首先一个{},其中包括5个数组,每个数组内容用{}括起来,数组间逗号分割
* 第一个{}用于初始化rain[0],数据项和数组大小和一维相同,少了初始化0,多了异常,但不会影响其他行初始化
* {}里由相同类型的元素组成,类型为float,数量为MONTHS
* 确认个数正确时可以省略内部{},数字不够初始化为0*/
int year, month;
float subtot, total;
printf(" YEAR RAINFALL(inches) \n");
for (year = 0, total = 0; year < YEARS; year++)
{ /*每一年,各月的降水量总和
* 常用遍历:两个嵌套for循环,横向遍历
* 第一个嵌套for循环处理第一个下标,外层循环改变年的值,
* 第二个循环处理第二个下标, 内层循环处理同一年的不同月*/
for (month = 0, subtot = 0; month < MONTHS; month++)
subtot += rain[year][month];
printf("%5d %15.1f\n",2010 + year, subtot);
total += subtot; // 5年的总降水量
}
printf("\nThe yearly average is %.1f inches.\n \n", total / YEARS);
printf("MONTHLY AVERAGES : \n\n");
printf("Jan Feb Mar Apr May Jun Jul Aug Sep Oct ");
printf("Nov Dec\n");
for (month = 0; month < MONTHS; month++)
{ /*每个月,5年的总降水量
* 外层循环遍历月,内层循环遍历年,纵向遍历*/
for (year = 0, subtot = 0; year < YEARS; year++)
subtot += rain[year][month];
printf("%3.1f ", subtot / YEARS);
}
printf("\n");
指针
array==&array[0] //array是数组时成立,数组名是数组首元素的地址,都是常量,程序运行过程中不会改变
按照字节编址,每个字节都按顺序编号
多字节对象的地址通常是该对象第一个字节的地址
每个类型占用的字节数见具体类型讲解
指针 = 指向对象的 地址
*指针 = 指向对象的 值
指针+1,指向下一个元素,递增 指向的类型的(字节)大小
地址可以赋值给指针变量
short dates[SIZE];
short *pti;
short index;
double bills[SIZE];
double *ptf;
pti = dates; // 把数组首地址赋给指针
ptf = bills;
printf("%20s %10s\n", "short", "double");
for (index = 0; index < SIZE; index++)
printf("pointers + %d: %10p %10p\n", index, pti + index, ptf + index);
/*指针+1指的是增加一个存储单元,
* 对数组来说,+1 即指向下一个元素
* 正确声明指针类型,才能准确指向下一个元素,和取回下一个元素的值*/
/* dates+2==&dates[2] // 相同的地址
*(dates+2)==dates[2] //相同的值,到内存的dates位置,然后移动2个单元,表示第3个元素的值
*dates+2 // *优先级高于+ ,第一个元素的值+2
*/
函数、数组、指针
ar[i] 和*(ar+i)表达式等价
数组名不可以++,只有指向数组的ar指针变量时,才能ar++,
即使函数形参是一个指针int *、同样允许在处理数组函数中使用数组表示法 ar[]
函数涉及到数组调用时,需要数组起始地址和数组长度/结束地址 两个信息
指针的大小依据地址总线,一般8字节
#define SIZE_10 10
//sum_arrl.c --数组元素之和
int sum(int ar[], int n);
/*函数声明可以省略变量名,以下声明等价,无论用哪一种实际使用的都是指针变量int *
int sum (int *ar, int n);数组名,首地址等价,都表示ar是指向int的指针,所以arr[]和*arr解释成一样
int ar[]:ar指向int类型值,是一个int类型数组的元素,只能用于声明形参
int sum (int [], int n); 只有在函数声明时可以省略参数名
int sum (int *, int n);
*/
int sump(int *start, int *end);
int marbles[SIZE_10] = {20, 10, 5, 39, 4, 16, 19, 26, 31, 20};
long answer;
//开始指针+数组长度
answer = sum(marbles, SIZE_10); // 传参使用数组名,即数组首元素的地址,int *
printf("The total number of marbles is %ld.\n", answer);
printf("The size of marbles is %zd bytes. \n", sizeof(marbles));
/* sizeof数组名,计算数组占的字节,int占4字节,10个int元素,共占40字节*/
//开始指针+结尾指针
answer = sump(marbles, marbles + SIZE_10);
// 0-(SIZE-1),marbles+SIZE指向数组末尾的下一个位置
printf("The total number of marbles is %ld.\n", answer);
//优先级
int data[2] = {100, 200};
int moredata[2] = {300, 400};
int *p1, *p2, *p3;
p1 = p2 = data;
p3 = moredata;
printf(" *pl = %d, *p2 = %d, *p3 = %d\n", *p1, *p2, *p3);
printf(" *p1++ = %d, *++p2 = %d, (*p3)++ = %d\n", *p1++, *++p2, (*p3)++);
/* *p1 打印完之后 ++指向下一个,++p2指向下一个200 *P2打印200,(*p3)取值打印完 300++*/
printf(" *p1 = %d, *p2 = %d, *p3 = %d\n", *p1, *p2, *p3);
int sum(int ar[], int n)
{ /* 指针(数组开始地址、数组类型)、数组长度(需要知道数组何时结束)
* 如果不传长度,可以用固定的长度,比较限制
* 也可以传数组结束位置指针
* int sum(int *ar, int n) 等价*/
int i;
int total = 0;
for (i = 0; i < n; i++)
total += ar[i]; // 累加数组元素
printf("The size of ar is %zd bytes.\n", sizeof (ar));
/*ar只是一个指向数组首地址的指针,指针的大小依据地址总线*/
return total;
}
int sump(int *start, int *end)
{ // 开始元素和结束元素,end=arr+SIZE,end前一个元素是数组结尾元素
int total = 0;
while (start < end) // 只取end前一位,=end越界
{
total += *start; // 累加数组元素
start++; // 指针指向数组下一个元素
/*等同于total += *start++; 相当于*(start++),
*和++优先级相同,结合律从右往左,先start++后*start,但是后缀模式:先用后加,故:*start,start++;
另外*++start,和*(++start)相同,前缀模式,先加后用,故先递增后指向,
(*start)++,指向的数组元素+1*/
}
return total;
}
操作
指针比较:指向相同类型的对象的指针可以用关系运算符比较指针值
int urn[5] = {100, 200, 300, 400, 500}; // 数组名不可以++,--,可以+1赋值给其他指针,不可以*其他指针
int *ptr1, *ptr2, *ptr3; // 指针可以有以下操作
/*把一个地址赋给指针,(数组名、&变量名、另一个指针)
地址应该和指针类型兼容,int型指针赋值给指向int指针*/
ptr1 = urn; // 数组名
ptr2 = &urn[2]; //&变量名
/*解引用指针 *指针 = 指针指向的值,使用*指针前必须先初始化指针(先指向一个东西才能取值赋值),
*获得指针的地址 &指针 = 指针的地址 */
printf("pointer value,dereferenced pointer,pointer address : \n");
printf("ptr1 = %p,*ptr1 =%d,&ptr1 = %p\n", ptr1, *ptr1, &ptr1);
/*指针加法,指针内容=整数*指针指向类型的大小+指针指向初始地址,字节为单位
* ptr1+4 = &ptr1[4],超出数组范围结果无效 */
ptr3 = ptr1 + 4;
printf(" \nadding an int to a pointer : \n");
printf("ptr1 + 4 = %p,*(ptr1 + 4) = %d\n", ptr1 + 4, *(ptr1 + 4));
ptr1++; // 递增指针,指针内容指向下一个元素,指针本身的地址不变
printf("\nvalues after ptr1++ : \n");
printf("ptr1 = %p,*ptr1 = %d,&ptr1 = %p\n", ptr1, *ptr1, &ptr1);
ptr2--; // 递减指针,指针内容指向上一个元素,指针本身的地址不变
printf("\nvalues after --ptr2 : \n");
printf("ptr2 = %p,*ptr2 = %d,&ptr2 = %p\n", ptr2, *ptr2, &ptr2);
--ptr1; // 恢复为初始值
++ptr2; // 恢复为初始值
printf("\nPointers reset to original values : \n");
printf("ptr1 = %p, ptr2 = %p\n", ptr1, ptr2);
/*一个指针减去另一个指针
* 一般求差的两个指针分别指向同一个数组的不同元素,求差得出两元素距离
* ptr2-ptr1=2,中间相差两个元素、 只有在一个数组相减时有效的*/
printf("\nsubtracting one pointer from another : \n");
printf("ptr2 = %p,ptr1 = %p,ptr2 - ptr1 = %td\n", ptr2, ptr1, ptr2 - ptr1);
/*一个指针减去一个整数,指针必须是第一个运算对象
* 指针-整数 指针内容=指针指向的初始地址-整数*指针指向类型的大小
* 相减超出数组范围数据无效*/
printf("\nsubtracting an int from a pointer : \n");
printf("ptr3 = %p, ptr3 - 2 = %p\n", ptr3, ptr3 - 2);
const
值传递
直接传值,函数使用的是原数据的副本,可以保证数据完整性;
需要改变值的时候才传递指针;
数组传递
数组只能传指针,把数组的地址传递给函数,直接处理原数组的效率更高;
函数使用的是原始数据,无法保证数据不被误改变;
按值传数组需要分配足够空间存原数组的副本,然后拷贝,效率低;
const修饰形参可以保护数组不被函数改变
const指针赋值右边可以是const或者非const
非const指针赋值右边只能是非const
// const double PI = 3.14159;//修饰变量做常量
// const int days [MONTHS] = { 31,28,31,30,31,30,31,31,30,31,30,31};
// //修饰数组,数组元素的值均不可改变,eg:days[9]=44编译器报错
// double rates[5] = {88.99,100.12,59.45,183.11,340.5};
// const double * pd = rates;
// /*常量指针,不可以通过指针改变值,可以指向别处
// * *pd=29.89;不允许
// * pd[2]=222.22;不允许
// * rates[0]=99.99;常量指针指向数组可以通过其他渠道修改
// * pd++; 可以指向别处
// * const指针赋值右边可以是const或者非const
// * 普通指针赋值右边只能是非const指针*/
// double * const pc=rates;
// /*const靠近指针名,指针常量,不可以指向别处,声明时必须初始化
// * *pc=92.99;可以更改指向的值
// * pc =&rates[2];不可以指向其他位置*/
// const double * const pc=rates;//既不可更改指向地址,也不可更改指向地址上的值
#define SIZE_5 5
void show_array(const double ar[], int n);
/*const int arr[] 告诉编译器该函数不可修改ar指向的数组中的内容
如果函数中arr[i]++,编译报错
const指针赋值右边可以是const或者非const,故并不要求arr是常数组,只是该函数认为是常量,
一般需要修改的数组不用const,不用修改的数组形参加上const*/
void mult_array(double ar[], int n, double mult);
double dip[SIZE_5] = {20.0, 17.66, 8.2, 15.3, 22.22};
printf ("The original dip array : \n") ;
show_array(dip, SIZE_5);
mult_array(dip, SIZE_5, 2.5);
printf ("The dip array after calling mult_array():\n" );
show_array(dip, SIZE_5);
void show_array(const double ar[], int n)
{/*显示数组的内容,函数定义*/
int i;
for (i = 0; i < n; i++)
printf("%8.3f ", ar[i]);
putchar('\n');
}
void mult_array(double ar[], int n, double mult)
{/*把数组的每个元素都乘以相同的值,没有const修饰形参,实参也不能被const修饰*/
int i;
for (i = 0; i < n; i++)
ar[i] *= mult; // 不需要return都可以修改原数组的值
}
指针 多维数组
/* zippol.c --zippo的相关信息*/
int zippo[4][2] = {{2, 4}, {6, 8}, {1, 3}, {5, 7}};
/*
* 数组名zippo是二维数组首元素(包含两个int类型元素的一维数组)的地址
故zippo是内含两个int值的数组的地址,即 &zippo[0],
*zippo=zippo[0]=&zippo[0][0]
**zippo=*(zippo[0])=zippo[0][0] 地址的地址需要解引用两次,双重间接
zippo+1=&zippo[1] 指针指向数组,每次加1相当于加一个数组,8字节
* 首元素zippo[0]为包含两个int的数组,
zippo[0]是首元素(一个整数)的地址,即 &zippo[0][0],
*(zippo[0])=zippo[0][0]
zippo[0]+1=&zippo[0][1],指针指向单独int,每次加1相当于加4字节
*
* 起始地址相同zippo=zippo[0]=&zippo[0][0]
*
* zippo &zippo[0]
* zippo+2 二维数组的第3个元素(即一维数组)的地址,&zippo[2]
* *(zippo+2) 二维数组的第3个元素(即一维数组)的首元素(一个int类型的值)地址,zippo[2],&zippo[2][0]
* *(zippo+2)+1 二维数组的第3个元素(即一维数组)的第2个元素(也是一个int类型的值)地址,zippo[2]+1,&zippo[2][1]
* *(*(zippo+2)+1) 二维数组的第3个一维数组元素的第2个int类型元素的值,即数组的第3行第2列的值(zippo[2][1]) zippo[m][n] == *(*(zippo + m)+n)
*/
printf(" zippo = %p,zippo+1 = %p\n", zippo, zippo + 1);
printf(" zippo[0] = %p,zippo[0]+1 = %p\n", zippo[0], zippo[0] + 1);
printf(" *zippo = %p,*zippo+1 = %p\n", *zippo, *zippo + 1);
printf(" zippo[0][0]= %d\n", zippo[0][0]);
printf(" *zippo[0] = %d\n", *zippo[0]);
printf(" **zippo = %d\n", **zippo);
printf(" zippo[2][1]= %d\n", zippo[2][1]);
printf(" *(*(zippo+2)+1)=%d\n", *(*(zippo + 2) + 1));
int(*pz)[2]; // pz是一个指针,指向一个内含两个int类型值的数组,和zippo匹配,和zippo用法相同 pz[m][n] == *(*(pz + m) + n)
int *pax[2]; // pax是一个数组,内含两个指针元素,每个元素都指向int的指针, []优先级更高
pz = zippo;
printf(" pz = %p,pz + 1 = %p\n", pz, pz + 1);
printf(" pz[0] = %p,pz[0] + 1 = %p\n", pz[0], pz[0] + 1);
printf(" *pz = %p,*pz + 1 = %p\n", *pz, *pz + 1);
printf(" pz[0][0] = %d\n", pz[0][0]);
printf(" *pz[0] = %d\n", *pz[0]);
printf(" **pz = %d\n", **pz);
printf(" pz[2][1] = %d\n", pz[2][1]);
printf(" *(*(pz+2) + 1) = %d\n", *(*(pz + 2) + 1));
函数 多维数组
//array2d.c --处理二维数组的函数
#define ROWS 3
#define COLS 4
void sum_rows(int ar[][COLS], int rows);
void sum_cols(int[][COLS], int); // 省略形参名,没问题
int sum2d(int (*ar)[COLS], int rows); // 另一种语法
/* 起始为一个指向(内含4个int元素的)数组的指针,共有3个(内含4个int元素的)数组元素
* 指向数组首元素的指针,元素个数
* 错误:int sum2 (int ar[][], int rows);编译器需要知道ar+1,就需要知道ar指向的对象的大小
* 故int ar[][4],即可指明ar指向一个4个int元素的数组
* int sum2(int ar[3][4], int rows ) ; //有效声明,但是3将被忽略
* 声明一个指向N维数组的指针时,只能省略最左边方括号的值
* int sum4d (int ar[][12][20][30], int rows) ;//ar[]声明指针,其他描述类型
* int sum4d (int (*ar)[12][20][30], int rows);
*/
int junk[ROWS][COLS] = {
{ 2,4,6,8 } ,
{ 3,5,7,9 } ,
{ 12,10,8,6}
} ;//3行4列
sum_rows(junk,ROWS);//每行总数
sum_cols(junk,ROWS);//每列总数
printf ("Sum of all elements = %d\n",sum2d(junk,ROWS));
void sum_rows(int ar[][COLS], int rows)
{
int r, c, tot;
for (r = 0; r < rows; r++)
{ // 每一行里把列递归完相加
tot = 0;
for (c = 0; c < COLS; c++)
tot += ar[r][c];
printf("row %d: sum = %d\n", r, tot);
}
}
void sum_cols(int ar[][COLS], int rows)
{
int r, c, tot;
for (c = 0; c < COLS; c++)
{ // 每一列里把行递归完相加
tot = 0;
for (r = 0; r < rows; r++)
tot += ar[r][c];
printf("col %d: sum = %d\n", c, tot);
}
}
int sum2d(int ar[][COLS], int rows)
{
int r, c, tot = 0;
for (r = 0; r < rows; r++)
for (c = 0; c < COLS; c++)
tot += ar[r][c];
return tot;
}
VLA变长数组
C规定数组的长度必须是常量、整型常量表达式、整型常量组合
eg:20,sizeof表达式、其他不是const的内容
C90规定const不可以作为数组长度
普通C数组 ,静态内存分配,在编译时确定数组大小
C99增加了变长数组:长度可以是变量,创建之后大小不变,自动存储类型,不可以使用static或extern,不能在声明时初始化
C99/C11允许在变长数组中使用const
变长数组允许动态分配,运行时指定数组大小
int sum2d_VLA(int, int, int ar[*][*]); // 声明时可以省略维度形参名
int quarters = 4;
int regions = 5;
double sales[regions][quarters]; // 一个变长数组(VLA )
int i, j;
int rs = 3;
int cs = 10;
int varr[rs][cs]; // 变长数组(VLA)
for (i = 0; i < rs; i++)
for (j = 0; j < cs; j++)
varr[i][j] = i * j + j;
printf("3x10 VLA\n");
printf("sum of all elements = %d\n", sum2d_VLA(rs, cs, varr));
int sum2d_VLA(int rows, int cols, int ar[rows][cols])
{ /*前两个形参作为第3个形参的两个维度,因此ar的形参必须在前两个形参之后,不可逆序
* 函数形参声明变长数组并没有实际创建数组,只是一个指针,还是在原数组处理数据,可以直接修改
*/
int r, c, tot = 0;
for (r = 0; r < rows; r++)
for (c = 0; c < cols; c++)
tot += ar[r][c];
return tot;
}
复合字面量
字面量:除符号常量外的常量
eg:5是int类型字面量,81.3是double类型的字面量,'Y’是char类型的字面量,"elephant"是字符串字面量。
复合字面量:类似数组初始化列表,C99支持,只是临时提供值的手段,块作用域
// (int[2]) {10, 20}
// /*有2个int值的复合字面量 ,int[2]是复合字面量的类型名,
// * 类型名代表首元素地址,可以赋值给指针
// * 可以省略数组大小 (int []){10,20,90}
// */
/*必须创建的同时使用*/
int *pt1;
pt1 = (int[2]){10, 20}; // *pt1是10,pt1 [1]是20
/* 可以作为实参传递给函数*/
//int sum(const int ar[], int n);
int total3;
total3 = sum((int[]){4, 4, 4, 5, 5, 5}, 6); // 传参之前不需要提前创建数组
/* 用于多维数组*/
int(*pt2)[4]; // 声明一个指向二维数组的指针,该数组内含2个数组元素,每个元素是内含4个int类型值的数组
pt2 = (int[2][4]){{1, 2, 3, -9}, {4, 5, 6, -8}};
指针使用规则
不同类型指针不能赋值,没有隐式类型转换
多重解引用,涉及const不安全
int n = 5;
double x;
int *p1 = &n;
double *pd = &x;
x = n; // 隐式类型转换
// pd = p1;//不同类型指针不能赋值,编译时错误
int *pt;
int(*pa)[3];
int ar1[2][3];
int ar2[3][2];
int **p2; // 一个指向指针的指针
pt = &ar1[0][0]; // 指向int的指针
pt = ar1[0]; // 指向int的指针
// pt = ar1;//指向int的指针、指针的指针、赋值无效
pa = ar1; // 都是指向内含3个int类型元素数组的指针
// pa = ar2 ;//指向包括3个int元素的数组的指针、指向包括2个int元素的数组的指针、无效
p2 = &pt; // 指针的指针,都指向一个int
*p2 = ar2[0]; // 都是指向int 的指针
// p2 = ar2;//指针的指针指向一个int、指向包括3个int元素的数组的指针、无效
int m = 20;
const int y = 23;
int *p3 = &m;
const int *p4 = &y;
const int **pp2;
// p3 = p4;//不安全--把const指针赋给非const指针,可以使用新的指针改变const指针指向的数据,故C语言会报编译警告,C++报错
p4 = p3; // 有效--把非const指针赋给const指针,只进行一级解引用
// pp2 = &p3;//不安全--嵌套指针类型赋值
// const int **pp3;
// int *p5;
// const int u = 13;
// pp3 = &p5;//允许,这样赋值出现下面的情况会导致const 限定符失效(根据第1行代码,不能通过*pp3修改它所指向的内容)
// *pp3 = &u;//有效,两者都声明为const,但是这将导致p5指向n, 由第一句*pp3=p5 即p5=&u
// *p5 = 10;//有效,p5=&n ,*p5相当于n=10(但是根据第3行代码,不能修改n的值),涉及到多重解引用,const,会导致不同编译器出现不同的结果