C 指针经过什么治疗仍然有效?

C 指针经过什么治疗仍然有效?,c,pointers,undefined-behavior,C,Pointers,Undefined Behavior,以下哪种处理和尝试恢复C指针的方法保证是有效的 1) 强制转换为无效指针并返回 int f(int *a) { void *b = a; a = b; return *a; } int f(int *a) { uintptr_t b = a; a = (int *)b; return *a; } 2) 转换为适当大小的整数并返回 int f(int *a) { void *b = a; a = b; return *a;

以下哪种处理和尝试恢复C指针的方法保证是有效的

1) 强制转换为无效指针并返回

int f(int *a) {
    void *b = a;
    a = b;
    return *a;
}
int f(int *a) {
    uintptr_t b = a;
    a = (int *)b;
    return *a;
}
2) 转换为适当大小的整数并返回

int f(int *a) {
    void *b = a;
    a = b;
    return *a;
}
int f(int *a) {
    uintptr_t b = a;
    a = (int *)b;
    return *a;
}
3) 几个简单的整数运算

int f(int *a) {
    uintptr_t b = a;
    b += 99;
    b -= 99;
    a = (int *)b;
    return *a;
}
4) 整数运算非常重要,足以掩盖出处,但仍将保持值不变

int f(int *a) {
    uintptr_t b = a;
    char s[32];
    // assume %lu is suitable
    sprintf(s, "%lu", b);
    b = strtoul(s);
    a = (int *)b;
    return *a;
}
int f(int *a) {
    uintptr_t b = a;
    for (uintptr_t i = 0;; i++)
        if (i == b) {
            a = (int *)i;
            return *a;
        }
}
5) 更多将保持值不变的间接整数操作

int f(int *a) {
    uintptr_t b = a;
    char s[32];
    // assume %lu is suitable
    sprintf(s, "%lu", b);
    b = strtoul(s);
    a = (int *)b;
    return *a;
}
int f(int *a) {
    uintptr_t b = a;
    for (uintptr_t i = 0;; i++)
        if (i == b) {
            a = (int *)i;
            return *a;
        }
}
显然,案例1是有效的,案例2肯定也是有效的。另一方面,我遇到了Chris Lattner的一篇帖子——不幸的是,我现在找不到——说类似于案例5的东西是无效的,标准允许编译器只将其编译成无限循环。然而,每一个案例看起来都是前一个案例无可非议的延伸

有效案例和无效案例之间的界限在哪里

根据评论中的讨论添加:虽然我仍然找不到激发案例5的帖子,但我不记得涉及到什么类型的指针;特别是,它可能是一个函数指针,这可能就是为什么该案例演示了无效代码,而我的案例5是有效代码的原因

第二个补充:好的,这里有另一个消息来源说有一个问题,我确实有一个链接关于指针出处的讨论-说,并用证据证明,不,如果编译器不知道指针来自何处,这是未定义的行为。

根据:

例1 有效,根据§6.5.16.1,即使没有明确的演员阵容

例2
intptr\u t
uintpr\u t
类型是可选的。将指针指定给整数需要显式强制转换(§6.5.16.1),尽管gcc和clang仅在您没有强制转换时才会发出警告。根据这些注意事项,往返转换在§7.20.1.4中有效ETA:约翰·贝林格(John Bellinger)指出,只有在对
void*
进行中间转换时,才会指定该行为。但是,gcc和clang都允许直接转换为文档化的扩展

例3 安全,但这仅仅是因为您使用的是无符号算术,它不会溢出,因此保证返回相同的对象表示。
intptr\u t
可能溢出!如果要安全地执行指针算术,可以将任何类型的指针转换为
char*
,然后在同一结构或数组中添加或减去偏移量。记住,
sizeof(char)
总是
1
ETA:标准保证两个指针的比较相等,但您与Chisnall等人的链接给出了编译器假定两个指针不相互别名的示例

