【问题标题】:Why is it better to use cast when you assign an integer to a pointer?为什么在将整数分配给指针时使用强制转换更好?
【发布时间】:2014-07-29 14:55:49
【问题描述】:

假设我有这个代码:

  char *pointer;
  unsigned int a;
  pointer = a;

对我来说这不会产生任何问题,但我会收到以下警告:

assignment makes pointer from integer without a cast

为什么使用强制转换更好? 我使用的是 32 位机器。

编辑: 通过“这不会产生任何问题”,我是说它编译并指针存储变量a的值。

编辑: 而变量a 实际上会存储一个地址。

【问题讨论】:

  • 将一种类型的指针存储到另一种类型的指针很奇怪,因此添加演员表可以清楚地表明这就是您的意图。
  • 你能说出你到底想做什么吗?为什么需要将a 分配到pointer 中?
  • 你应该通过扩展“对我来说这不会产生任何问题”的意思来澄清你的问题。
  • 这并没有真正改变任何事情。
  • @StoicaDan:正如我所提到的,我在一台具有 32 位 ints 和 64 位指针的机器上键入这个,这很常见。在这种情况下,您不能在不丢失信息的情况下将指针存储在 int 中。有整数类型intptr_tuintptr_t,在<stdint.h> 中声明,它们可以存储转换后的指针(如果这些类型存在)。我没有必要删除这个问题;一些答案中有一些很好的信息。

标签: c pointers casting


【解决方案1】:

最好不要尝试将整数值分配给指针。在这种分配有意义的极少数情况下,演员表不仅更好,而且是必需的。

指针和整数是不同的类型。在某些情况下,混合它们是有意义的;在这种情况下,您需要进行强制转换,因为整数和指针之间没有隐式转换(0 的特殊情况除外,被视为空指针常量)。

char *pointer;
int a;         /* or unsigned int a, it's the same either way */
pointer = a;

此分配是非法的(更准确地说,是违反约束)。一个符合标准的 C 编译器至少必须警告它。许多编译器在打印警告后会生成执行隐式转换的代码,但这不是语言所要求的,你不应该依赖它。我个人的看法是,这样做的编译器对你没有任何好处。

pointer = (char*)a;

这是合法的;演员表告诉编译器生成从intchar* 的转换。但这种转换的结果是实现定义的,很可能不会产生有意义的结果——即使char*int 恰好大小相同。

(在您问题中的代码中,a 尚未初始化。我认为您未向我们展示的真实代码中并非如此。)

你真正想要完成什么?

【讨论】:

  • 其实我是在测试别人的代码。他希望能够从键盘输入地址并将其存储到指针中。
  • @StoicaDan:这样做几乎没有意义;这就是为什么该语言不允许您直接执行此操作的原因。在少数有意义的情况下,您可以使用演员表。演员实际上说,“闭嘴,编译器,我真的知道我在做什么”。这段代码如何处理结果指针?
  • @StoicaDan:对于您想从用户那里获取地址的极少数情况,您可能需要使用sscanf%p
  • @mafso:好主意,但它需要用户知道sscanf 识别的实现定义格式。 %p 转换的行为在“输入项是在同一程序执行期间较早转换的值”时定义 *(N1570 7.21.6.2);否则行为未定义。没有可移植的方法来检测或处理输入错误。
  • 是的,我应该提到风险。但是我想不出在可移植代码中使用整数到指针的转换,所以这样做时你已经坚持了某个平台。对于%p,格式至少与printf 相同。在使用整数类型时,您在输入检查方面也面临同样的问题。无论如何,我只是觉得 OP 可能对%psscanf 感兴趣。
【解决方案2】:

因为在 C 中:

pointer = a;  // not valid in C: a is an int object
              // pointer an object of pointer type

该语句无效,您需要显式转换(强制转换)将a 值转换为指针类型。

pointer = (char *) a;  // valid assignmnent

有些编译器对你很友好,会将int 值隐式转换为指针类型(并添加警告!),但它们不是必须这样做的,编译器可以拒绝编译你的程序。

