Snowming04's Blog
一颗红❤
Toggle navigation
Snowming04's Blog
主页
Cobalt Strike
Accelerated C++
区块链安全
友链
关于我
常用工具
代码积累
归档
标签
【C基础】课时10:数组和指针
? C ?
2020-03-09 17:16:22
439
0
0
snowming
? C ?
**<u>本章要点:</u>** - 关键字:`static` - 运算符:`&`、`*`(一元) - 如何创建并初始化数组 - 指针、指针和数组的关系 - **编写处理数组的函数** - 二维数组 # 关于数组的基本概念 <u>什么是数组?</u> 数组由数据类型相同的一系列元素组成。 > 注: 这里的数据类型是“基本数据类型”。如:float、char、int...... 普通变量可以使用的类型,数组元素都可以用。 需要使用数组时,通过`声明数组`告诉编译器: - 数组内含多少元素 - 这些元素的类型 编译器根据这些信息正确地创建数组。 <u>一些数组声明</u> ``` /* 一些数组声明 */ int main(void) { float candy[365]; //内含365个float类型元素的数组 char code[12]; /*内含12个char类型元素的数组*/ int states[50]; /*内含50个int类型元素的数组*/ } ``` - 方括号(`[]`)表示candy、code、states都是数组。 - 方括号中的数字表明数组中的元素个数。 <u>访问数组中的元素</u> 要访问数组中的元素,通过使用数组下标数(也称为`索引`)表示数组中的各元素。 数组的编号从0开始。 # 初始化数组 数组被用来储存程序需要的数据。在这种情况下,在程序一开始就初始化数组比较好。 <u>C中初始化数组的语法</u> ``` int main(void) { int powers[8] = {1,2, 4,6,8 , 16,32 ,64}; /* 从ANSI C开始支持这种初始化*/ } ``` - 逗号和值之间可以使用空格。 - 不支持 ANSI 的编译器会把这种形式的初始化识别为语法错误,在数组声明前加上关键字 `static` 可解决此问题。 **<u>使用const关键字把数组设置为只读</u>** 比如: ``` const int days[MONTHS] = {31,28,31,30,31,30,31,31,30,31,30,31}; ``` - 用 `const` 关键字**声明和初始化**数组。 这样把数组设置为只读之后,程序只能从数组中**检索值**,不能把新值写入数组。 const 使得程序在运行过程中不能修改该数组中的内容。 <u>存储类别警告</u> 数组和其他变量类似,可以把数组创建成不同的存储类别(`storage class`)。 这里描述的数组属于**自动存储类别**,意思是: 1. 这些数组在函数内部声明; 2. 声明时未使用关键字 `static`。 不同的存储类别有不同的属性,所以不能把本章的内容推广到其他存储类别。对于一些其他存储类别的变量和数组,如果在声明时未初始化,编译器会自动把它们的值设置为0。 而对于自动存储类别的数组,如果初始化失败,那么某元素的值是**内存相应位置上的现有值**,所以是不确定的值。 <u>当初始化列表中的项数与数组的大小不一致</u>  当初始化列表中的值少于数组元素个数时,编译器会把剩余的元素都初始化为0。也就是说,如果不初始化数组,数组元素和未初始化的普通变量一样,其中储存的都是垃圾值;但是,如果**部分初始化数组**,剩余的元素就会被初始化为0。 <u>自动匹配项数和数组大小</u>  - 如果初始化数组时省略方括号中的数字,编译器会根据初始化列表中的项数来确定数组的大小。 - 确定数组元素的个数可以这样计算:`size of days / size of days[0]`,可以避免人工算错数组的项数,让程序找出数组的大小,避免初始化的个数超过数组的大小导致储存垃圾值。 - 自动计数的弊端在于:无法察觉初始化列表中的项数有误。(逻辑错误等) >- `size of` 运算符给出它的运算对象的大小(以字节为单位); - 所以 size of days 是整个数组的大小(以字节为单位); - sizeof days[0] 是数组中一个元素的大小(以字节为单位); - 所以数组元素的个数 = 整个数组的大小/单个元素的大小 # # 指定初始化器(C99) - C99的新特性:可以初始化指定的数组元素,比如,**仅仅**初始化数组中的最后一个元素。对于传统C初始化语法,必须初始化最后一个元素之前的所有元素,才能初始它。 传统初始化语法: ``` int arr[6] = {0,0,0,0,0,212}; ``` 指定初始化器: ``` int arr[6] = {[5]=212}; //把arr[5]初始化为212,第6项 ```  `指定初始化器`的两个重要特性: 1. 如果指定初始化器后面有更多的值,如 `[4]=31,30,31`,那么后面这些值将被用于初始化指定元素后面的元素。days[4]被初始化为31之后,days[5]被初始化为30,days[6]被初始化31。 2. 如果再次初始化指定的元素,那么最后的初始化将会取代之前的初始化。比如days[1]一开始被初始化为28,又被指定初始化器[1]=29初始化为29。 3. 【初始化的通用特性】在初始化一个元素后,未初始化的元素都会被设置为0。 <u>未指定元素大小的指定初始化</u> ``` int stuff[] = {1,[6]=23}; //stuff数组有7个元素,编号为0~6 int staff[] = {1,[6]=4,9,10}; //staff数组有9个元素,编号为0~8 ``` # 给数组元素赋值 声明数组后,可以借助数组下标(或索引)给数组元素赋值。 ``` /* 给数组的元素赋值 */ #include <stdio.h> #define SIZE 50 int main(void) { int counter, evens[SIZE]; for (counter = 0; counter < SIZE; counter++) { evens[counter] = 2 * counter; } } ``` 这段代码中使用循环给数组的元素依次赋值。 <u>一些无效的数组赋值</u> - 不允许把数组作为一个单元赋值给另一个数组 - 除初始化之外不允许使用花括号列表的形式赋值 - oxen[SIZE]数组的最后一个元素是oxen[SIZE-1],注意不要下标越界 ``` #define SIZE 5 int main(void) { int oxen[SIZE] = {5,3,2,8}; /*初始化时候可以使用花括号列表的形式赋值*/ int yaks[SIZE]; yaks = oxen; /*不允许把数组作为一个单元赋值给另一个数组*/ yaks[SIZE] = oxen[SIZE]; /*数组下标越界,一个5大小的数组索引应该为0~4*/ yaks[SIZE] = {5,3,2,8}; /* 初始化之外使用花括号列表的形式赋值无效*/ } ``` # 数组边界 使用数组时,要防止数组下标超出边界。编译器不会检查出这种下标越界的错误。  此程序创建了一个内含4个元素的数组,然后错误地使用了-1~6的下标。 **在C标准中,使用越界下标的结果是未定义的。这意味着程序看上去可以运行,但是运行结果很奇怪,或异常中止。** 使用越界的数组下标会导致程序改变其他变量的值,不同的编译器运行该程序的结果可能不同,有些会导致程序异常中止。 # 指定数组的大小 - C99标准之前,声明数组时只能在方括号内使用`整型常量表达式`。 - C99引入变长数组(简称`VLA`): ``` float a8[n]; // C99之前不允许 float a9[m]; // C99之前不允许 ``` # 多维数组 多维数组是以数组为元素的数组。 <u>多维数组声明</u> ``` float rain[5][12]; ``` - 主数组有5个元素(每个元素表示一年) - 每个元素也是一个数组,内含12个元素(每个元素表示一个月) 这个声明表示声明一个内含5个数组元素的数组,每个数组元素内含12个float类型的元素。 > 在计算机内部,二维数组是按顺序储存的,从第1个内含12个元素的数组开始,然后是第2个内含12个元素的数组,以此类推。 # 初始化二维数组  <u>当值的个数不匹配</u>  <u>初始化二维数组的两种方法</u>  ``` int sq[2][3] = {{5,6},{7,8}}; ``` 这表示声明并初始化二维数组sq,这个二维数组有两个元素,每个元素是一个 int xxx[3] 的数组。但是初始化的时候子数组里面的元素没有完全初始化,所以每个子数组没有初始化的元素填充0。 ``` int sq[2][3] = {5,6,7,8}; ``` 这表示声明并初始化二维数组sq,这个二维数组有两个元素,每个元素是一个 int xxx[3] 数组。所以是这样的: s0 s1 s2 t0 t1 t2 但是计算机里面是依次写入数组,就是先写第一个数组,然后写入第二个数组。这里这个二维数组在初始化时候省略了内部的花括号,就会按照先后顺序逐行初始化,直到用完所有的值。因为一共有6个空,只有4个值,所以依次填入就是: 5,6,7,8,0,0 <u>const关键字</u> 如果一共数组里存储的数据应该是不能修改的,那么声明该数组的时候我们可以使用 `const` 关键字,这样在运行时程序就不能改变该数组。 如: ``` const float rain[YEARS][MONTHS] = {}; ``` # 其他多维数组 还有三维数组或更多维的数组。这些多维数组的语法规则同二维数组。 <u>声明一个三维数组</u> ``` int box[10][20][30]; ``` <u>理解三维数组</u> - 可以把一维数组想象成一行数据,把二维数组想象成数据表,把三维数组想象成一叠数据表。例如,把 `int box[10][20][30];` 这个声明的三维数组想象成由10个二维数组(每个二维数组都是20行30列)堆叠起来。 - 另一种理解三维数组的方法是:把三维数组看作数组的数组。也就是说,box内含10个元素,每个元素是内含20个元素的数组。这20个数组元素中的每个元素是内含30个元素的数组。或者,可以简单地根据所需的下标值去理解数组。 <u>处理多维数组</u> 通常,处理三维数组要使用3重嵌套循环,处理四维数组要使用4重嵌套循环。对于其他多维数组以此类推。 ----- > 划重点部分 # 指针和数组 指针:提供一种以符号形式使用地址的方法。指针能有效地处理数组。 先记住一个表示法: 数组名是数组首元素的地址。 ``` //&是地址运算符 shuzu == &shuzu[0]; //数组名是该数组首元素的地址 ``` 以上两种数组首元素地址的表示方法,都是常量。所以在程序的运行过程中不会改变。但是可以把它们赋值给`指针变量`,然后可以修改指针变量的值。 如下程序,给指针变量分别加1、2、3:  注: 1. 转换说明`%p`通常以十六进制显示指针的值。 2. 地址是十六进制的,因此 dd 比 dc 大1, a1 比 a0 大1。 <u>程序输出分析</u> - 数组首元素地址本身是一个常量,但是把数组首元素地址赋值给一个指针变量(`* pti`、`* ptf`),就可以改变此指针变量的值了。 - `pointers + 0`这一行打印的是两个数组开始的地址,下一行打印的是指针加1后的地址。 - 为什么加了1之后是这个结果呢?要结合对象类型来看。 - 在C中,指针加1指的是增加一个`存储单元`。对数组而言,这意味着加1后的地址是下一个`元素`的地址,而不是下一个字节的地址。 - 系统中,地址按字节编址,`short`类型占用2字节,`double`类型占用8字节(可以用`sizeof`查看)。 - 因为`pti`的类型是short,所以指针每加1,其值每次递增2字节。也就是short数组这边,78+2字节=7A,7A+2字节=7C,7C+2字节=7E; - 同理,因为`ptf`的类型是double。所以指针变量每加1,其值每次递增8字节。也就是:double数组这边,a0+8字节=a8,a8+8字节=b0,b0+8字节=b8。 - 这就是为什么必须声明指针所指向对象类型的原因之一。只知道地址不够,因为计算机要知道储存对象需要多少字节,否则指针变量就无法正确地取回地址上的值。 --------------- <u>数组和指针加法总结</u>  **<u>指针和数组的关系</u>** 注: dates 是指针变量 ``` dates + 2 == &dates[2] //相同的地址 *(dates + 2) == dates[2] //相同的值 ``` <u>分析</u> 记住前提知识点: 数组名是数组首元素的地址。 ``` //&是地址运算符 shuzu == &shuzu[0]; //数组名是该数组首元素的地址 ``` 以上两种数组首元素地址的表示方法,都是常量。所以在程序的运行过程中不会改变。但是可以把它们赋值给`指针变量`,然后可以修改指针变量的值。 所以, - dates 是(数组名赋值给的)指针变量,指针变量的值是它所指向对象的地址。 - dates + 2,指针变量+2,也就是增加了2个存储单元。对于数组来说,这意味着下两个元素的地址。所以dates[0]→dates[2],加上`&`符号就是其地址。 - `*(dates + 2)`意味着存储在当前指针变量对应地址往后两个存储单元的地址的值,它等于同类型的dates数组的下标2元素的值。 <u>C语言标准中借助指针描述数组</u> C 语言标准在描述数组表示法时借助了指针。 也就是说,定义 ar[n] 的意思是 *(ar + n)。可以认为 *(ar + n) 的意思是「到内存的 ar 位置,然后移动 n 个单元,检索存储在那里的值」。 总之,要**站在内存的角度思考指针和数组**,就会发现都是一样的,不同的表示法而已。 <u>`*`的优先级高于`+`</u>  <u>用**指针表示法**替换**数组表示法**</u> 用数组表示法的程序:  替换成对应的指针表示法:  把`days[index]`替换成`*(days+index)`,结果都是一样的。 <u>代码分析</u> - 这里,`days`是数组首元素的地址,`days+index` 是元素 `days[index]` 的地址,而 `*(days+index)` 是该元素的值,相当于`days[index]`。 - 指针表示法和数组表示法是两种等效的方法。编译器编译这两种写法生成的代码相同。 - 总之,要**站在内存的角度思考指针和数组**,就会发现都是一样的,不同的表示法而已。 # 函数、数组和指针 假设要编写一个处理数组的函数,该函数返回数组中所有元素之和,待处理的是名为marbles的int类型数组。 应该如何调用该函数?可能的函数调用为: ``` total = sum(marbles) //参数是函数名 ``` 那么函数的原型是什么? ``` int sum(参数类型 marbles); ``` 如何去表达marbles的类型为数组呢? 因为,数组名是该数组首元素的地址,所以实际参数marbles是一个储存int类型值的地址,应该把赋值给一个指针形式参数,即:该形参是一个指向int的指针: ``` int sum(int * ar); //对应的函数原型 ``` <u>获取数组元素个数的信息</u>  <u>在函数代码中写上固定的数组大小</u> ``` int sum(int * ar) { int i; int total = 0; for (i = 0; i < 10; i++) //假设数组有10元素 total += ar[i]; //ar[i]与*(ar + i)相等 return total; } ``` <u>把数组大小作为第2个参数</u> ``` int sum(int * ar, int n) //用n表示数组元素个数,更加具有通用性 { int i; int total = 0; for (i = 0; i < n; i++) { total += *(ar + i); } return total; } ``` <u>int ar[]</u>  <u>声明数组类型的形参</u>  <u>程序分析</u>   # 使用指数形参   要注意: - 因为start是指向int的指针,start递增1相当于其值递增int类型的大小(的字节)。 - sump() 函数使用第2个指针来结束循环。  <u>指针运算符的优先级</u>  - 一元运算符`*`和`++`的优先级相同,但是结合律是从右往左。 - 第一行:`*p1`表示data数组的首元素的地址对应的值,所以是100;`*p2`表示data数组的首元素的地址对应的值,所以是100;`*p3`表示moredata数组的首元素的地址对应的值,所以是300. - 第二行:`*p1++`,一元运算符`*`和`++`的优先级相同,但是结合律是从右往左,所以相当于`*(p1++)`,`p1`是data数组的首元素的地址,这里的加1是加上一个存储单元,注意是`p1++`而不是`++p1`,所以是相当于`*p1`先取p1指针地址存储的值,然后再递增指针,所以结果为100;<br/>`*++p2`,`*`和`++`的优先级相同,但是由于结合律从右往左,所以相当于先递增指针,然后取值。指针递增一个存储单元,相当于data[1]的值,也就是200;<br/>`(*p3)++`,虽然结合律从右往左,但是加上了括号改变了顺序,先取p3指针的值,然后加1,这个式子的结果到取完值为止,而不是计算加1后p3的值,所以结果是300(moredata数组首元素的值)。 - 第三行:计算的是经过++之后被改变的值。`p1`经过`*p1++`的改变,相当于`p1++`地址上的值,即data[1]就是200;`p2`同理,总之根据结合律,是先递增至真,再求值;`p3`就是先求指针对应的值,再递增1,所以是301。  # 指针表示法和数组表示法 处理数组的函数实际上用指针作为参数,但是在编写这样的函数时,可以选择使用指针表示法还是数组表示法。  # 指针操作  指针的一些基本操作包括: <u>1、赋值</u>  <u>2、解引用</u>  <u>3、取址</u>  <u>4、指针与整数相加</u> 加的单位其实是存储单元:  <u>5、递增指针</u> 递增指针之后指针的地址没有变,也就是 ptr1++ 之后 &ptr1 没有变,因为变量不会因为值的变化就移动位置。   <u>6、指针减去一个整数</u> 只能指针减整数,不能整数减指针:  <u>7、递减指针</u>  <u>8、指针求差</u>  <u>9、比较</u> 指针的值就是该指针指向的地址,而不是储存在指针指向地址上的值。  ------- <u>两种减法</u> 上面提到的减法有两种: 1. 可以用一个指针减去另一个指针得到一个整数(两个指针求差计算的是同一个数组中两元素之间的距离); 2. 用一个指针减去一个整数得到另一个指针(减去的整数单位是存储单元)。 <u>编译器不会检查指针是否指向数组元素</u>  <u>不要解引用未初始化的指针</u>  # 保护数组中的数据   - 注意:该程序中两个函数的返回类型都是void,虽然muly_array函数更新了dip数组的值,但是并未使用return机制。 <u>const的其他内容</u> 如果尝试改变使用const关键字保护的数组的值,编译器将生成一个编译器错误消息:  <u>指向const的指针不能用于改变值</u>  <u>只能把非const数据的地址赋给普通指针</u> ``` double rates[5] = {1.11,2.22,3.33,4.44,5.55}; const double locked[4] = {0.08,0.075,0.0725,0.07}; //有效,把非const数据的地址赋给const指针 const double * pc = rates; //有效,把const数据的地址赋给const指针 pc = locked; //有效,把非const数据的地址赋给const指针 pc = &rates[3]; //有效,把非const数据的地址赋给普通指针 double * pnc = rates; //无效!把const数据的地址赋给普通指针 pnc = locked; //有效,把非const数据的地址赋给普通指针 pnc = &rates[3]; ``` 从上面的例子可以看出: 1. 把const数据或非const数据的地址初始化为**指向const的指针**或为其赋值是合法的。(指向const的指针不能用于改变值、表明不会使用指针改变数据); 2. 把const数据的地址赋给普通指针(非const)指针是不合法的,这样的话就导致可以通过指针改变const数组中的数据; 3. 所以只能把非const数据的地址赋值给普通指针,因为把数据地址赋值给普通指针的话、就可以通过指针改变数据(比如交换数据的例子)。 <u>使用const声明并初始化一个不能指向别处的指针</u>   - const double * pc = rates; 使得不能通过该指针pc去修改指向地址上的值(但是值自己可以变); - double * const pc; 使得该指针pc不能更改它所指向的地址。 - 关键是const的位置。 # 指针和多维数组 处理多维数组的函数要用到指针。  <u>程序分析</u> - 这是一个二维数组 `int zippo[4][2]`,数组名 zippo 是该数组首元素的地址。数组 zippo 的首元素是一个内含两个int元素的数组,所以zippo是这个内含两个int值的数组的地址。地址为`000000C2F14FFC68`。给指针或地址+1,其值会增加对应类型大小的数值。因为zippo指向的对象占用了两个int的大小,由第一行打印的sizeof(int)可知,一个int 4 byte,两个int就是8 byte,末尾8+8=16,在16进制中进一位,所以就是`000000C2F14FFC70`。 - zippo[0]是一个内含两个整数的数组,**所以可以把zippo[0]看作数组名**,zippo[0]的值和它首元素的地址(即&zippo[0][0]的值相同)。即:zippo[0]是一个占用1个int大小对象的地址。由于zippo[0]这个数组和zippo[0][0]这个整数都开始于同一个地址,所以zippo和zippo[0]作为首元素值相同。所以zippo[0]也等于`000000C2F14FFC68`。zippo[0]作为数组zippo[0][0]的首元素,数据类型为指针或地址,它+1就会加上一个数据单元,就是一个int的大小4byte,8+4=12=C,所以zippo[0]+1=000000C2F14FFC6C。 - `*zippo`代表该数组首元素zippo[0]的值,但是zippo[0]本身也是一个int类型值的地址(想象成zippo[0]数组的首元素),该值的地址是&zippo[0][0],所以*zippo=&zippo[0][0]=zippo[0],但是数据单位是1 int。 - zippo[0][0] = 2 - `*zippo[0]`的话,因为zippo[0]是zippo[0]这个内层数组的首元素的地址,所以`*zippo[0]`就等于zippo[0][0]就等于2。 - 作为二维数组,zippod是地址的地址,必须解引用两次才能获得原始值。`**zippo`与`*&zippo[0][0]`等价,就相当于zippo[0][0],也就等于2。 - zippo[2][1]相当于第3个数组的第2个元素(0基),也就是3。 - `*(*(zippo+2)+1))`的话,要以数组的角度去想。首先可以看出是两层`*`取值,那么括号里面的也要看成一个地址,其实更准确的说是地址的地址。等价于zippo[2][1],所以为3。 <u>总结</u> - `zippo[0]`和`*zippo`完全相同。 - 对二维数组解引用两次,得到储存在数组中的值。 - `zippo`→二维数组首元素的地址(每个元素都是内含两个int类型元素的一维数组)。 - `zippo+2`→二维数组的第三个元素(即一维数组)的地址。 - `*(zippo+2)`→二维数组的第三个元素(即一维数组)的首元素(一个int类型的值)的地址。(即zippo[2]是zippo[2]这个数组的数组名) - `*(zippo+2)+1`→二维数组的第三个元素(即一维数组)的第2个元素(也是一个int类型的值)的地址。 - `*(*(zippo+2)+1)`→二维数组的第3个一维数组元素的第2个int类型元素的值,即数组的第3行第2列的值(zippo[2][1])。 如果程序恰好使用一个指向二维数组的指针,而且要通过该指针获取值时,最好用简单的数组表示法,而不是指针表示法。 <u>数组地址、数组内容和指针之间的关系</u>  # 指向多维数组的指针 如何声明一个指针变量pz指向一个二维数组?  <u>程序分析</u>  上面这个程序演示了如何使用指向二维数组的指针。 可以看到: 虽然pz只是一个(指向二维数组的)指针,不是数组名,但是也可以使用pz[2][1]这样的写法(zippo[2][1])。 ---------------------------- **划重点:** 可以用数组表示法或指针表示法来表示一个数组元素,既可以使用数组名,也可以使用指针名: ``` zippo[m][n] == *(*(zippo + m) + n) pz[m][n] == *(*(pz + m) + n) ``` -------------- # 指针的兼容性  - 通过非 const 指针更改 const 数据是未定义的。 <u>C const 和 C++ const</u>  # 函数和多维数组 编写处理二维数组的函数。通常使用数组表示法进行相关操作。 对于这样一个二维数组: ``` int junk[3][4] = {{2,4,5,8},{3,5,6,9},{12,10,8,6}}; ```  <u>程序分析</u>  此程序中演示了三种等价的原型语法。 - 数组名junk(即,指向数组首元素的指针,首元素是子数组) - 每个函数都把ar视为内含数组元素(每个元素是内含4个int类型值的数组)的数组。 - ar和main()中的junk都使用数组表示法。因为ar和junk的类型相同。它们都是指向内含4个int类型值的数组的指针。 -------------- **划重点:** <u>不正确的声明</u> 注意,这种声明不正确: ``` int sum2(int ar[][], int rows); //错误的声明 ``` 因为编译器会把数组表示法转换成指针表示法。例如,编译器会把 ar[1] 转换成 ar+1。编译器对 ar+1 求值,要知道ar所指向的对象大小。 此声明: ``` int sum2(int ar[][4], int rows); //有效声明 ``` 表示ar指向一个内含4个int类型值的数组(在我的系统中,ar指向的对象占16字节),所以ar+1的意思是“该地址加上16字节”。如果第2对方括号是空的,编译器就不知道该怎样处理。 也可以在第1对方括号中写上大小,如下所示,但是编译器会忽略该值: ``` int sum2(int ar[3][4], int rows); //有效声明,但是3将被忽略 ``` ----------------  # 变长数组(VLA) 在处理二维数组的函数中,只把数组的行数(外层数组的大小)作为函数的形参,而列数(内层数组的大小)却内置在函数体内。例如,函数定义如下: ``` #define COLS 4 int sum2d(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; } ```  sum2d()函数之所以能处理这些数组,是因为这些数组的列数固定为4(也就是内层函数的大小),而行数被传递给形参rows,rows是一个变量。 但是如果要计算6*5的数组(即6行5列),就不能使用这个函数,必须重新创建一个COLS为5的函数。因为C规定,数组的维数必须是常量,不能用变量来替代COLS。(也就是说,写在方括号内的数字必须是符号常量或者常量数字)。 <u>创建能处理任意大小二维数组的函数</u> C99新增了变长数组(`variable-length array,VLA`),允许使用变量表示数组的维度。如下所示: ``` int quarters = 4; int regions = 5; double sales[regions][quarters]; //一个变长数组(VLA) ```  <u>必须先声明数组维度的变量</u>  <u>省略原型中的形参名</u>  <u>程序分析</u>  看起来,我的编译器不支持变长数组特性。 但是从上面的程序看出:**以变长数组作为形参的函数既可处理传统C数组,也可处理变长数组。** 如果编译器支持变长数组特性,该程序的输出应该是这样的:  <u>变长数组的其他特性</u>   # 复合字面量 `复合字面量`主要是为了解决数组没有等价的数组常量的问题。  <u>程序分析</u>  因为我的编译器支持C99,所以此程序可以运行。  # 关键概念  # 本章小结 
上一篇:
【C基础】课时11:通过看内存 debug
下一篇:
【C基础】课时9:多文件编译
0
赞
439 人读过
新浪微博
微信
腾讯微博
QQ空间
人人网
提交评论
立即登录
, 发表评论.
没有帐号?
立即注册
0
条评论
More...
文档导航
没有帐号? 立即注册