【问题标题】:How to evaluate double pointers in c?如何评估c中的双指针?
【发布时间】:2019-03-31 16:47:02
【问题描述】:

我正在学习c,我对这个双指针问题感到很困惑。

int x = 44;
int *p = &x;
int **t = &p;

bool a = (*t = &x);

我需要判断a是否会是true or false,正确答案是true。我的想法是t指向p的地址,p指向x的地址。我知道如果你放**t,它应该指向x的地址,但我想如果你只是放*t,它应该指向p的地址。谁能解释一下?

【问题讨论】:

  • (*t == &x)。其余的都是正确的。
  • 我认为您在 *t = &x 中的意思是“==”(双等于)而不是“=”(单等于)
  • 如果您单击“双指针”标签,您会发现这是一个令人困惑的术语。它既可以引用指向指针的指针,也可以引用指向double 类型的指针。

标签: c pointers boolean pointer-to-pointer


【解决方案1】:
int x = 44;

声明整数变量 x,它存储的值是 44。

int *p = &x;

声明名为p的整数指针变量,p现在存储x的地址。

int **t = &p;

声明指向称为 t 的 int 类型指针的指针,t 存储 p 的地址。 (指针也有地址)

bool a = (*t = &x);

在 C 中;

'*' = 从地址中提取值(取消引用)

'&' = 给出变量地址(参考)

因为t 是指向存储在p 中的值的指针。 *t 将是存储在p 中的值,即x 的地址。 (我们在第二行发现了这一点)

另一方面,因为& 用于变量x。这将提取x 的地址。

因此*t == &x,它将布尔值a设置为true。

【讨论】:

  • "**t 是指向存储在 p 中的值的指针" 不,它不是。 t 是一个指针。 *t 是一个指针。 **tint
  • 如果你没有右对齐你的星号,你可能不会那么困惑。 int* p = &xint** t = &p 现在更清楚了。
  • 是的,你是对的。意思是说 **t 声明了一个指向存储在 p 中的值的指针
  • @LightnessRacesinOrbit 或者如果您确实右对齐,并且将其读取为 **t 被声明为int.
  • @AnttiHaapala 这令人困惑,因为该行声明了t,一个指针,而不是int(它可能存在也可能不存在,也可能在其他地方被声明过)。因此,由于这种奇怪的做法,OP 感到困惑。
【解决方案2】:

我知道如果你放**t,它应该指向x的地址,但我想如果你只是放*t,它应该指向p的地址。

嗯,这是对的,也是错的。 “指向的地址”应该写成“指定”,没错。


首先,你声明了 3 个变量

int x = 42;
int *p = &x;
int **t = &p;

现在这可以读作x is an int, *p is an int and **t is强>一个int。然后我们将x初始化为42,然后将p初始化为x的地址,将t初始化为p的地址。

给定

int *p = &x;