例4 总是,总是,总是无论何时读取,尤其是写入缓冲区时,都要检查缓冲区溢出!如果你能通过静态分析从数学上证明溢出不会发生?然后写出明确证明这一点的假设,以及它们没有改变的
assert()
static\u assert()
。使用
snprintf()
,不要使用不推荐的、不安全的
sprintf()
!如果你不记得这个答案中的其他内容,请记住

要做到这一点,最好使用
中的格式说明符,并根据任何指针表示的最大值定义缓冲区长度。在现实世界中,您将使用
%p
格式打印指针

你想问的问题的答案是肯定的:重要的是你得到了相同的对象表示。下面是一个不那么做作的例子:

#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    int i = 1;
    const uintptr_t u = (uintptr_t)(void*)&i;
    uintptr_t v;

    memcpy( &v, &u, sizeof(v) );
    int* const p = (int*)(void*)v;

    assert(p == &i);
    *p = 2;
    printf( "%d = %d.\n", i, *p ); 

    return EXIT_SUCCESS;
}
#包括并建议C标准明确添加关于指针来源的语言。这将确定是否允许一致性实现假定由位操作创建的指针不会别名另一个指针

具体而言,建议的勘误表将引入指针来源,并允许不同来源的指针不进行相等的比较。它还将引入一个
-fno progence
选项,该选项将保证任何两个指针在且仅当它们具有相同的数字地址时比较相等。(如上所述,两个对象指针相互比较相同的别名。)

1) 强制转换为无效指针并返回

int f(int *a) {
    void *b = a;
    a = b;
    return *a;
}
int f(int *a) {
    uintptr_t b = a;
    a = (int *)b;
    return *a;
}
这将生成一个等于原始指针的有效指针。本标准第6.3.2.3/1段对此有明确规定:

指向void的指针可以转换为指向任何对象类型的指针,也可以转换为指向任何对象类型的指针。指向任何对象类型的指针可以转换为指向void的指针,然后再返回;结果应与原始指针进行比较


2) 转换为适当大小的整数并返回

int f(int *a) {
    void *b = a;
    a = b;
    return *a;
}
int f(int *a) {
    uintptr_t b = a;
    a = (int *)b;
    return *a;
}
3) 几个简单的整数运算

int f(int *a) {
    uintptr_t b = a;
    b += 99;
    b -= 99;
    a = (int *)b;
    return *a;
}
4) 整数运算非常重要,足以掩盖出处,但仍将保持值不变

int f(int *a) {
    uintptr_t b = a;
    char s[32];
    // assume %lu is suitable
    sprintf(s, "%lu", b);
    b = strtoul(s);
    a = (int *)b;
    return *a;
}
int f(int *a) {
    uintptr_t b = a;
    for (uintptr_t i = 0;; i++)
        if (i == b) {
            a = (int *)i;
            return *a;
        }
}
5) 更多将保持值不变的间接整数操作

int f(int *a) {
    uintptr_t b = a;
    char s[32];
    // assume %lu is suitable
    sprintf(s, "%lu", b);
    b = strtoul(s);
    a = (int *)b;
    return *a;
}
int f(int *a) {
    uintptr_t b = a;
    for (uintptr_t i = 0;; i++)
        if (i == b) {
            a = (int *)i;
            return *a;
        }
}
[…]显然案例1是有效的,案例2肯定也是有效的。另一方面,我遇到了Chris Lattner的一篇帖子——不幸的是,我现在找不到——说案例5是无效的,标准允许编译器将其编译成无限循环

在指针和整数之间进行转换时,C确实需要强制转换,并且在示例代码中省略了其中一些转换。从这个意义上讲,你的例子(2)-(5)都是不一致的,但对于这个答案的其余部分,我将假装需要的类型转换存在

尽管如此,由于非常迂腐,所有这些示例都有实现定义的行为,因此它们不是严格一致的。另一方面,“实现定义”行为仍然是定义行为;这是否意味着您的代码“有效”或“无效”