C primer plus(学习笔记)—— 第十章 数组和指针

目录

10.1 数组

10.1.1 初始化数组

10.1.2 指定初始化器

10.1.3 给数组元素赋值

10.1.4 数组边界

10.1.5 指定数组的大小

10.2 多维数组

10.2.1 初始化二维数组

10.2.2 其他多维数组

10.3 指针和数组

10.4 函数和指针

10.4.1 使用指针形参

10.4.2 指针表示法和数组表示法

10.5 指针操作

10.6 保护数组中的数据

10.6.1 对形式参数使用const

10.6.2 const的其他内容

10.7 指针和多维数组

10.8 变长数组(VLA)

一、定义和语法

二、特点和优势

三、限制和注意事项

10.9 复合字面量


10.1 数组


数组由数据类型相同的一系列元素组成。使用数组时,通过声明数组告诉编译器数组中含有多少元素和这些元素的类型。

下面是几个数组声明:

float candy[365];

char code[12];

10.1.1 初始化数组

标量变量:只存储单个值的变量

使用{}来初始化数组:

int powers[3] = {1,2,4};

tip:使用const前缀声明数组,可以把数组设置为只读。

例如:const int powers[3] = {1,2,4};

初始化数组时可以省略方括号里的数字,编译器会根据初始化列表中的项数来确定数组大小。

int powers[] = {1,2,4};

使用for循环遍历数组时,可以让计算机计算数组大小,例如一个数组days[],  通过 sizeof days/ sizeof days[0]  可得元素个数。

//sizeof days得到数组大小,sizeof days[0]得到单个数组大小。相除得到个数。

//no_data.c--未初始化数组
#include <stdio.h>
#define SIZE 4
int main(void){
    int no_data[SIZE];  //未初始化数组
    int i;

    printf("%2s%14s\n", "i","no_data[i]");
    for(i = 0; i< SIZE;i++){
        printf("%2d%14d\n", i,no_data[i]);
    }
    return 0;
}

 示例结果:

        乱码原因:残留数据

//no_data.c--未初始化数组
#include <stdio.h>
#define SIZE 4
int main(void){
    int no_data[SIZE]={1492,1066};  //未初始化数组
    int i;

    printf("%2s%14s\n", "i","no_data[i]");
    for(i = 0; i< SIZE;i++){
        printf("%2d%14d\n", i,no_data[i]);
    }
    return 0;
}

 示例结果:     

当初始化列表中的值少于数组元素个数时,编译器会把剩余的元素都初始化为0(编译器会自动匹配数组大小和初始化列表中的项数)

//day_mon2.c--让编译器计算元素个数
#include <stdio.h>

int main(void){
    const int days [] ={31,28,31,30,31,30,31,31,30,31,30,31};
    int index;

    for(index=0;index<sizeof (days)/sizeof (days[0]);index++)
        printf("Mouth %2d has %d days.\n",index +1,days[index]);
    return 0;
}

 示例结果:

PS:

  • 编译器会根据初始化列表中的项数来确认数组的大小
  • sizeof运算符会给出它所运算对象的大小(字节为单位)sizeof(days)是整个数组的大小,sizeof(days[0])是数组中一个元素的大小 ;sizeof(days)/sizeof(days[0])表示数组元素的个数

弊端:无法察觉初始化列表中的项数有误

10.1.2 指定初始化器

C99可以初始化指定的数组元素:

int arr[6] { [5] = 212}

//designate.c--使用指定初始化器
#include <stdio.h>
#define MONTHS 12

int main(void){
    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,days[i]);
    }
    return 0;
}

示例结果:        

重要特性:

  • 如果指定初始化器后面有更多的值,如该例中的初始化列表中的片段;[4]=31,30,31;那么后面的这些值将被用于初始化指定元素后面的元素。
  • 如果再次初始化指定的元素,那么最后的初始化将会取代之前的初始化

10.1.3 给数组元素赋值

使用元素下标给数组元素赋值。

a[2] = 4;

C不允许把数组作为一个单元赋给另一个数组

10.1.4 数组边界

防止数组下标越界。如果声明数组int arr[N],数组下标范围是0~ N-1

示例:

//bounds.c --数组下标越界
#include <stdio.h>
#define SIZE 4