这两个L-value expressionsx*p不仅都是int类型,而且具有相同的值;他们指定 相同的对象。就像 10565305 和 Amy Finck 指定同一个用户一样。这也称为 aliasing - *p aliases x,就像 Amy Fink 和 User 10565305 是您在 Stack Overflow 上的别名(并且名称 x 不是对象本身,就像您一样'是一个人,而不是一个名字)。

int **t = &p 一样,我们现在注意到就像上面一样,*tp 指定相同的对象p。而ps 的当前值是表达式&x 的值。因此*t == &x 必须为真。

至于*ts 类型,如果**tint,那么*t 必须是int *(指针中的恒星守恒定律;)当然匹配@987654358 的类型@,以及&x 的类型。

【讨论】:

    【解决方案3】:

    它将是true。解释如下:

    bool a = (*t = &x); 中,变量t 保存p 的地址。您正在取消引用t,即*t 将指向存储在p 位置的内容。

    p地址中存储的内容为x地址。记住*p = &x。比较 *t == &x 将是真的。

    在您提到的*t = &x 的问题中,如果那是原始代码,它仍然是正确的,因为(*t = &x); 中的赋值返回一个非空指针。当该非空指针转换为布尔值时,它变为true

    【讨论】:

    • bool a = (*t = &x); 中的赋值返回一个非空指针(不正确)。当该非空指针转换为布尔值时,它变为true;只有一个空指针会变成false。语义最终大致相同,但详细步骤与“赋值将返回 1 (true)”略有不同。
    • 感谢您的纠正,我会相应地更新我的答案。
    • 我的问题出在你的第三段——因为p 即将通过*t 分配给,所以存储在p 中的当前值在很大程度上无关紧要(尽管它恰好是@ 987654341@分配前)。然后你说“比较*t == &x 将是真的”。而且我不确定您是否认为分配是相等的,或者您假设在 bool 行之前进行测试,或者在之后进行测试。由于&x 被分配给*tp(又名*t)中的值不会改变;之前和之后都是&x。你的第三段说“你提到了*t = &x,这似乎低估了它。
    • 这不是完全错误,但我认为您没有说清楚。也许,如果您扩展int y = 99; 提供的代码并使用int *p = &y; 可能会更好,然后您可以在bool 行之前打印各种值和地址,然后解释bool 行中发生的情况,然后打印各种值和地址。我只是发现自己不确定我是否理解你是如何解释正在发生的事情的。 “是的”,做出明确的答案是一项艰苦的工作。我一直在与它斗争!
    【解决方案4】:

    第一行声明了一个类型为“int”的变量“x”,并将数值文字值 44 分配给该变量。

    int x = 44;
    

    下一行(2)声明了一个'pointer to int'类型的变量'p',并赋予表达式'&x'的值(即x的地址,一个指向int的指针),

    int *p = &x;
    

    下一行 (3) 声明了一个类型为 'pointer to, pointer to int' 的变量 't'(一个包含指向 int 类型的指针的指针),并分配表达式 '&p'(即地址p,一个指向 int 的指针,

    int **t = &p;
    

    变量 t 是一个“双向间接指针”。 现在是“棘手的部分”。

    下一行 (4) 声明了一个变量 'a',它是一个 bool(ean) 变量 (true/false),并在 rhs 上分配表达式的值(右侧,'(*t = &x )'。括号是为了确保遵守预期的优先顺序。所以我们计算表达式'*t = &x'。

    bool a = (*t = &x);
    

    这里是“棘手”的部分,提供的代码有一个赋值“=”运算符!如果这是比较,那么会出现“==”运算符。所以这条语句获取x的地址(指向int的指针),并将其存储到*t(t指向的位置,该位置是指向int的指针)。

    如果是这种比较,则表达式将比较相同的值,并且为真。但这是任务。并且赋值表达式返回赋值的值,即 x 的地址(指向 x 的指针)。在大多数系统上,该指针是内存中的一个位置(如果变量存储在零地址,则只能为零(NULL))。

    表达式的类型为“指向 int 的指针”,但被转换(强制转换)为 bool 类型。由于 C 认为 false=0 和 true 任何不等于 0 的值,该值从(可能不是 NULL)指针转换为 int 到 bool (true)。

    如果括号被省略,那么赋值仍然会发生(赋值运算符的评估顺序从右到左),但随后会发生强制转换。但是去掉括号,编译它,看看你的编译器是怎么说的。

    【讨论】:

    • 请注意,任何变量都不能有一个地址,使得它的地址比较等于空指针; C 要求这样做。 C11 §6.3.2.3 Pointers ¶3: 一个值为 0 的整数常量表达式,或这种类型转换为 void * 的表达式,称为空指针常量。66) 如果将空指针常量转换为指针类型,则结果指针,称为空指针,保证与指向任何对象或函数的指针不相等。(脚注 66 提到 NULL 是一个定义为空指针常量的宏)。
    • @JonathanLeffler 我认为一些嵌入式系统使用 0x00 来存储东西,也许是引导加载程序代码或其他东西?我使用旧的 C 89 并且不再处理嵌入式内容,但是如何使用您添加的 NULL 指针注释访问内存中的 0x00?
    • 有些微处理器将重启地址存储在地址 0 或附近。但这超出了标准 C 的范围——不允许标准 C 编译器将变量放置在可能与空指针混淆的地址处。另请参阅 C 常见问题解答和有关该主题的各种其他来源。基本上,您永远不能让&x 为任何变量(对象)x 生成空指针。因此,“该值从(绝对不是 NULL)指向 int 的指针转换为 booltrue 或 1”。上一段也是如此。
    • 如果以int(比如)访问位置0 是合法的,那么您将使用int *ip = 0; int val0 = *ip; *ip = 42;。但是,在大多数现代(非嵌入式)系统上,这几乎可以保证分段错误和核心转储。如果重新启动地址位于位置 0,您可以写:void (*restart)(void) = 0; (*restart)(); // Or: restart(); 以跳转到位置 0。但是&ip&restart 都不会返回空指针。 “几乎可以保证的分段错误”的例外很少,以至于很容易放弃“几乎”;这条规则几乎没有例外。
    • “括号是为了确保遵守预期的优先顺序”在这种情况下完全没有必要。
    【解决方案5】:

    你说:

    我需要判断 a 是真还是假,正确答案是真的。我的想法是t指向p的地址,p指向x的地址。 我知道如果你放**t,它应该指向x的地址,但我想如果你只是放*t 它应该指向p的地址。谁能解释一下?

    这是我的尝试和解释:

    粗体部分是您感到困惑的地方。 **t == x == *p,和 *t == &x == p。

    **t 引用存储在 x 地址中的值,而不是地址本身。

    *t =p 引用 x 的地址,解引用其中任何一个都会得到 x 的值。

    这个解释清楚了吗?如果有帮助,我可以扩展或制作一个示例程序。

    示例暂存代码将打印这些值以获得清晰:

    int x = 42;   //&x = 0x1234DEAD (or some other address, just picked one for an example)
    int * p = &x; // p = 0x1234DEAD and &p = 0x5678BEEF  (agian an example address)
    int **t = &p; // t = 0x5678BEEF
    
    printf("%d and %d and %d\n",**t, *p, x); //should be 42;
    printf("%p and %p\n",*t, p); //should be 0x1234DEAD;
    printf("%p\n",t); //should be 0x5678BEEF;
    

    【讨论】:

      【解决方案6】:

      我的想法是 t 指向 p 的地址

      是的。

      而p指向x的地址

      是的。

      我知道如果你放**t,它应该指向x的地址

      不,**t x

      但我想如果你只是放 *t 它应该指向 p 的地址。

      不,*t p

      解引用操作符“给你”指向的东西。因此,因为p 也是&x(因为这是你设置的),*t == &x

      这没有实际意义,因为您实际上并未与 == 进行比较,而是分配了 =,因此您的表达式计算为该分配的结果,即 &x,它是一个非 NULL 指针,所以整个事情都是“真实的”。 (只有空指针转换为false)。

      【讨论】:

        猜你喜欢
        • 2016-06-05
        • 2015-10-09
        • 2020-09-26
        • 1970-01-01
        • 1970-01-01
        • 2019-08-01
        • 1970-01-01
        • 2021-05-29
        • 2019-10-29
        相关资源
        最近更新 更多