这个问题, 有人建议在一个 评论 我应该 投的结果 malloc, 即

int *sieve = malloc(sizeof(int) * length);

而不是:

int *sieve = (int *) malloc(sizeof(int) * length);

为什么会这样呢?

有帮助吗?

解决方案

没有;你的 不不 投的结果,因为:

  • 这是不必要的,因为 void * 是自动和安全提升为其他任何类型的指针在这种情况。
  • 它增加了杂乱的代码转换都不是很容易阅读(特别是如果所指类型是长)。
  • 它使你重复自己,这一般是坏的。
  • 它可以隐藏的错误如果你忘了包括 <stdlib.h>.这可能会导致崩溃(或者,更糟糕的是, 导致崩溃的方式,直到后来在一些完全不同的一部分代码)。考虑什么情况,如果指针和整数都不同大小的;然后你隐藏了一个警告通过浇铸,并可能会失去位的您的回返地址。注:作为C11隐含的职能已经从C,这一点就不再相关,因为没有自动假定,即未经申报的职能返回 int.

作为一个澄清,请注意,我说"你不投",不是"你不 需要 投".在我看来,它是一个失败包括铸造的,甚至如果你得到了它的权利。只是有不利益这样做,但是一堆潜在的风险,包括铸表示你不知道有关的风险。

还注意到,作为评论家指出的是,上述谈觉C,不C++。我非常坚定地相信,在C和C++作为独立的语言。

添加另外,你的代码不必要的重复信息的类型(int 的),这可能会导致错误。这是更好地引用的指针,正在用于储存回报的价值,"锁定"两个在一起:

int *sieve = malloc(length * sizeof *sieve);

这也移动 length 到前面增加了可见性,并降的多余的括号与 sizeof;他们 只需要 当参数是一种名称。许多人似乎不知道(或忽视)这样的,这使得他们的代码的更多详细。请记住: sizeof 是不是一个功能!:)


同时移动 length 到前面 增加可见性,在某些罕见的情况下,还应该注意的是,在一般情况下,应当更好地写信表达为:

int *sieve = malloc(sizeof *sieve * length);

由于保持 sizeof 首先,在这种情况下,确保乘法是至少 size_t 数学。

比较: malloc(sizeof *sieve * length * width)malloc(length * width * sizeof *sieve) 第二可能会溢出 length * widthwidthlength 是较小的类比 size_t.

其他提示

在C中,您不需要转换malloc的返回值。 sieve返回的void指针自动转换为正确的类型。但是,如果您希望使用C ++编译器编译代码,则需要进行强制转换。社区中的首选替代方案是使用以下内容:

int *sieve = malloc(sizeof *sieve * length);

除了更改<=>的类型外,还可以让您不必担心更改表达式的右侧。

正如人们所指出的那样,演员阵容很糟糕。特别是指针转换。

你的 铸造的,因为:

  • 它使你的代码 更多的便携式 C和C++,因为经验表明,很多权利要求程序员,他们都写在C时他们是真正的写作用C++(或C加上当地编译器扩).
  • 如果不这样做 可以隐藏的错误:注意到所有这样的例子令人困惑的时候写的 type *type **.
  • 这个想法,它让你从没有注意到你没有 #include 适当标题的文件失误 森林里的树.它是相同的话说,"不要担心的是你失败的要求编译器的抱怨没有看到的原型--那个讨厌的标准库.h是真正重要的事情要记得!"
  • 它的力量一个 额外的认知交叉检查.它把(指称的)所需类型的旁边,算您正在做的原尺寸是可变的。我敢打赌你可以做一个这样的研究显示, malloc() 错误是抓得更快的时候有一个演员。作为与断言,注释揭示的意图,减少错误。
  • 重复自己的方式,机器可以检查通常是一个 伟大的 想法。事实上,这是一个断言的是,这种使用铸是一个断言。断言仍然是最大的技术,我们已经得到正确的代码,由于图灵了这么多年以前。

正如其他人所说的那样,C不需要它,但对于C ++则不需要。如果您认为要使用C ++编译器编译C代码,出于某种原因,您可以使用宏,例如:

#ifdef __cplusplus
# define NEW(type, count) ((type *)calloc(count, sizeof(type)))
#else
# define NEW(type, count) (calloc(count, sizeof(type)))
#endif

