令人困惑的调整代码以使用限制限定符

标签 c c99 strict-aliasing restrict-qualifier

我正在尝试调整以下版本的 stpcpy 函数,以在内部使用 restrict 限定指针作为其参数,但我不确定是否只是简单添加限定符会导致引入未定义的行为。

#define ALIGN (sizeof(size_t)-1)
#define ONES ((size_t)-1/UCHAR_MAX)
#define HIGHS (ONES * (UCHAR_MAX/2+1))
#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)

char *__stpcpy(char *d, const char *s)
{
        size_t *wd;
        const size_t *ws;

        if (((uintptr_t)s & ALIGN) == ((uintptr_t)d & ALIGN)) {
                for (; (*d=*s) && ((uintptr_t)s & ALIGN); s++, d++);
                if (!*s) return d;
                wd=(void *)d; ws=(const void *)s;
                for (; !HASZERO(*ws); *wd++ = *ws++);
                d=(void *)wd; s=(const void *)ws;
        }
        for (; (*d=*s); s++, d++);

        return d;
}

假设 C99 6.7.3.1 中有关访问对象的规则仅适用于访问的单个对象而不是整个数组,我认为这可能没问题,因为写入的元素仅访问一次,并且仅用于写入。但此时我对使用 restrict 感到相当不舒服,并且不想仅依赖我自己的判断。

最佳答案

为了符合标准,唯一的限制是操作函数通过 restrict 指针接收到的任何对象的所有指针表达式都应该基于该指针。它不要求指针表达式具有相同的类型。因此,从这个意义上说,通过这些 size_t* 访问对象并不是约束违规或 UB。

我不确定用 *d 读取您通过 *wd 修改的对象的一部分的值是否会读取没有别名,因为指针类型不同。但正如你所说,你不会这样做,所以这应该是安全的。

顺便说一句,代码做了一个不一定可移植的重要假设,即 uintptr_t 的低位反射(reflect)了转换为它的指针的对齐属性。这对于我们日常使用中遇到的所有架构来说可能都是如此,但标准并不能保证。即使 C11 也只说了一些关于“字节地址的倍数”的内容,但没有任何内容指定它是什么。

关于令人困惑的调整代码以使用限制限定符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12255710/

相关文章:

c - C 中函数 typedef 的前向声明

c - 函数的隐式声明 ‘str[n]casecmp’ [-Werror=implicit-function-declaration]

c++ - `u8string_view` 到 `char` 数组而不违反严格别名?

rust - 读取或写入整个 32 位字,即使我们只引用其中的一部分,是否会导致未定义的行为?

c++ - 如何通过不同类型重新解释数据? (类型双关困惑)

c - 矩阵作为C中的双指针,如何移动它

C 中的复合语句表达式

c - C99 标准中的默认参数提升

c - 使用 memcpy 丢弃限定符

c - 使用 Union 中存在的函数指针执行 shell 代码