int main(void){
    int valve1=44;
    int arr[SIZE];
    int valve2=88;
    int i;

    printf("value1 = %d,value2=%d\n",valve1,valve2);
    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",valve1,valve2);
    printf("address of arr[-1]:%p\n",&arr[-1]);
    printf("address of value1:%p\n",&valve1);
    printf("address of value2:%p\n",&valve2);

    return 0;
}

 示例结果:

在C标准中,使用越界下标的结果是未定义的;使用越界的数组下标会导致程序改变其他变量的值

10.1.5 指定数组的大小

10.2 多维数组


多维数组可以看成是数组的数组。

如:int day[12][31] , 可以看成是day有12个元素,每个元素都是一个包含31个int的数组。

在具体的任务中,基本上分为主数组和其他部分;

无论是二维数组、三维数组还是多维数组,在计算机内存中通常都是顺序存储的。

//计算每年的总的降水量、年平均降水量、5年中每月的平均降水量
#include <stdio.h>
#define MONTHS 12
#define YEARS  5
int main(void)
{
	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 };
	int years, months;
	float subtot, total=0;//subtot-每年的降水总量,total-5年降水总量
	printf("YEAR		RAINFALL(inches)\n");
	for (years = 0; years < YEARS; years++)
	{
		for (subtot=0,months = 0; months < MONTHS; months++)
			subtot += *(*(rain + years) + months);
		printf("%4d		%.1f\n", 2010 + years, subtot);
		total += subtot;
	}
	printf("The yearly average is %.1f inches.\n", total / YEARS);
	printf("MONTHLY AVERAGES:\n");
	printf("Jan  Feb  Mar  Apr  May  Jun  Jul  Aug  Sep  Oct  Nov  Dec\n");
	for (months = 0; months < MONTHS; months++)
	{
		subtot = 0;
		for (years = 0; years < YEARS; years++)
			subtot += *(*(rain + years) + months);
		printf("%.1f  ", subtot / YEARS);
	}
	
	printf("\nDone!\n");
	return 0;
}

示例结果:

10.2.1 初始化二维数组

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 }};

初始化可以省略内部的花括号,只保留最外面的一对花括号。只要保证初始化的数值个数正确,初始化的效果和上面相同

10.2.2 其他多维数组

通常我们处理几位数组我们就用几层嵌套循环

10.3 指针和数组


数组是变相的指针。

a == &a[0]   数组名是首元素的地址。

10.4 函数和指针


传递数组给函数:

int sum(int *ar, int n);

等价于

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

10.4.1 使用指针形参

int sump(int *start, int * end)

{

...

while(start < end)

{...}

}

10.4.2 指针表示法和数组表示法

10.5 指针操作

操作实例:

