我可以减去或比较受限制的指针吗?
Can I subtract or compare restricted pointers?
问题:
如果我有两个指针(本质上是一个begin
和一个end
)用restrict
限定。 begin
指针用于 dereferencing/reading,而 end
指针是一个永远不会取消引用的尾数指针,仅用于检查范围的大小(通过 end - begin
)。一旦范围被消耗,我希望 begin
和 end
相等并且 end - begin
为 0,尽管此时这两个指针永远不会被取消引用。
鉴于 restrict
对指针的限制,减去和比较这两个指针是否是明确定义的行为?
MVCE:
我有一些代码如下:
#include <stddef.h>
struct Reader {
const char* restrict data;
size_t size;
};
char read_char(struct Reader* reader) {
--reader->size;
return *reader->data++;
}
int main(int argc, char* argv[]) {
struct Reader reader = {
.data = argv[1],
.size = argv[1] ? strlen(argv[1]) : 0,
};
if (reader.size > 0) {
return read_char(&reader);
}
return 0;
}
我想修改一下,这样阅读时就不用同时修改data
和size
,只需要修改data
:
#include <stddef.h>
struct Reader {
const char* restrict data;
const char* restrict end; // Not sure if this should be restrict or not.
};
char read_char(struct Reader* reader) {
return *reader->data++;
}
int main(int argc, char* argv[]) {
struct Reader reader = {
.data = argv[1],
.end = argv[1] + (argv[1] ? strlen(argv[1]) : 0),
};
if (reader.end - reader.data > 0) { // Is this okay?
return read_char(&reader);
}
return 0;
}
考虑到 restrict
对指针的限制,这是允许的吗?
TL;DR:根据我对标准的阅读,您的使用是允许的。
标准对使用 restrict
限定指针的要求都与别名和对指向对象的访问有关。我发现使用这样的指针作为指针差分运算符(-
)的操作数没有任何限制,并且这样的限制不符合restrict
的目的(这是为了提供更大的优化机会)。
相比之下,通过添加整数从 restrict
限定的指针获得的指针值是 "based on" 在某种意义上对标准很重要的原始指针。粗略地说,通过这样的指针访问指向的对象是可以接受的,而通过指向不是 "based on" 受限指针的同一对象的指针访问该对象是不可接受的。
此外,我认为 Reader.end
不需要 restrict
资格,而且我认为这样的资格对您根本没有帮助。但是,您必须确保 Reader.end
或派生自它的任何指针均未用于访问数据;为此,您只能使用 Reader.data
。在 main()
中也是如此。
另一方面,如果您无处修改指向的对象,几乎所有这些都没有实际意义。
标准或基本原理中没有任何内容表明有任何意图禁止对 restrict 限定指针进行比较或计算。然而,标准定义一个指针是 "based upon" 另一个指针的概念的方式与比较奇怪地相互作用。例如,给定如下内容:
int test(int *restrict p, int *q, int i)
{
p[i] = 1;
if (p+1 == q)
p[1] = 2;
return p[i];
}
用指向已识别数组副本的指针替换 p
会阻止对 p[1]
的赋值,从而无法回答此类替换是否会影响该赋值中使用的地址的问题。虽然 p[1]
基于 p
看起来很明显,而且比较会影响它似乎很荒谬,但 clang 和 gcc 都不认为上面的左值 p[1]
是基于与左值 p[i]
.
相同 p
问题:
如果我有两个指针(本质上是一个begin
和一个end
)用restrict
限定。 begin
指针用于 dereferencing/reading,而 end
指针是一个永远不会取消引用的尾数指针,仅用于检查范围的大小(通过 end - begin
)。一旦范围被消耗,我希望 begin
和 end
相等并且 end - begin
为 0,尽管此时这两个指针永远不会被取消引用。
鉴于 restrict
对指针的限制,减去和比较这两个指针是否是明确定义的行为?
MVCE:
我有一些代码如下:
#include <stddef.h>
struct Reader {
const char* restrict data;
size_t size;
};
char read_char(struct Reader* reader) {
--reader->size;
return *reader->data++;
}
int main(int argc, char* argv[]) {
struct Reader reader = {
.data = argv[1],
.size = argv[1] ? strlen(argv[1]) : 0,
};
if (reader.size > 0) {
return read_char(&reader);
}
return 0;
}
我想修改一下,这样阅读时就不用同时修改data
和size
,只需要修改data
:
#include <stddef.h>
struct Reader {
const char* restrict data;
const char* restrict end; // Not sure if this should be restrict or not.
};
char read_char(struct Reader* reader) {
return *reader->data++;
}
int main(int argc, char* argv[]) {
struct Reader reader = {
.data = argv[1],
.end = argv[1] + (argv[1] ? strlen(argv[1]) : 0),
};
if (reader.end - reader.data > 0) { // Is this okay?
return read_char(&reader);
}
return 0;
}
考虑到 restrict
对指针的限制,这是允许的吗?
TL;DR:根据我对标准的阅读,您的使用是允许的。
标准对使用 restrict
限定指针的要求都与别名和对指向对象的访问有关。我发现使用这样的指针作为指针差分运算符(-
)的操作数没有任何限制,并且这样的限制不符合restrict
的目的(这是为了提供更大的优化机会)。
相比之下,通过添加整数从 restrict
限定的指针获得的指针值是 "based on" 在某种意义上对标准很重要的原始指针。粗略地说,通过这样的指针访问指向的对象是可以接受的,而通过指向不是 "based on" 受限指针的同一对象的指针访问该对象是不可接受的。
此外,我认为 Reader.end
不需要 restrict
资格,而且我认为这样的资格对您根本没有帮助。但是,您必须确保 Reader.end
或派生自它的任何指针均未用于访问数据;为此,您只能使用 Reader.data
。在 main()
中也是如此。
另一方面,如果您无处修改指向的对象,几乎所有这些都没有实际意义。
标准或基本原理中没有任何内容表明有任何意图禁止对 restrict 限定指针进行比较或计算。然而,标准定义一个指针是 "based upon" 另一个指针的概念的方式与比较奇怪地相互作用。例如,给定如下内容:
int test(int *restrict p, int *q, int i)
{
p[i] = 1;
if (p+1 == q)
p[1] = 2;
return p[i];
}
用指向已识别数组副本的指针替换 p
会阻止对 p[1]
的赋值,从而无法回答此类替换是否会影响该赋值中使用的地址的问题。虽然 p[1]
基于 p
看起来很明显,而且比较会影响它似乎很荒谬,但 clang 和 gcc 都不认为上面的左值 p[1]
是基于与左值 p[i]
.
p