C-study(十)

本文围绕C语言中数组和指针展开。介绍了数组的声明、初始化、赋值、边界等,包括多维数组的使用;阐述了指针与数组的关系,如数组名是首元素地址,指针操作及指针在函数中的应用;还提及const的使用、VLA变长数组、复合字面量以及指针使用规则等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数组

数据类型相同的一系列元素组成,派生类型

声明时必须说明元素个数和类型,按顺序存储,下标索引访问

可以高效处理大量相关数据

自动存储类别

声明、初始化、赋值、边界

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,会导致不同编译器出现不同的结果
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值