// ptr_ops.c -- 指针操作
#include <stdio.h>
int main(void)
{
   int urn[5] = { 100, 200, 300, 400, 500 };
   int * ptr1, *ptr2, *ptr3;
   ptr1 = urn;      // 把一个地址赋给指针
   ptr2 = &urn[2];    // 把一个地址赋给指针
              // 解引用指针,以及获得指针的地址
   printf("pointer value, dereferenced pointer, pointer address:\n");
   printf("ptr1 = %p, *ptr1 =%d, &ptr1 = %p\n", ptr1, *ptr1, &ptr1);
   // 指针加法
   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);
   // 一个指针减去另一个指针
   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);
   return 0;
}

 示例结果:

  • 赋值:可以把地址赋给指针。例如,用数组名、带地址运算符(&)的变量名、另一个指针进行赋值。在该例中,把urn数组的首地址赋给了ptr1,该地址的编号恰好是0x7fff5fbff8d0。变量ptr2获得数组urn的第3个元素(urn[2])的地址。注意,地址应该和指针类型兼容。也就是说,不能把double类型的地址赋给指向int的指针,至少要避免不明智的类型转换。C99/C11已经强制不允许这样做。
  • 解引用:*运算符给出指针指向地址上存储的值。因此,*ptr1的初值是100,该值存储在编号为0x7fff5fbff8d0的地址上
  • 取址:和所有变量一样,指针变量也有自己的地址和值。对指针而言,&运算符给出指针本身的地址。本例中,ptr1存储在内存编号为0x7fff5fbff8c8的地址上,该存储单元存储的内容是0x7fff5fbff8d0,即urn的地址。因此&ptr1是指向ptr1的指针,而ptr1是指向utn[0]的指针。
  • 指针与整数相加:可以使用+运算符把指针与整数相加,或整数与指针相加。无论哪种情况,整数都会和指针所指向类型的大小(以字节为单位)相乘,然后把结果与初始地址相加。因此ptr1+4与&urn[4]等价。如果相加的结果超出了初始指针指向的数组范围,计算结果则是未定义的。除非正好超过数组末尾第一个位置,C保证该指针有效。
  • 递增指针:递增指向数组元素的指针可以让该指针移动至数组的下一个元素。因此,ptr1++相当于把ptr1的值加上4(我们的系统中int为4字节),ptr1指向urn[1](见图10.4,该图中使用了简化的地址)。现在ptr1的值是0x7fff5fbff8d4(数组的下一个元素的地址),*ptr的值为200(即urn[1]的值)。注意,ptr1本身的地址仍是0x7fff5fbff8c8。毕竟,变量不会因为值发生变化就移动位置。
  • 指针减去一个整数:可以使用-运算符从一个指针中减去一个整数。指针必须是第1个运算对象,整数是第2个运算对象。该整数将乘以指针指向类型的大小(以字节为单位),然后用初始地址减去乘积。所以ptr3 - 2与&urn[2]等价,因为ptr3指向的是&urn[4]。如果相减的结果超出了初始指针所指向数组的范围,计算结果则是未定义的。除非正好超过数组末尾第一个位置,C保证该指针有效。
  • 递减指针:当然,除了递增指针还可以递减指针。在本例中,递减ptr2使其指向数组的第2个元素而不是第3个元素。前缀或后缀的递增和递减运算符都可以使用。注意,在重置ptr1和ptr2前,它们都指向相同的元素urn[1]。
  • 指针求差:可以计算两个指针的差值。通常,求差的两个指针分别指向同一个数组的不同元素,通过计算求出两元素之间的距离。差值的单位与数组类型的单位相同。例如,程序清单10.13的输出中,ptr2 - ptr1得2,意思是这两个指针所指向的两个元素相隔两个int,而不是2字节。只要两个指针都指向相同的数组(或者其中一个指针指向数组后面的第1个地址),C都能保证相减运算有效。如果指向两个不同数组的指针进行求差运算可能会得出一个值,或者导致运行时错误。
  • 比较:使用关系运算符可以比较两个指针的值,前提是两个指针都指向相同类型的对象。

使用指针时,尽量在声明时进行初始化,防止出现

int *pt;

*pt = 5;

这种解引用未初始化的指针会导致未知错误。(因为*pt指向的地址不确定,所以5会被存在一个不确定的地方。)

10.6 保护数组中的数据


传递地址会导致一些问题,原始数据可能会被修改。

10.6.1 对形式参数使用const

如果函数不想修改数组中的数据,可在函数原型和函数定义中使用const:

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


10.6.2 const的其他内容


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

使用const关键字保护数组:

#define MONTHS 12

const int days[MONTHS] = {31,28,31,30,31,30,31,31,30,31,30,31};

指向const的指针不能用于改变值。考虑下面的代码:

double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};

const double * pd = rates; // pd指向数组的首元素

第2行代码把pd指向的double类型的值声明为const,这表明不能使用pd来更改它所指向的值:

*pd = 29.89; // 不允许

pd[2] = 222.22; //不允许

rates[0] = 99.99; // 允许,因为rates未被const限定

可以让pd指向别处:

pd++; /* 让pd指向rates[1] – 没问题 */

指向const的指针通常用于函数形参中,表明该函数不会使用指针改变数据。

关于指针赋值和const需要注意一些规则。首先,把const数据或非const数据的地址初始化为指向const的指针或为其赋值是合法的:

double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};*

const double locked[4] = {0.08, 0.075, 0.0725, 0.07};*

const double * pc = rates; // 有效*

pc = locked; //有效

pc = &rates[3]; //有效 然而,只能把非const数据的地址赋给普通指针:

double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};*

const double locked[4] = {0.08, 0.075, 0.0725, 0.07};

double * pnc = rates; // 有效

pnc = locked; // 无效

pnc = &rates[3]; // 有效

这个规则非常合理。否则,通过指针就能改变const数组中的数据。

const还有其他的用法。例如,可以声明并初始化一个不能指向别处的指针,关键是const的位置:

double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};

double * const pc = rates; // pc指向数组的开始

pc = &rates[2]; // 不允许,因为该指针不能指向别处

*pc = 92.99; // 没问题 – 更改rates[0]的值

