使用指针表示法比数组表示法有什么好处吗?我意识到,在某些特殊情况下,指针表示法更好,但在我看来,数组表示法更清晰。我的教授告诉我们,他更喜欢指针符号“因为它是C”,但这不是他将要标记的东西。我知道,将字符串声明为字符数组和将指针声明为字符串是有区别的——我只是在一般情况下在数组中循环。
最佳答案
如果您编写一个简单的循环,那么数组和指针窗体通常都编译成相同的机器代码。
特别是非常量循环退出条件存在差异,但只有当您试图优化特定编译器和体系结构的循环时才有意义。
那么,我们考虑一个依赖于两者的现实世界的例子怎么样?
这些类型实现动态确定大小的双精度浮点矩阵,并具有单独的引用计数数据存储:
struct owner {
long refcount;
size_t size;
double data[]; /* C99 flexible array member */
};
struct matrix {
long rows;
long cols;
long rowstep;
long colstep;
double *origin;
struct owner *owner;
};
其思想是,当需要矩阵时,可以使用
struct matrix
类型的局部变量来描述它。在C99灵活数组成员中,所有引用的数据都存储在动态分配的struct owner
结构中。在您不再需要矩阵之后,必须显式地“删除”它。这允许多个矩阵引用同一数据:您甚至可以有单独的行、列或对角线向量,对其中一个的任何更改都会立即反映在所有其他向量中(因为它们引用相同的数据值)。当一个矩阵与数据相关时,通过创建一个空矩阵,或者通过引用另一个矩阵所引用的现有数据,就可以增加所有者结构。每当删除矩阵时,引用的所有者结构refcount都会减少。当refcount降至零时,所有者结构将被释放。这意味着您只需要记住“删除”您使用的每个矩阵,并且所引用的数据将被正确地管理并尽快发布(不需要),但决不能太早。
这一切都假设一个单线程进程;多线程处理相当复杂。
要访问矩阵
struct matrix m
、行r
、列c
中的元素,假设0 <= r < m.rows
和0 <= c < m.cols
,可以使用m.origin[r*m.rowstep + c*m.colstep]
。如果您想转置矩阵,只需交换
m.rows
和m.cols
,以及m.rowstep
和m.colstep
。所有这些变化,就是数据(存储在所有者结构中)的读取顺序。(注意
origin
指向矩阵中第0行第0列处出现的双精度值,rowstep
和colstep
可以为负。这允许对原本单调的常规数据(如镜子和对角线等)进行各种奇怪的“查看”。)如果我们没有C99灵活数组成员——比如,我们只有指针,而根本没有数组符号——所有者结构
data
成员必须是指针。这将意味着在硬件级别上有一个额外的重定向(将数据访问速度减慢一点)。我们要么需要分别分配data
所指向的内存,要么使用技巧指向所有者结构本身后面的地址,但要适当地对齐成double。多维数组确实有它们的用途——基本上,当所有维度的尺寸(或者所有维度都是一维的)已知时,编译器可以很好地处理索引,但并不意味着它们总是比使用指针的方法更容易。例如,在上述矩阵结构的情况下,我们总是可以定义一些辅助预处理器宏,比如
#define MATRIXELEM(m, r, c) ((m).origin[(r)*(m).rowstep + (c)*(m).colstep])
不可否认,它的缺点是对第一个参数
m
求值三次。(这意味着MATRIXELEM(m++,0,0)
实际上会尝试增加m
三次。)在这种特殊情况下,m
通常是struct matrix
类型的局部变量,这会尽量减少意外。一个人可以有。struct matrix m1, m2;
/* Stuff that initializes m1 and m2, and makes sure they point
to valid matrix data */
MATRIXELEM(m1, 0, 0) = MATRIXELEM(m2, 0, 0);
这些宏中的“extra”括号确保如果使用计算(例如
i + 4*j
作为行),索引计算是正确的((i + 4*j)*m.rowstep
而不是i + 4*j*m.rowstep
)。在预处理器宏中,这些括号根本不是“额外的”。除了确保正确的计算,使用“额外”括号还可以告诉其他程序员,宏编写器在避免此类与算术相关的错误时非常小心。(我认为把括号放在那里是“好形式”,即使在语法不明确不需要括号的情况下,如果它向阅读代码的其他开发人员传达了这种“保证”。)在所有这些文字之后,这引出了我最重要的一点:有些东西比指针符号更容易被我们人类程序员表达和理解,反之亦然。
"Foo"[1]
相当明显等于'o'
,而*("Foo"+1)
则不那么明显。(同样,两者都不是1["foo"]
,但您可以将此归咎于c标准化人员。)根据上面的例子,我认为这两个符号是互补的;它们确实有很大的重叠,特别是在简单的循环中——在这种情况下,只选择一个是可以的——但是能够利用这两个符号并选择一个并不是基于一个人的熟练程度,而是基于一个人对什么是最有意义的WRT的看法。可读性和可维护性,在我看来,对任何C程序员来说都是一项重要的技能。
关于c - 数组符号与指针符号C,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41915930/