动态数组与字符串常量可算是两种“另类”数组。
VLA可变长数组并不为C89所支持,C99才开始支持VLA。但如果想在只支持C89的编译环境中使用VLA的话,怎么办呢?我们可以用动态数组来“模拟”,动态数组在矩阵的运算中很常见,常用来向函数传递一个大小可变的矩阵。动态数组的原理,是利用一块或多块动态分配的内存存储各维的首地址,这样就可以p[i][j]的形式访问数组的数据了。但是,动态数组并非真正的数组,它只是对数组的一种模拟。由于具有数组类型的数组名是系统行为,在用户这一级没法做到,因此只能以指针的形式存放首地址,sizeof(p)和sizeof(p[i])结果都是4字节。虽然动态数组是依靠动态分配内存来建立的,但动态的意义并非来自这里,而是指大小可变。笔者觉得用“动态数组”这个名称来命名非常适合,既不失大小可变的特征,又可以跟VLA可变长数组区分开来。
下面是建立动态数组的示例:
#include <stdio.h>
#include <stdlib.h>
void computedata(int *, int, int);
int main(void)
{
int iData[100], x, y;
do
{
printf("The product obtained by multiplying x and y must be
less than 100!");
printf("x=");
scanf("%d", &x);
printf("y=");
scanf("%d", &y);
}
while(x*y > 100);
computedata(iData, x, y);
return 0;
}
void computedata(int *ipSource, int iRow, int iColumn)
{
int
**ipTemp, i, j;
ipTemp = (int
**)malloc(iRow*sizeof(int*));
for(i=0; i<iRow; ++i) ipTemp[i] =
ipSource+i*iColumn;
for(i=0; i<iRow; ++i) for(j=0; j<iColumn;
++j) ipTemp[i][j] += 1;
free(ipTemp);
return;
}
以上示例把动态数组ipTemp的元素都加了1,由于只是示例,笔者省略了检测数据合法性的代码。iRow是第一维上界,iColumn是第二维上界,iData是源数据缓冲区,iRow*iColumn的积不能超过iData缓冲区的大小,否则就会越界了,但可以比它小。示例中iData被定义为一维数组,当然根据自己的需要也可以用其它类型的缓冲区代替,例如动态分配的一块内存,或者多维数组,如果是多维数组,例如三维数组int iData[10][10][10],调用computedata函数时,实参iData得做一些转换:
computedata((int *)iData, x, y);或者computedata(&iData[0][0][0], x, y);都可以。
ipSource指针用来传递缓冲区的首地址,这个指针由于要用来计算各维的地址,因此最好定义为一级指针,这样比较方便。ipTemp是一个二级指针,这是因为它指向的那块内存存放的是指针,这些指针指向各维的首地址,对ipTemp的元素来说,ipTemp就是二级的。最后记得free(ipTemp);
以上是定义一个二维动态数组的例子,多维动态数组的创建方法跟这个类似,下面给出三维动态数组的代码:
void computedata(int *ipSource, int iHigh, int iRow, int
iColumn)
{
int ***ipTemp, i, j, k;
ipTemp = (int
***)malloc(iHigh*sizeof(int**));
for(i=0; i<iHigh; ++i) ipTemp[i]
= (int **)malloc(iRow*sizeof(int*));
for(i=0; i<iHigh; ++i)
for(j=0; j<iRow; ++j) ipTemp[i][j] =
ipSource+i*iRow*iColumn+j*iColumn;
for(i=0; i<iHigh; ++i) for(j=0;
j<iRow; ++j) for(k=0; k<iColumn; ++k) ipTemp[i][j][k] += 1;
for(i=0; i<iHigh; ++i) free(ipTemp[i]);
free(ipTemp);
return;
}
下面来讨论字符串常量。
众所周知,C语言是没有字符串变量的,因而,C89规定,字符串常量就是一个字符数组。因此,尽管字符串常量的外部表现形式跟数组完全不同,但它的确是一个真正的数组,实际上,字符串常量本身就是这个数组的首地址,并且具有数组类型,对一个字符串常量进行sizeof运算,例如sizeof("abcdefghi"),结果是10,而不是4。字符串常量与一般数组的主要区别,是字符串常量存放在静态存储区,而一般数组(非static)则是在栈中静态分配的。由于字符串常量是数组首地址,因此可以数组引用的形式使用它,例如:
printf("%s", &"abcdefghi"[4]);
这将打印出字符串efghi。还可以这样:
printf("%s", "abcdefghi"+4);
同样打印出字符串efghi。实际上,&"abcdefghi"[4]等价于&*("abcdefghi"+4),去掉&*后,就是"abcdefghi"+4了。
我们可以利用字符串常量这些特性写出一些有趣的程序来,例如:
#include <stdio.h>
int iLine=1;
int main(void)
{
printf("%*s\n", 7-(iLine>4?iLine-4:4-iLine),
"*******"+2*(iLine>4?iLine-4:4-iLine));
if(++iLine != 8)
main();
return 0;
}
这个程序不使用任何数组形式的引用,不使用循环,就可以打印出用*号组合出来的菱形。当然,笔者并非鼓励大家编写这样的代码,但通过这样的例子加深对字符串常量的认识,仍然是非常重要的。
发表评论
-
《再再论指针》后记
2011-03-02 11:25 682在这篇后记中,笔者将对三个问题进行补充: 一、 ... -
再再论指针----篇首语
2011-03-02 11:24 588指针是C语言规范 ... -
第十章 围绕p()与(*p)()的争论
2011-03-02 11:23 727对于一个函数: void func(void) ... -
第九章 指针与const
2011-03-02 11:22 700const一词是英文constan ... -
第八章 右左法则----复杂指针解析
2011-03-02 11:22 669上一章费那么多唇舌讨论C语言的声明,其实目的都是为了这一 ... -
第七章 C语言声明详解
2011-03-02 11:21 703人们常说,C语言的 ... -
第五章 指向数组的指针
2011-03-02 11:19 1096讲到第五章了,数组两个字还离不开我们的左右,数组的内容 ... -
第四章 [ ]运算符的本质
2011-03-02 11:18 754数组是存在于人们头 ... -
第三章 数组的解剖学
2011-03-02 11:17 606这一章我们来讨论 ... -
第二章 再一次吃惊----数组的数组与多维数组的区别
2011-03-02 11:17 512看见这个题目,也许有些人就会嘀咕了:难道两者不是一样的 ... -
第一章 什么是数组名?----一个让你吃惊的事实!
2011-03-02 11:15 728数组是指针的基础,多数人就是从数组的学习开始指针的旅程的 ... -
图书推荐
2011-02-19 19:04 820图书收藏夹 ...
相关推荐
6. **编程实践**:在实际编程中,我们还需要考虑错误处理,例如验证输入是否有效,以及数组元素数量是否足够找出第K个最大/最小值。此外,为了提高效率,可以采用更高级的算法,比如二分查找或者快速选择。 7. **...
第6章 数组 6.1 一维数组 6.2 多维数组 6.3 字符串数组 第7章 面向对象和类的概念 7.1 面向对象程序设计基本概念 7.2 类 7.3 成员变量 7.4 方法 7.5 构造函数 第8章 类的继承性和多态性 8.1 类...
数组名实际上代表的是数组的起始地址,即第一个元素的地址。 - **数组名与指针的关系**:在C语言中,当我们将数组名传递给函数时,实际上传递的是该数组的地址。这意味着函数内部可以通过这个地址访问数组的内容。但...
4. **“另类”数组**:了解特殊类型的数组,如多维数组、字符数组等,以及如何正确使用它们。 #### 六、进阶指针应用 随着对指针理解的深入,我们可以进一步探索更高级的应用场景: 1. **C语言声明详解**:掌握...
#### 六、“另类”数组 有时候我们会遇到一些特殊的数组,比如空数组或者具有不确定大小的数组。这些特殊类型的数组在C语言中也有特定的应用场景。例如,使用`void f(int arr[])`定义函数时,传递的数组会退化为...
7. **算法和数据结构**:游戏设计中常见的数据结构包括数组、链表、树和图,以及排序、搜索等算法。它们对于游戏逻辑的实现至关重要。 8. **游戏循环**:所有游戏都有一个主循环,用于处理输入、更新游戏状态、渲染...
3. **播放MP3**:可以使用第三方库如VB6的`AxWindowsMediaPlayer`控件或者更底层的DirectShow API来播放MP3文件。因为这个项目未使用MediaPlayer,所以我们可能需要调用WinAPI函数或者第三方库来实现音频播放。 4. ...
接下来,我们将详细介绍一些JS中的小技巧,这些技巧有助于提升编程的幸福感,同时,还能帮助开发者识别和运用更多另类的写法。 首先,我们来谈谈类型强制转换。在JS中,类型转换是一种常见的操作,它允许我们改变...
6. **输出队列内容**:如果队列非空,则按顺序输出队列中的所有元素。 - ```cpp void OutPut(SqQueue&Q) { if (Q.front == Q.rear) cout 队列为空。" ; int k = Q.front; while (k != Q.rear) { if (k % 5 ==...
在JavaScript编程中,有时我们需要生成一个不重复的数字序列,...第一种方法更注重于数组操作,而第二种方法利用了对象属性的特性,避免了数组中元素的重复。在实际应用中,可以根据具体需求和性能要求选择合适的方法。
第6章 日期和时间 121 0185 获得系统当前时间 122 0186 获得系统当前日期 122 0187 将日期时间格式化为指定格式 122 0188 计算两个日期之间相差的天数 123 0189 根据指定日期返回星期几 123 0190 将...
第6章 日期和时间 121 0185 获得系统当前时间 122 0186 获得系统当前日期 122 0187 将日期时间格式化为指定格式 122 0188 计算两个日期之间相差的天数 123 0189 根据指定日期返回星期几 123 0190 将...
首先需要初始化最大值为数组的第一个元素,然后遍历数组,如果当前元素大于最大值,则更新最大值。填空部分为: ```c max = a[0]; if(a[i] > max) max = a[i]; ``` 3. 对三个数进行排序(升序): 这是一个...
6. **数组**:了解一维和多维数组的创建和操作。 7. **异常处理**:学习使用try-catch块处理程序运行时可能出现的错误。 8. **输入输出**:掌握使用Scanner类进行用户输入,以及使用FileReader、FileWriter等进行...
【标签】"另类其它"可能指的是这个项目在功能或者设计上具有一定的独特性,可能不同于常规的留言本。"控件"可能是指页面上的交互元素,如输入框、提交按钮等,这些是PHP和HTML结合实现用户交互的关键部分。"源码...
2. **数组和循环**:创建二维数组来存储一个月的每一天,然后用循环遍历这个数组,填充日期并格式化输出。 3. **字符串格式化**:利用`Format`函数或`String.Format`方法(在VB.NET中)来格式化日期字符串,使其...
- 创建第二个`ByteArrayOutputStream`,同样添加数据。 - 创建一个新流,合并两个已有的流。 - 创建`InputStream`,从合并后的流中读取数据。 - 最后,可能将内存流的内容写入文件或显示在控制台。 8. **源码和...