这样你仍然可以用非常紧凑的方式编写它:

int *sieve = NEW(int, 1);

它将为C和C ++编译。

维基百科:

优点在于铸造

  • 包括铸可以允许一C节目的或功能,以编译为C++。

  • 投允许为预1989年版本的malloc最初返回char*.

  • 铸造可以帮助开发人员查明不一致的现象,在类型上浆应当目标指的类型的变化,特别是如果指针是宣布远离malloc()call(尽管现代的汇编者和静态分析可以警告这种行为不需要投).

缺点铸造

  • 下ANSI C的标准,演员是多余的。

  • 增加铸可能掩盖未包括的头 标准库.h, 在 其中的原型malloc被发现。在没有 原型malloc,该标准要求,C编译器 假设malloc返回一个int.如果没有投警告 时发出的这一整数分配给指针;然而,与 演员,这个警告不生产的,隐藏着一个错误。在某些 体系结构和数据模型(例如LP64 64位系统,在那里 长期和指针64位和int是32位),这种错误可以 实际上结果不确定的行为,作为隐含地宣布 malloc返回的一个32位数,而实际上定义功能 返回64位的价值。根据呼吁公约和存储器 布局,这可能导致堆的粉碎。这个问题不太可能 去忽视现代个编译器,因为它们均匀的生产 警告一个未申报的功能已经被使用,因此警告会 仍然出现。例如,海湾合作委员会的默认行为是显示 警告,内容为"不相容隐含的宣言》的内在 功能"不论是否投存或没有。

  • 如果该类型的指针是改变在其宣言,其中一个可能 此外,需要改变所有行malloc是所谓的和演员。

虽然 malloc而不是优选方法和最有经验的程序员选择它, 你应该使用哪个你喜欢有意识到的问题。

i。e:如果你需要C编译程序样C++(虽然这些都是单独的语言)应使用 malloc 与铸造。

在C中,您可以隐式地将void指针转换为任何其他类型的指针,因此不需要强制转换。使用一个可能会向不经意的观察者建议,有一些理由需要一个,这可能会产生误导。

你不投的结果malloc,因为这样做会增加毫无意义的混乱你的代码。

最常见的原因,为什么人们投的结果malloc是因为他们不能确定关于如何C语言的工作。这是一个警告标志:如果你不知道如何在特定的语言机构工作的,然后 不不 采取一种猜测。看看它,或要求在栈溢出。

一些评论:

  • 一个无效的指针可以转换为任何其他类型的指针没有一个明确的投(C11 6.3.2.3和6.5.16.1).

  • C++但是不允许一个隐含的铸造间 void* 和另一个指针的类型。因此,在C++、投会是正确的。但如果你计划在C++,你应该使用 new 并不malloc().你应该从来没有C编译代码的使用C++编译器。

    如果你需要支持这两个C和C++同来源的代码时,使用的编译器标记的分歧。不要试图满足这两种语言的标准相同的代码,因为它们是不相容的。

  • 如果有C编译器无法找到一个功能,因为你忘了包括头,你会得到一个编译/连接的错误有关。所以如果你忘了包括 <stdlib.h> 这没什么大不了,你就不能够建立您的节目。

  • 在古老的编译器,遵循一个版本的标准,它是超过25岁,忘记包括 <stdlib.h> 会导致危险行为。因为在那个古老标准、职能没有可见的原型隐含地转化为返回的类型 int.铸造的结果malloc明确地将然后躲起来这个错误。

    但这确实是一个非的问题。你不是使用25岁的计算机,那么为什么你会用一个25岁的编译器吗?

在C中,您将获得从void*到任何其他(数据)指针的隐式转换。

现在没有必要转换由malloc()返回的值,但我想添加一个似乎没有人指出的点:

在古代,也就是说,在 ANSI C 提供void *作为通用类型的指针之前,char *是这种用法的类型。在这种情况下,强制转换可以关闭编译器警告。

参考: C FAQ

不必强制转换malloc的结果,因为它返回void*,并且<=>可以指向任何数据类型。