可以用这种指针修改它所指向的值,但是它只能指向初始化时设置的地址。

最后,在创建指针时还可以使用const两次,该指针既不能更改它所指向的地址,也不能修改指向地址上的值:

double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};

const double * const pc = rates;

pc = &rates[2]; //不允许

*pc = 92.99; //不允许

10.7 指针和多维数组

int zippo[4][2]; /* 内含int数组的数组 */

  • 然后数组名zippo是该数组首元素的地址。在本例中,zippo的首元素是一个内含两个int值的数组,所以zippo是这个内含两个int值的数组的地址。下面,我们从指针的属性进一步分析。
  • 因为zippo是数组首元素的地址,所以zippo的值和&zippo[0]的值相同。而zippo[0]本身是一个内含两个整数的数组,所以zippo[0]的值和它首元素(一个整数)的地址(即&zippo[0][0]的值)相同。简而言之,zippo[0]是一个占用一个int大小对象的地址,而zippo是一个占用两个int大小对象的地址。由于这个整数和内含两个整数的数组都开始于同一个地址,所以zippo和zippo[0]的值相同。
  • 给指针或地址加1,其值会增加对应类型大小的数值。在这方面,zippo和zippo[0]不同,因为zippo指向的对象占用了两个int大小,而zippo[0]指向的对象只占用一个int大小。因此,zippo + 1和zippo[0] + 1的值不同。
  • 解引用一个指针(在指针前使用*运算符)或在数组名后使用带下标的[]运算符,得到引用对象代表的值。因为zippo[0]是该数组首元素(zippo[0][0])的地址,所以*(zippo[0])表示存储在zippo[0][0]上的值(即一个int类型的值)。与此类似,*zippo代表该数组首元素(zippo[0])的值,但是zippo[0]本身是一个int类型值的地址。该值的地址是&zippo[0][0],所以*zippo就是&zippo[0][0]。对两个表达式应用解引用运算符表明,**zippo与*&zippo[0][0]等价,这相当于zippo[0][0],即一个int类型的值。简而言之,zippo是地址的地址,必须解引用两次才能获得原始值。地址的地址或指针的指针是就是双重间接(double indirection)的例子。
- /* zippo1.c -- zippo的相关信息 */
#include <stdio.h>
int main(void)
{
   int zippo[4][2] = { { 2, 4 }, { 6, 8 }, { 1, 3 }, { 5, 7 } };
   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));
   return 0;
}
/*
 zippo = 0x0064fd38,    zippo + 1 = 0x0064fd40
 zippo[0]= 0x0064fd38,  zippo[0] + 1 = 0x0064fd3c
  *zippo = 0x0064fd38,   *zippo + 1 = 0x0064fd3c
zippo[0][0] = 2
 *zippo[0] = 2
  **zippo = 2
   zippo[2][1] = 3
*(*(zippo+2) + 1) = 3
*/
  • 输出显示了二维数组zippo的地址和一维数组zippo[0]的地址相同。它们的地址都是各自数组首元素的地址,因而与&zippo[0][0]的值也相同。
  • 尽管如此,它们也有差别。在我们的系统中,int是4字节。前面讨论过,zippo[0]指向一个4字节的数据对象。zippo[0]加1,其值加4(十六进制中,38+4得3c)。数组名zippo是一个内含2个int类型值的数组的地址,所以zippo指向一个8字节的数据对象。因此,zippo加1,它所指向的地址加8字节(十六进制中,38+8得40)。
  • 该程序演示了zippo[0]和*zippo完全相同,实际上确实如此。然后,对二维数组名解引用两次,得到存储在数组中的值。使用两个间接运算符(*)或者使用两对方括号([])都能获得该值(还可以使用一个*和一对[],但是我们暂不讨论这么多情况)。
  • 要特别注意,与zippo[2][1]等价的指针表示法是*(*(zippo+2) + 1)。看上去比较复杂,应最好能理解。

    zippo       ←二维数组首元素的地址(每个元素都是内含两个int类型元素的一维数组)
    zippo+2      ←二维数组的第3个元素(即一维数组)的地址
    *(zippo+2)    ←二维数组的第3个元素(即一维数组)的首元素(一个int类型的值)地址
    *(zippo+2) + 1  ←二维数组的第3个元素(即一维数组)的第2个元素(也是一个int类型的值)地址
    *(*(zippo+2) + 1) ←二维数组的第3个一维数组元素的第2个int类型元素的值,即数组的第3行第2
              列的值(zippo[2][1])