【讨论】:

  • 按标准来说答案不错,但“客观”地看待它,就像你的孩子问你“为什么不允许它”,而你回答“因为它被禁止”。
  • @MatteoItalia 好吧,不,在 C 中,为什么?
  • @barakmanos 警告背后的原因是赋值运算符存在约束违规,并且约束违规至少需要来自编译器的诊断消息。
  • 我明白这一点,但这里的人们似乎经常试图“坚持”他们对标准所说的什么而不是为什么的回答它说。有时确实没有理由解释或详细说明。但在这种情况下,问题出处的基本逻辑表明 OP 不了解此(不安全)分配所带来的风险。
  • @barakmanos “两种类型在每个平台上的大小不一定相同” 我认为这不是标准中分配约束的基本原理。看,您可以将任何不同大小的整数类型分配给任何其他大小的整数类型(例如从unsigned long longunsigned char)。在这种情况下,一种类型是整数类型,另一种类型是指针类型。 C 可能禁止赋值,因为 C 是一种类型语言,类型语言倾向于限制隐式转换的数量。
【解决方案3】:
% cat int-cast.c

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    printf("sizeof char *: %zu\n", sizeof (char *));
    printf("sizeof int: %zu\n", sizeof (int));
}

那么如果编译为 32 位或 64 位,则类型的大小不同

% cc -m32 int-cast.c && ./a.out sizeof char *: 4 sizeof int: 4 % cc -m64 int-cast.c && ./a.out sizeof char *: 8 sizeof int: 4

正如 Keith Thompson 明确指出的那样,即使保证它们的大小相同,仍然需要强制转换,并且结果最多只能由实现定义。

【讨论】:

  • 这两种类型可能有不同的大小,好的,但是使用强制转换将如何影响分配行为? (这是 OP 的实际问题。)
  • 我只是展示了编译器需要显式转换的一个原因 - 我认为下面的其他答案(包括你的)可以更好地解释转换(以及为什么它可能不是一个好的想法)。
  • 这不能回答问题。赋值无效,但不是因为intchar*的大小;即使保证它们的大小相同,仍然需要进行强制转换,并且结果充其量是实现定义的。此外,void main(void) 应该是int main(void)#include &lt;sys/types.h&gt; 是不可移植且不必要的,打印size_t 值的正确格式是%zu,而不是%d
  • 根据 Keith 的反馈进行了更新(现在我有足够的声誉,对 Keith 最适合我的回答表示支持)
【解决方案4】:

在 C 中,指针不仅是内存中的地址,还是类型:

pointer = memory size + type

理解这一点的最佳方法是考虑指针算术,以防止执行某些算术运算。例如,您可以这样做:

int i;
int * ptr, ptr2;

ptr = &i;  // Assign the address of an int to an int pointer
ptr2 = ptr + 2; // Shift the pointer of two int size further
i = ptr - ptr2; // Compute the number of slots between ptr and ptr2

但是,你不能这样做:

int i;
char c;
int * ptr;
char * ptr2;

ptr = &c; // The types are not matching, the compiler will complain 
ptr = 2 * ptr; // This is not allowed by pointer arithmetic
ptr = ptr + ptr; // This is not allowed by pointer arithmetic

等等……

但是,您必须始终记住的是,指针不仅仅是一个内存地址。它是内存地址类型的组合。

【讨论】:

  • 顺便说一下,ptr2 = ptr + 2; 在您的第一个示例中调用了未定义的行为。
  • @PascalCuoq:嗯,它在执行的意义上是未定义的。但是,这在语义上是完全定义的。但是,是的,我同意你的看法。
  • @perror:你在执行和语义之间有什么区别? ptr2 = ptr + 2 确实有明确定义的行为 if ptr 包含数组元素的地址,并且至少有一个元素紧随其后。指针加法的语义是:如果有特定的要求,则产生一个指定的值,否则行为是未定义的。
  • @KeithThompson:我区分语义和执行是因为ptr2 = ptr + 2; 具有语义,但它会在执行时给出未定义的行为。例如,如果您考虑这一行:for (int i = 0; i &lt; 10; i = i++){printf("%d", i);} i=i++ 没有明确定义的语义(因此编译器将自行选择并再次给出不可预测的行为......但出于语义原因)。
  • @perror:好的——但我不相信这是一个有用的区别
猜你喜欢
  • 1970-01-01
  • 2018-05-12
  • 2015-10-08
  • 1970-01-01
  • 2017-04-23
  • 2017-02-12
  • 2016-08-07
  • 2017-03-28
  • 2018-03-26
相关资源
最近更新 更多