只是添加我的经验,学习计算机工程我看到我在C中写过的两三位教授总是投写malloc,但是我问的那个(有着巨大的简历和对C的理解)告诉我它是绝对不必要,但过去只是绝对具体,并让学生进入绝对具体的心态。基本上,转换不会改变它的工作方式,它完全按照它所说的方式,分配内存,而且转换不会影响它,你得到相同的内存,即使你错误地将它转换为别的东西(并以某种方式规避编译器)错误)C将以相同的方式访问它。

编辑:投射有一定的意义。当您使用数组表示法时,生成的代码必须知道它必须前进多少内存位置才能到达下一个元素的开头,这是通过强制转换实现的。通过这种方式,你知道对于一个双倍,你前进8个字节而对于一个int你去4个,依此类推。因此,如果使用指针表示法则无效,在数组表示法中则必须使用。

GNU C Library Reference 手册说:

  

您可以将malloc的结果存储到任何没有a的指针变量中   强制转换,因为ISO C会自动将类型void *转换为另一个   必要时指针的类型。但演员阵容在上下文中是必要的   除了赋值运算符或者您可能希望运行代码   在传统的C中。

确实 ISO C11标准(p347)如此说:

  

如果分配成功,则返回指针   它可以被分配给指向任何类型的对象的指针   基本对齐要求,然后用于访问这样的   对象或分配的空间中的此类对象的数组(直到   空间明确解除分配)

void指针是一个通用指针,C支持从void指针类型到其他类型的隐式转换,因此不需要显式地对其进行类型转换。

但是,如果您希望相同的代码在C ++平台上完全兼容(不支持隐式转换),则需要进行类型转换,因此这一切都取决于可用性。

返回的类型为void *,可以转换为所需类型的数据指针,以便可以解除引用。

在C语言中,可以为任何指针分配一个void指针,这就是你不应该使用类型转换的原因。如果你想要<!> quot; type safe <!> quot;分配,我可以推荐以下宏函数,我总是在我的C项目中使用:

#include <stdlib.h>
#define NEW_ARRAY(ptr, n) (ptr) = malloc((n) * sizeof *(ptr))
#define NEW(ptr) NEW_ARRAY((ptr), 1)

有了这些,你可以简单地说

NEW_ARRAY(sieve, length);

对于非动态数组,第三个必备函数宏是

#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])

使数组循环更安全,更方便:

int i, a[100];

for (i = 0; i < LEN(a); i++) {
   ...
}

这取决于编程语言和编译器。如果在C中使用malloc,则不需要输入强制类型,因为它会自动键入强制类型,但是如果你使用C ++,那么你应该键入强制转换,因为void*将返回<=>类型。

过去GCC和Clang的人都被宠坏了。那里并不是那么好。

多年来,我一直被我需要使用的令人咋舌的老化编译器吓坏了。公司和经理通常会采用极端保守的方法来更改编译器,如果新的编译器(具有更好的标准兼容性和代码优化)在他们的系统中工作,他们甚至不会测试。工作开发人员的实际情况是,当你编写代码时,你需要覆盖你的基础,不幸的是,如果你无法控制可能对你的代码应用的编译器,那么铸造mallocs是一个好习惯。

我还建议许多组织应用自己的编码标准, 在没有明确指导的情况下,我倾向于最有可能在任何地方进行编译,而不是盲目遵守标准。

根据现行标准,不必要的论点是非常有效的。但是这个论点忽略了现实世界的实用性。我们不是在一个完全由当时标准统治的世界中编码,而是通过我喜欢称之为<!>“本地管理的现实领域<!>”的实用性来编码。这比以往任何时候都更加弯曲和扭曲。 : - )

因人而异。

我倾向于将malloc视为防御性操作。不漂亮,不完美,但一般都很安全。 (老实说,如果你没有包含stdlib.h,那么你方式比铸造malloc更多的问题!)。

我放入演员只是为了表示不赞成类型系统中的丑陋漏洞,这允许在没有诊断的情况下编译下面的代码片段,即使没有使用强制转换来导致错误的转换:

double d;
void *p = &d;
int *q = p;

我希望它不存在(并且它不在C ++中),所以我投了。它代表了我的品味和我的编程政治。我不仅要投掷指针,而且还要有效地投票,然后消除愚蠢的恶魔。如果我不能实际 愚弄,那么至少让我表达一下希望以抗议的姿态这样做。

