代码之家  ›  专栏  ›  技术社区  ›  Jared Forsyth appas

指针(地址)可以是负数吗?

  •  32
  • Jared Forsyth appas  · 技术社区  · 14 年前

    我有一个函数,我想能够返回特殊值 未初始化

    现在它回来了 NULL 失败,以及 -1

    [更新]

    我的另一个想法(如果-1有风险)是 malloc 一个字符 @

    12 回复  |  直到 4 年前
        1
  •  76
  •   caf    14 年前

    不,地址并不总是正的-在x86\u64上,指针是符号扩展的,地址空间是围绕0对称聚集的(尽管“负”地址通常是内核地址)。

    < > if (p < NULL) 没有明确定义的语义。

    您应该创建一个具有静态存储持续时间的虚拟对象,并将其地址用作 unintialised 价值:

    extern char uninit_sentinel;
    #define UNINITIALISED ((void *)&uninit_sentinel)
    

        2
  •  21
  •   James McNellis    14 年前

    指针的有效值完全依赖于实现,所以,是的,指针地址 要消极。

    但是,更重要的是,考虑一下(作为一个可能的实现选择的示例)您所处的32位平台具有32位指针大小的情况。任何可以由该32位值表示的值都可能是有效的指针。除了空指针,任何指针值都可能是指向对象的有效指针。

    对于您的特定用例,您应该考虑返回一个状态码,并可能将指针作为函数的参数。

        3
  •  17
  •   Prof. Falken    13 年前

    尝试将特殊值复合到返回值上通常是一个糟糕的设计。。。你试图用一个值做太多的事情。通过参数而不是返回值返回“success pointer”会更干净。对于您要描述的所有条件,在返回值中留下大量不冲突的空间:

    int SomeFunction(SomeType **p)
    {
        *p = NULL;
        if (/* check for uninitialized ... */)
            return UNINITIALIZED;
        if (/* check for failure ... */)
            return FAILURE;
    
        *p = yourValue;
        return SUCCESS;
    }
    

        4
  •  5
  •   AnT stands with Russia    14 年前

    C语言没有定义指针的“否定性”概念。“为负”的性质主要是算术性质,决不适用于指针类型的值。

    如果有指针返回函数,则不能有意义地返回 -1 -1 来自指针返回函数的是立即违反约束的情况,这将导致诊断消息。简而言之,这是一个错误。如果您的编译器允许,这意味着它不会过于严格地强制执行该约束(大多数情况下,他们这样做是为了与前标准代码兼容)。

    -1 若要通过显式转换为指针类型,则转换的结果将由实现定义。语言本身对此没有任何保证。它可能很容易被证明与其他一些有效的指针值相同。

    如果要创建保留指针值,则无需 malloc 什么都行。您可以简单地声明所需类型的全局变量,并将其地址用作保留值。它保证是独一无二的。

        5
  •  4
  •   jamesdlin    14 年前

        6
  •  1
  •   Ken Bloom    14 年前

    失败和单一化有什么区别。如果unitialized不是另一种失败,那么您可能需要重新设计接口来分离这两种情况。

    void* func();
    
    void* result=func();
    if (result==0)
      /* handle error */
    else if (result==-1)
      /* unitialized */
    else
      /* initialized */
    

    // sets the *a to the returned object
    // *a will be null if the object has not been initialized
    // returns true on success, false otherwise
    int func(void** a);
    
    void* result;
    if (func(&result)){
      /* handle error */
      return;
    }
    
    /*do real stuff now*/
    if (!result){
      /* initialize */
    }
    /* continue using the result now that it's been initialized */
    
        7
  •  0
  •   Randolpho    14 年前

    @当然,詹姆斯是对的,但我想补充一点,指针并不总是代表 绝对的 相对的 指向内存中某个点的地址,通常是堆栈或帧指针,这些地址可以是正的也可以是负的。

        8
  •  0
  •   Steve314    14 年前

    詹姆斯的回答可能是正确的,但当然描述了 实施 选择,不是你能做的选择。

    ~0 -1 ,对于相同的整数类型,请给出相同的值。如果直觉上没有签名, ~0 可能会使一个更直观的特殊情况值-我使用它的错误情况下无符号整数相当多。不是的 真正地 ~0 -1 与众不同。

    可以 -1 对于实际中的真正分配,极不可能出现指针。还有一些特定于平台的规则—例如,在32位Windows上,进程只能有2GB的地址空间,周围有许多代码将某种标志编码到指针的顶部位(例如,用于平衡二进制树中的平衡标志)。

        9
  •  0
  •   ruslik    14 年前

    int* x = NULL;
    x[10] = 1;
    

    现在考虑以下代码(可为AndreyT编译):

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    #define ERR_NOT_ENOUGH_MEM (int)NULL
    #define ERR_NEGATIVE       (int)NULL + 1
    #define ERR_NOT_DIGIT      (int)NULL + 2
    
    char* fn(int i){
        if (i < 0)
            return (char*)ERR_NEGATIVE;
        if (i >= 10)
            return (char*)ERR_NOT_DIGIT;
        char* rez = (char*)malloc(strlen("Hello World ")+sizeof(char)*2);
        if (rez)
            sprintf(rez, "Hello World %d", i);
        return rez;
    };
    
    int main(){
        char* rez = fn(3);
        switch((int)rez){
            case ERR_NOT_ENOUGH_MEM:    printf("Not enough memory!\n"); break;
            case ERR_NEGATIVE:          printf("The parameter was negative\n"); break;
            case ERR_NOT_DIGIT:         printf("The parameter is not a digit\n"); break;
            default:                    printf("we received %s\n", rez);
        };
        return 0;
    };
    

    这在某些情况下可能有用。 它不适用于哈佛的一些架构,但适用于冯诺依曼的架构。

        10
  •  0
  •   R.. GitHub STOP HELPING ICE    14 年前

    不要使用 malloc 例如,调用sentinel并将其分配到一个高地址),它会混淆内存调试器/泄漏检测器。相反,只需返回一个指向本地 static const char

        11
  •  0
  •   phuclv    4 年前

    您不需要关心指针的符号性,因为它是实现定义的。真正的问题是 “如何从返回指针的函数返回特殊值?” Pointer address span on various platforms

    malloc 家人永远不会回来-1。事实上,这个值甚至由许多Linux系统调用和win32api返回,以指示指针的另一个状态。所以如果你需要 失败 未初始化 那是个不错的选择

    但是通过利用变量必须正确对齐这一事实,可以返回更多的错误状态(除非指定了其他选项)。例如在指向 int32_t 低2位总是零,这意味着只有可能的值是有效的地址,剩下的所有位模式供您使用。所以一个简单的解决方案就是检查最低位

    int* result = func();
    if (!result)
        error_happened();
    else if ((uintptr_t)result & 1)
        uninitialized();
    

    您还可以使用高位在64位系统中存储数据。ARM上有一个标志,告诉CPU忽略地址中的高位。在x86上没有类似的东西,但您仍然可以使用这些位,只要在取消引用之前使其规范化。看到了吗 Using the extra 16 bits in 64-bit pointers

    另请参见

        12
  •  -1
  •   phuclv    5 年前

    NULL 是本例中唯一有效的错误返回,无论何时返回无符号值(如指针),都是如此。在某些情况下,指针的大小可能不足以将符号位用作数据位,但由于指针是由操作系统控制的,而不是由程序控制的,因此我不会依赖这种行为。

    请记住,指针基本上是一个32位的值;这是一个可能的负数还是总是正数只是一个解释问题(即32 位被解释为符号位或数据位。因此,如果将0xFFFFFFF解释为有符号数,它将是-1,如果将其解释为无符号数,它将是4294967295。从技术上讲,指针不大可能有这么大,但无论如何都应该考虑这种情况。

    作为替代,你可以使用一个额外的 无效的 但是,这将要求客户机创建并传递一个值,即使他们不需要区分特定的错误。

    GetLastError SetLastError

        13
  •  -1
  •   phuclv    5 年前

    正或负不是指针类型的有意义方面。它们与有符号整数有关,包括有符号字符、short、int等。

    人们在把指针的机器表示形式看作整数类型的情况下谈论负指针。例如 reinterpret_cast<intptr_t>(ptr)

    在某些情况下,我认为指针本质上是无符号的,我们用下面或上面的术语来讨论地址。 0xFFFF.FFFF 0x0AAAA.0000 0xFFFF.FFFF文件 实际上是“消极”的 0x0AAA.0000

    但在其他情况下,如指针减法 (ptr1 - ptr2) 这将产生一个类型为的有符号值 ptrdiff_t ,与整数的减法比较不一致, signed_int_a - signed_int_b 结果是有符号的int类型, unsigned_int_a - unsigned_int_b 生成无符号类型。但是对于指针减法,它会产生一个有符号类型,因为语义是两个指针之间的距离,单位是元素数。

    void * ):

    1. 列表项
    2. + +=

    3. - , -=

      ptr-任意\u整数\u类型

    4. ++ 前缀和后缀

    5. --

    注意没有 / * % 指针的操作。指针应该被视为独立类型,而不是“类似于int的类型”或“基础类型为int的类型,因此应该看起来像int”。