10.8 变长数组(VLA)

在 C 语言中,变长数组(Variable Length Arrays,VLA)是一种在 C99 标准中引入的特性。

一、定义和语法

变长数组允许你在程序运行时指定数组的大小,而不是像传统的 C 语言数组那样必须在编译时确定大小。其语法如下:

​​int n;
scanf("%d", &n);
int arr[n]; // 定义一个变长数组,大小由运行时输入的 n 决定

二、特点和优势

  1. 灵活性

    • 可以根据程序运行时的实际情况动态确定数组的大小,适应不同的输入和场景需求。例如,在处理不同大小的数据集时,可以根据实际数据的数量来创建合适大小的数组,避免了浪费内存或内存不足的问题。
  2. 局部性

    • 变长数组通常在函数内部声明,与自动变量具有相似的存储方式和生命周期。这使得它们在局部作用域内具有更好的内存局部性,可能提高程序的性能。

三、限制和注意事项

  1. 作用域限制
  • 变长数组通常只能在声明它们的块作用域内使用。一旦离开这个作用域,数组的内存将被自动释放。
  • 例如:
    ​void someFunction() {
        int n;
        scanf("%d", &n);
        int arr[n];
        // 使用 arr
        // 离开这个函数后,arr 的内存被释放。
    }

     2.不支持初始化列表

  • 变长数组不能像普通数组那样使用初始化列表进行初始化。
  • 例如,以下是错误的用法:
​int n = 5;
int arr[n] = {1, 2, 3, 4, 5}; // 错误,变长数组不能这样初始化

          3.并非所有编译器都完全支持

  • 虽然 C99 引入了变长数组,但并不是所有的 C 语言编译器都完全支持这个特性。一些较老的编译器可能不支持变长数组,或者只提供有限的支持。在使用变长数组时,需要确保你的编译器支持这个特性,并了解其具体的实现细节和限制。

10.9 复合字面量

C99新增了复合字面量(compound literal)。字面量是除符号常量外的常量。例如,5是int类型字面量,81.3是double类型的字面量,'Y’是char类型的字面量,"elephant"是字符串字面量。发布C99标准的委员会认为,如果有代表数组和结构内容的复合字面量,在编程时会更方便。

下面的复合字面量创建了一个和diva数组相同的匿名数组,也有两个int类型的值:

(int [2]){10, 20} // 复合字面量
注意,去掉声明中的数组名,留下的int [2]即是复合字面量的类型名。

复合字面量也可以省略大小,编译器会自动计算数组当前的元素个数:

(int []){50, 20, 90} // 内含3个元素的复合字面量

因为复合字面量是匿名的,所以不能先创建然后再使用它,必须在创建的同时使用它。使用指针记录地址就是一种用法。也就是说,可以这样用:

int * pt1;

pt1 = (int [2]) {10, 20};

还可以把复合字面量作为实际参数传递给带有匹配形式参数的函数:

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

int total3;

total3 = sum((int []){4,4,4,5,5,5}, 6);

 

// flc.c -- 有趣的常量
#include <stdio.h>
#define COLS 4
int sum2d(const int ar[][COLS], int rows);
int sum(const int ar[], int n);
int main(void)
{
   int total1, total2, total3;
   int * pt1;
   int(*pt2)[COLS];
   pt1 = (int[2]) { 10, 20 };
   pt2 = (int[2][COLS]) { {1, 2, 3, -9}, { 4, 5, 6, -8 } };
   total1 = sum(pt1, 2);
   total2 = sum2d(pt2, 2);
   total3 = sum((int []){ 4, 4, 4, 5, 5, 5 }, 6);
   printf("total1 = %d\n", total1);
   printf("total2 = %d\n", total2);
   printf("total3 = %d\n", total3);
   return 0;
}
int sum(const int ar [], int n)
{
   int i;
   int total = 0;
   for (i = 0; i < n; i++)
     total += ar[i];
   return total;
}
int sum2d(const int ar [][COLS], int rows)
{
   int r;
   int c;
   int tot = 0;
   for (r = 0; r < rows; r++)
     for (c = 0; c < COLS; c++)
        tot += ar[r][c];
   return tot;
}
  • 要支持C99的编译器才能正常运行该程序示例(目前并不是所有的编译器都支持),其输出如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值