事实上,一个好的做法是将malloc(和朋友)包含在返回unsigned char *的函数中,并且基本上永远不会在代码中使用void *。如果需要通用指针到任意对象,请使用char *memset,并在两个方向上进行强制转换。可能放纵的一种放松方式是使用memcpygrep等函数而不使用强制转换。

关于转换和C ++兼容性的主题,如果您编写代码以便它编译为C和C ++(在这种情况下,必须在分配时强制转换strip_qual的返回值除了const之外的其他东西,你可以为自己做一个非常有用的事情:你可以使用宏进行转换,在编译为C ++时转换为C ++样式转换,但在编译为C时减少为C转换:

/* In a header somewhere */
#ifdef __cplusplus
#define strip_qual(TYPE, EXPR) (const_cast<TYPE>(EXPR))
#define convert(TYPE, EXPR) (static_cast<TYPE>(EXPR))
#define coerce(TYPE, EXPR) (reinterpret_cast<TYPE>(EXPR))
#else
#define strip_qual(TYPE, EXPR) ((TYPE) (EXPR))
#define convert(TYPE, EXPR) ((TYPE) (EXPR))
#define coerce(TYPE, EXPR) ((TYPE) (EXPR))
#endif

如果您遵守这些宏,那么只需简单地volatile搜索这些标识符的代码库,就会显示所有演员阵容的位置,以便您查看其中是否有任何错误。

然后,继续,如果您经常使用C ++编译代码,它将强制使用适当的强制转换。例如,如果您仅使用-Wold-style-cast删除(type)或<=>,但程序更改现在涉及类型转换,您将获得诊断,并且您将不得不使用演员阵容的组合,以获得所需的转换。

为了帮助您遵守这些宏,GNU C ++(而不是C!)编译器具有一个很好的功能:为所有出现的C样式转换生成一个可选的诊断。

     -Wold-style-cast (C++ and Objective-C++ only)
         Warn if an old-style (C-style) cast to a non-void type is used
         within a C++ program.  The new-style casts (dynamic_cast,
         static_cast, reinterpret_cast, and const_cast) are less vulnerable
         to unintended effects and much easier to search for.

如果您的C代码编译为C ++,您可以使用此<=>选项查找可能会蔓延到代码中的所有<=>转换语法,并通过替换为适当的代码来跟进这些诊断从上述宏中选择(或必要时组合)。

对转换的这种处理是在<!>“Clean C <!>”中工作的单个最大的独立技术理由:C和C ++组合的方言,在技术上证明了转换<=>的返回值>

尽可能在C语言编程时最好的事情:

  1. 通过打开所有警告的C编译器编译程序-Wall并修复所有错误和警告
  2. 确保没有声明为auto
  3. 的变量
  4. 然后使用带有-std=c++11stdlib.h的C ++编译器进行编译。修复所有错误和警告。
  5. 现在再次使用C编译器进行编译。您的程序现在应该在没有任何警告的情况下编译,并且包含更少的错误。
  6. 此过程允许您利用C ++严格类型检查,从而减少错误数量。特别是,此过程会强制您包含malloc或者您将获得

      

    void*未在此范围内声明

    并且还会强制您投射T*的结果,否则您将获得

      

    从<=>到<=>

    的无效转换

    或者你的目标类型是什么。

    我可以找到用C而不是C ++编写的唯一好处是

    1. C具有良好指定的ABI
    2. C ++可能会生成更多代码[例外,RTTI,模板,运行时多态]
    3. 请注意,当使用C的公共子集和 static 多态特征时,理想情况下的第二个缺点应该消失。

      对于那些发现C ++严格规则不方便的人,我们可以使用推断类型的C ++ 11特性

      auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...
      

不,你不投的结果 malloc().

在一般情况下,您 不投向或从 void *.

一个典型的原因不这样做是失败 #include <stdlib.h> 可以去被忽视。这不是一个问题了很长一段时间现在为C99了 隐含的功能的声明 非法的,所以,如果你的编译器符合至少C99,你会得到一个诊断信息。

但是有一个 更强的原因 不要引入不必要的指针投下:

在C, 指针,演员是几乎总是一个错误.这是因为以下的规则(§6.5p7 在N1570,最新的草案为C11):

一个物体应当有其储存的价值访问只能通过左值的表达,有一个的 以下类型:
—类型兼容的有效类型的对象,
—一个合格的版本的a类型兼容的有效类型的对象,
—一类型的签名或未签署的类型相对应的有效类型的 目的,
—一类型的签名或未签署的类型对应一个合格的版本 有效类型的对象,
—一个聚合或联盟的类型,包括一个上述类型之间 成员(包括递归的一个成员subaggregate或包含的联盟),或
—一个性格类型。

这也被称为 严格的规则混淆.所以,下列代码是 未定义的行为:

long x = 5;
double *p = (double *)&x;
double y = *p;

而且,有时令人惊讶的是,下面是作为:

struct foo { int x; };
struct bar { int x; int y; };
struct bar b = { 1, 2};
struct foo *p = (struct foo *)&b;
int z = p->x;

有时候,你 需要投指针,但是鉴于 严格的规则混淆, 你必须要非常小心。因此,任何发生的一个指投在你的代码是一个地方你 必须仔细检查其有效性.因此,你永远写不必要的指针的演员。

tl博士

一言以蔽之:因为在C, 任何 生一个 指针铸 应该提出一个红色的标志,用于代码需要特殊关注,你不应该写信 不必要的 指针转换。


侧面的注释:

  • 在有些情况下实际上你 需要 一个铸造 void *, 如如果你想打印指针:

    int x = 5;
    printf("%p\n", (void *)&x);
    

    铸是必要的,在这里,因为 printf() 是一个可变的功能,因此隐含的转换不工作。

  • C++,情况是不同的。铸造的指针的类型是有点共同的(和更正)在处理对象的衍生课程。因此,很有意义的是,在C++的转换 void * 隐含的。C++的一整套不同的口味的铸造。

我更喜欢演员,但不是手动。我最喜欢的是使用glib中的g_newg_new0宏。如果没有使用glib,我会添加类似的宏。这些宏减少了代码重复,而不会影响类型安全性。如果你得到错误的类型,你会得到非void指针之间的隐式转换,这会引起警告(C ++中的错误)。如果您忘记包含定义malloccalloc的标题,则会出现错误。 0和<=>都采用相同的参数,与<=>不同,参数少于<=>。只需添加<=>即可获得零初始化内存。代码可以使用C ++编译器进行编译而无需更改。

Casting仅适用于C ++而不是C.如果您使用的是C ++编译器,最好将其更改为C编译器。

void指针背后的概念是它可以转换为任何数据类型,这就是malloc返回void的原因。您还必须了解自动类型转换。因此,必须执行此操作并不强制转换指针。它有助于保持代码清洁并有助于调试

  1. 正如其他所说,C不需要,但C ++不需要。

  2. 包含强制转换可能允许C程序或函数以C ++编译。

  3. 在C中没有必要,因为void *会自动安全地提升为任何其他指针类型。

  4. 但是如果你投射,那么如果忘记包含它,它可以隐藏错误 的 stdlib.h中即可。这可能会导致崩溃(或者更糟糕的是,不会导致崩溃 直到稍后在代码的一些完全不同的部分)。

    因为 stdlib.h 包含找到malloc的原型。在里面 没有malloc的原型,标准要求C 编译器假定malloc返回一个int。如果没有演员,a 将此整数分配给指针时发出警告; 然而,对于演员表,这个警告不会产生,隐藏了一个错误。

void指针是一个通用指针,C支持从void指针类型到其他类型的隐式转换,因此不需要显式地对其进行类型转换。

但是,如果您希望相同的代码在C ++平台上完全兼容(不支持隐式转换),则需要进行类型转换,因此这一切都取决于可用性。

铸造的malloc是不必要的,但必须在C++。

铸造不必要的,C因为:

  • void * 是自动和安全提升为其他任何类型的指针的情况下C.
  • 它可以隐藏的错误如果你忘了包括 <stdlib.h>.这可能会导致崩溃。
  • 如果指针和整数都不同大小的,然后你隐藏了一个警告通过浇铸,并可能会失去位的您的回返地址。
  • 如果该类型的指针是改变在其宣言,其中一个可能也需要改变所有的线在哪里 malloc 被称为和演员。

另一方面,铸造可增加可移植程序。i。电子,它允许一C节目的或功能,以编译为C++。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top