提问人:Patrick McDonald 提问时间:3/3/2009 最后编辑:Jan SchultkePatrick McDonald 更新时间:10/7/2023 访问量:376785
我应该投射 malloc 的结果吗?
Should I cast the result of malloc?
问:
在这个问题中,有人在评论中建议我不应该投出 .也就是说,我应该这样做:malloc
int *sieve = malloc(sizeof(*sieve) * length);
而不是:
int *sieve = (int *) malloc(sizeof(*sieve) * length);
为什么会这样?
答:
在 C 语言中,您可以获得从任何其他(数据)指针到任何其他(数据)指针的隐式转换。void *
评论
在 C 中,不需要强制转换 的返回值。返回的指向 void 的指针会自动转换为正确的类型。但是,如果您希望代码使用 C++ 编译器进行编译,则需要强制转换。社区中首选的替代方案是使用以下方法:malloc
malloc
int *sieve = malloc(sizeof *sieve * length);
此外,如果您更改了 .sieve
正如人们指出的那样,演员阵容很糟糕。尤其是指针投射。
评论
malloc(length * sizeof *sieve)
sizeof
malloc(length * sizeof(*sieve))
malloc(length * (sizeof *sieve))
()
length*width
sizeof
size_t
malloc(sizeof( *ptr) * length * width)
malloc(length * width * sizeof (*ptr))
length*width
width,length
size_t
malloc(sizeof *sieve * length)
static_cast>()
reinterpret_cast<>()
不;您不应该强制转换结果,因为:
- 这是不必要的,因为在这种情况下会自动安全地提升到任何其他指针类型。
void *
- 它给代码增加了混乱,强制转换不是很容易阅读(特别是如果指针类型很长)。
- 它让你重复自己,这通常是不好的。
- 如果您忘记包含 .这可能会导致崩溃(或者更糟糕的是,直到稍后在代码的某些完全不同的部分才导致崩溃)。考虑如果指针和整数的大小不同会发生什么;然后,您通过强制转换来隐藏警告,并且可能会丢失返回地址的部分内容。注意:从 C99 开始,隐式函数已从 C 中删除,这一点不再相关,因为没有自动假设未声明的函数返回 。
<stdlib.h>
int
澄清一下,请注意,我说的是“你不应该投射”,而不是“你不需要投射”。在我看来,即使你做对了,包括演员阵容也是一个错误。这样做根本没有好处,但有一堆潜在的风险,包括演员表明你不知道风险。
还要注意,正如评论者指出的那样,上面讨论的是直接 C,而不是 C++。我非常坚信 C 和 C++ 是独立的语言。
此外,您的代码会不必要地重复类型信息 (),这可能会导致错误。最好取消引用用于存储返回值的指针,将两者“锁定”在一起:int
int *sieve = malloc(length * sizeof *sieve);
有人可能会说:“好吧,以前类型是重复的,现在变量名是重复的;这不和以前一样重复吗?这怎么会更好呢?区别在于,如果有一天你更改了变量的类型,而忘记更改了要匹配下的类型,你会默默地得到一个错误大小的分配,并且没有警告;但是,如果您更改了变量的名称,但忘记更改 under 下的名称以匹配,则更有可能是旧名称不再解析为任何内容,因此您的代码将停止编译,提示您修复错误。sizeof
sizeof
这也将 移到前面以增加可见性,并删除带有 ;仅当参数是类型名称时才需要它们。许多人似乎不知道(或忽略)这一点,这使得他们的代码更加冗长。记住:不是一个函数!:)length
sizeof
sizeof
虽然在极少数情况下移动到前面可能会增加可见性,但还应该注意,在一般情况下,最好将表达式写成:length
int *sieve = malloc(sizeof *sieve * length);
因为在这种情况下,保留第一个,确保乘法至少用数学完成。sizeof
size_t
比较: vs. 第二个可能溢出 when 和 是小于 的类型。malloc(sizeof *sieve * length * width)
malloc(length * width * sizeof *sieve)
length * width
width
length
size_t
评论
int x = (int) 12;
(int)12
12
int
malloc()
void *
void *
(int)12
(void*)malloc(…)
#include <stdlib.h>
在 C 语言中,可以将指针隐式转换为任何其他类型的指针,因此不需要强制转换。使用一个可能会向不经意的观察者暗示,需要一个是有原因的,这可能会产生误导。void
评论
正如其他人所说,它不是 C 需要的,但 C++ 是必需的。如果您认为要使用 C++ 编译器编译 C 代码,无论出于何种原因,您都可以改用宏,例如:
#ifdef __cplusplus
# define MALLOC(type) ((type *)malloc(sizeof(type)))
# define CALLOC(count, type) ((type *)calloc(count, sizeof(type)))
#else
# define MALLOC(type) (malloc(sizeof(type)))
# define CALLOC(count, type) (calloc(count, sizeof(type)))
#endif
# define FREE(pointer) free(pointer)
这样你仍然可以用非常紧凑的方式编写它:
int *sieve = MALLOC(int); // allocate single int => compare to stack int sieve = ???;
int *sieve_arr = CALLOC(4, int); // allocate 4 times size of int => compare to stack (int sieve_arr[4] = {0, 0, 0, 0};
// do something with the ptr or the value
FREE(sieve);
FREE(sieve_arr);
它将针对 C 和 C++ 进行编译。
评论
new
new
delete
malloc()
free()
NEW
delete
DELETE
MALLOC
CALLOC
强制转换 的结果不是强制性的,因为它返回 ,并且 a 可以指向任何数据类型。malloc
void*
void*
评论
void*
void*
void*
你确实会投射,因为:
- 它使你的代码在 C 和 C++ 之间更具可移植性,正如 SO 经验所表明的那样,许多程序员声称他们正在用 C 编写,而他们实际上是用 C++(或 C 加本地编译器扩展)编写的。
- 如果不这样做,可能会隐藏一个错误:请注意所有混淆何时写入与 .
type *
type **
- 它让你无法注意到你未能获得适当的头文件的想法错过了树木的森林。这就像说“不要担心你没有要求编译器抱怨没有看到原型的事实——讨厌的 stdlib.h 是真正需要记住的重要事情!
#include
- 它强制进行额外的认知交叉检查。它将(据称的)所需类型放在您正在为该变量的原始大小进行的算术运算旁边。我敢打赌,你可以做一个 SO 研究,表明当有演员表时,错误被捕获得更快。与断言一样,揭示意图的注释可以减少错误。
malloc()
- 以机器可以检查的方式重复自己通常是一个好主意。事实上,这就是断言,而这种对 cast 的使用就是一种断言。断言仍然是我们获得正确代码的最通用技术,因为图灵在多年前就提出了这个想法。
评论
.c
.cpp
throw
return -1;
malloc
char **foo = malloc(3*sizeof(*foo));
foo[i] = calloc(101, sizeof(*(foo[i])));
unsigned char
struct Zebra *p; ... p=malloc(sizeof struct Zebra);
p=(struct Zebra*)malloc(sizeof struct Zebra);
p
返回的类型为 void*,可以将其转换为所需的数据指针类型,以便可取消引用。
评论
void*
可以转换为所需的类型,但无需这样做,因为它会自动转换。因此,演员阵容是不必要的,事实上,由于高分答案中提到的原因,这是不可取的。
现在没有必要强制转换返回的值,但我想补充一点,似乎没有人指出:malloc()
在古代,即在 ANSI C 提供指针的泛型类型之前,是此类用法的类型。在这种情况下,强制转换可以关闭编译器警告。void *
char *
参考资料:C 常见问题解答
评论
void *
这取决于编程语言和编译器。如果您在 C 中使用,则无需键入 cast,因为它会自动键入 cast。但是,如果您使用的是 C++,那么您应该键入强制转换,因为将返回一个类型。malloc
malloc
void*
评论
您不会强制转换 的结果,因为这样做会给代码添加毫无意义的混乱。malloc
人们选择结果的最常见原因是因为他们不确定 C 语言是如何工作的。这是一个警告信号:如果你不知道特定的语言机制是如何工作的,那么就不要猜测。在 Stack Overflow 上查找或询问。malloc
一些评论:
void 指针可以转换为任何其他指针类型,而无需显式强制转换(C11、6.3.2.3 和 6.5.16.1)。
但是,C++ 不允许在和另一个指针类型之间进行隐式转换。所以在 C++ 中,强制转换是正确的。但是,如果您使用 C++ 编程,则应使用 而不是 .而且你永远不应该使用 C++ 编译器编译 C 代码。
void*
new
malloc()
如果需要使用相同的源代码同时支持 C 和 C++,请使用编译器开关来标记差异。不要尝试使用相同的代码来满足这两种语言标准,因为它们不兼容。
如果 C 编译器找不到函数,因为您忘记包含标头,您将收到有关该函数的编译器/链接器错误。因此,如果您忘记包含这没什么大不了的,您将无法构建您的程序。
<stdlib.h>
在遵循超过 25 年历史的标准版本的古代编译器上,忘记包含会导致危险行为。因为在那个古老的标准中,没有可见原型的函数隐式地将返回类型转换为 。从显式转换结果将隐藏此错误。
<stdlib.h>
int
malloc
但这确实不是问题。你没有使用一台有 25 年历史的计算机,那么你为什么要使用一台有 25 年历史的编译器呢?
评论
template<T> operator T*() const {...}
void*
加上我学习计算机工程的经验,我看到我看到的两三位教授用 C 语言写作总是投 malloc,但是我问的那位(有大量的简历和对 C 的理解)告诉我,这是绝对不必要的,但只是用来绝对具体,并让学生进入绝对具体的心态。从本质上讲,强制转换不会改变它的工作方式,它完全按照它所说的去做,分配内存,并且强制转换不会影响它,你会得到相同的内存,即使你错误地将其强制转换为其他东西(并以某种方式避免编译器错误),C 也会以同样的方式访问它。
编辑:选角有一定的道理。当您使用数组表示法时,生成的代码必须知道它必须前进多少个内存位置才能到达下一个元素的开头,这是通过强制转换实现的。这样你就知道,对于 double,你领先 8 个字节,而对于 int,你领先 4 个字节,依此类推。因此,如果您使用指针表示法,则它不起作用,在数组表示法中,它变得必要。
评论
p = malloc(sizeof *p * n);
void 指针是一个泛型对象指针,C 支持从 void 指针类型到其他类型的隐式转换,因此不需要显式类型转换它。
但是,如果您希望相同的代码在不支持隐式转换的 C++ 平台上完美兼容,则需要进行类型转换,因此这完全取决于可用性。
评论
malloc
尽可能用 C 语言编程时,最好的办法是:
- 使程序通过 C 编译器编译,并打开所有警告并修复所有错误和警告
-Wall
- 确保没有声明为
auto
- 然后使用 C++ 编译器和 进行编译。修复所有错误和警告。
-Wall
-std=c++11
- 现在再次使用 C 编译器进行编译。您的程序现在应该在没有任何警告的情况下编译,并且包含更少的错误。
此过程允许您利用 C++ 严格的类型检查,从而减少 bug 的数量。特别是,此过程会强制您包含,否则您将得到stdlib.h
malloc
未在此范围内声明
并且还迫使你投射结果,否则你会得到malloc
从 到 的转换无效
void*
T*
或者您的目标类型是什么。
我能找到用 C 而不是 C++ 编写的唯一好处是
- C 具有明确指定的 ABI
- C++ 可能会生成更多代码 [异常、RTTI、模板、运行时多态性]
请注意,在理想情况下,当将 C 共有的子集与静态多态特征一起使用时,第二个缺点应该消失。
对于那些觉得 C++ 严格规则不方便的人,我们可以使用具有推断类型的 C++11 功能
auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...
评论
gcc -c c_code.c
g++ -c cpp_code.cpp
gcc c_code.o cpp_code.o
static const __m128 ones = _mm_set1_ps(1.0f);
p = malloc(sizeof(*p));
p
p
void 指针背后的概念是它可以被强制转换为任何数据类型,这就是 malloc 返回 void 的原因。此外,您还必须了解自动类型转换。因此,尽管您必须这样做,但强制要求投指针并不是强制性的。它有助于保持代码整洁并帮助调试
评论
在 C 语言中,void 指针可以分配给任何指针,这就是为什么不应使用类型转换的原因。如果你想要“类型安全”的分配,我可以推荐以下宏函数,我总是在我的 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++) {
...
}
评论
malloc()
void*
void*
malloc()
强制转换仅适用于C++C.In 如果您使用的是 C++ 编译器,最好将其更改为 C 编译器。
《GNU C 库参考手册》是这么说的:
您可以将结果存储到任何指针变量中,而无需 强制转换,因为 ISO C 会自动将类型转换为另一种类型 必要时的指针类型。但是演员阵容在上下文中是必要的 除赋值运算符外,或者您可能希望代码运行。 在传统 C 中。
malloc
void *
事实上,ISO C11标准(p347)是这样说的:
如果分配成功,则返回的指针会适当地对齐,以便 它可以被分配给指向任何类型的对象的指针,该指针具有 基本对齐要求,然后用于访问这样的 对象或分配的空间中此类对象的数组(直到 空间被显式解除分配)
来自维基百科:
铸造优势
包括强制转换可能允许 C 程序或函数编译为 C++。
强制转换允许 1989 年之前的 malloc 版本,该版本最初返回 char *。
如果目标指针类型发生更改,强制转换可以帮助开发人员识别类型大小的不一致,特别是当指针声明远离 malloc() 调用时(尽管现代编译器和静态分析器可以在不需要强制转换的情况下警告此类行为)。
铸造的缺点
在 ANSI C 标准下,强制转换是多余的。
添加强制转换可能会掩盖在 找到了 malloc 的原型。在没有 prototype for malloc,标准要求 C 编译器 假设 malloc 返回一个 int。如果没有强制转换,则会发出警告 当此整数分配给指针时发出;但是,随着 投射,此警告不产生,隐藏错误。在某些 体系结构和数据模型(例如 64 位系统上的 LP64,其中 long 和指针是 64 位,int 是 32 位),此错误可以 实际上会导致未定义的行为,正如隐式声明的那样 malloc 返回一个 32 位值,而实际定义的函数 返回一个 64 位值。取决于调用约定和内存 布局,这可能会导致堆栈粉碎。此问题不太可能发生 在现代编译器中被忽视,因为它们统一生成 警告已使用未声明的函数,因此警告将 仍然出现。例如,GCC 的默认行为是显示 显示“内置的不兼容隐式声明”的警告 函数“,无论是否存在强制转换。
如果指针的类型在其声明时发生更改,则可以 此外,需要更改调用和转换 malloc 的所有行。
尽管不强制转换的 malloc 是首选方法,并且大多数有经验的程序员都选择它,但您应该使用任何您喜欢的方法,以了解这些问题。
即:如果需要将C程序编译为C++(尽管它是一门单独的语言),则必须转换使用结果。malloc
评论
malloc()
调用,则强制转换可以帮助开发人员识别类型大小的不一致”是什么意思?你能举个例子吗?
p = malloc(sizeof(*p) * count)
习惯了 GCC 和 Clang 的人被宠坏了。外面的情况并不好。
多年来,我一直对被要求使用的老化编译器感到非常震惊。通常,公司和经理会采用一种极端保守的方法来改变编译器,甚至不会测试新的编译器(具有更好的标准合规性和代码优化)是否会在他们的系统中工作。对于在职开发人员来说,实际的现实是,当你编写代码时,你需要覆盖你的基础,不幸的是,如果你无法控制什么编译器可以应用于你的代码,那么强制转换mallocs是一个好习惯。
我还建议许多组织应用他们自己的编码标准,如果定义了它,这应该是人们遵循的方法。在没有明确指导的情况下,我倾向于选择最有可能到处编译,而不是盲目地遵守标准。
在现行标准下没有必要的论点是相当有效的。但这种说法忽略了现实世界的实用性。我们不是在一个完全由当今标准统治的世界里编码,而是在我喜欢称之为“本地管理的现实领域”的实用性中编码。这比时空更弯曲和扭曲。:-)
YMMV。
我倾向于认为施放 malloc 是一种防御行动。不漂亮,不完美,但总体上是安全的。(老实说,如果你没有包括 stdlib.h,那么你遇到的问题比投射 malloc 要多得多!
我加入强制转换只是为了表示对类型系统中丑陋漏洞的不赞成,它允许像以下代码片段这样的代码在没有诊断的情况下进行编译,即使没有使用强制转换来带来错误的转换:
double d;
void *p = &d;
int *q = p;
我希望它不存在(而且它在 C++ 中不存在),所以我投了。它代表了我的品味,以及我的编程政治。我不仅在投指针,而且有效地投了一张选票,赶走了愚蠢的恶魔。如果我真的不能摆脱愚蠢,那么至少让我用抗议的姿态来表达这样做的愿望。
事实上,一个好的做法是用返回的函数包装(和朋友),基本上永远不要在代码中使用。如果需要指向任何对象的通用指针,请使用 或 ,并在两个方向上进行强制转换。也许,一种可以放纵的放松是使用类似石膏和不使用石膏的功能。malloc
unsigned char *
void *
char *
unsigned char *
memset
memcpy
关于转换和 C++ 兼容性的主题,如果您编写代码以便它编译为 C 和 C++(在这种情况下,您必须在将其分配给其他东西时转换返回值 ),您可以为自己做一件非常有用的事情:您可以使用宏进行转换,在编译为 C++ 时转换为 C++ 样式的转换, 但在编译为 C 时简化为 C 强制转换:malloc
void *
/* 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
如果您遵循这些宏,那么在代码库中搜索这些标识符将向您显示所有强制转换的位置,以便您可以查看其中是否有任何不正确。grep
然后,如果您定期使用 C++ 编译代码,它将强制使用适当的强制转换。例如,如果您只使用 删除 或 ,但程序以现在涉及类型转换的方式更改,您将获得诊断,并且您必须使用强制转换的组合来获得所需的转换。strip_qual
const
volatile
为了帮助您遵守这些宏,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++,则可以使用此选项找出可能潜入代码的所有强制转换语法,并通过将其替换为上述宏中的适当选择(或组合,如有必要)来跟进这些诊断。-Wold-style-cast
(type)
这种转换处理是在“干净的 C”中工作的最大的独立技术理由:结合的 C 和 C++ 方言,这反过来又在技术上证明了转换 的返回值是合理的。malloc
评论
我更喜欢做演员,但不是手动。我最喜欢的是使用 glib 中的宏。如果不使用 glib,我会添加类似的宏。这些宏在不影响类型安全性的情况下减少了代码重复。如果类型错误,则会在非 void 指针之间进行隐式强制转换,这将导致警告(C++ 中的错误)。如果忘记包含定义 和 的标头,则会出现错误。 两者都采用相同的参数,不像 .只需添加即可获得零初始化内存。代码可以使用 C++ 编译器进行编译,无需更改。g_new
g_new0
g_new
g_new0
g_new
g_new0
malloc
calloc
0
void 指针是一个泛型指针,C 支持从 void 指针类型到其他类型的隐式转换,因此不需要显式类型转换它。
但是,如果您希望相同的代码在不支持隐式转换的 C++ 平台上完美兼容,则需要进行类型转换,因此这完全取决于可用性。
如前所述,C不需要它,但C++需要它。
包括强制转换可能允许 C 程序或函数编译为 C++。
在 C 中,这是不必要的,因为 void * 会自动安全地提升到任何其他指针类型。
但是,如果您当时强制转换,如果您忘记包含 stdlib.h,它可能会隐藏错误。这可能会导致崩溃(或者更糟糕的是,不会导致崩溃 直到稍后在代码的一些完全不同的部分)。
因为 stdlib.h 包含 malloc 的原型。在 由于没有 malloc 的原型,该标准要求 C 编译器假定 malloc 返回一个 int。如果没有强制转换,则 当此整数分配给指针时发出警告; 但是,在强制转换时,不会产生此警告,从而隐藏了一个错误。
不,您不会强制转换 的结果。malloc()
通常,您不会向 void * 强制转换或从 void *
强制转换。
不这样做的一个典型原因是,如果不这样做,可能会被忽视。这在很长一段时间内不再是问题,因为 C99 使隐式函数声明成为非法,因此如果您的编译器至少符合 C99,您将收到一条诊断消息。#include <stdlib.h>
但是,有一个更充分的理由不引入不必要的指针转换:
在 C 语言中,指针转换几乎总是一个错误。这是因为以下规则(N1570 中 §6.5 p7,C11 的最新草案):
对象的存储值只能由具有 以下类型:
— 与对象的有效类型兼容的类型, — 与对象的有效类型兼容的类型的限定版本,
— 与对象的有效类型对应的有符号或无符号类型 object, — 一种类型,
该类型是与 对象的有效类型,— 一种聚合或联合类型,
其类型包括上述类型之一 成员(递归地包括子聚合或包含的联合的成员),或者
— 字符类型。
这也称为严格锯齿规则。因此,以下代码是未定义的行为:
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++ 中,与 和 from 的转换不是隐式的,这是有道理的。C++ 有一整套不同风格的强制转换。
void *
评论
malloc 的强制转换在 C 中是不必要的,但在 C++ 中是强制性的。
在 C 中不需要强制转换,因为:
void *
在 C 的情况下,会自动安全地提升为任何其他指针类型。- 如果您忘记包含 .这可能会导致崩溃。
<stdlib.h>
- 如果指针和整数的大小不同,则会通过强制转换来隐藏警告,并且可能会丢失返回地址的位。
- 如果指针的类型在其声明时发生了变化,则可能还需要更改调用和强制转换的所有行。
malloc
另一方面,强制转换可能会增加程序的可移植性。即,它允许 C 程序或函数编译为 C++。
对我来说,这里的结论是完全没有必要用 C 语言进行投射,但如果你无论如何投射,它不会影响,因为仍然会为你分配你所要求的祝福内存空间。
另一个带回家的是人们进行强制转换的原因或原因之一,这是为了使他们能够用 C 或 C++ 编译相同的程序。malloc
malloc
malloc
可能还有其他原因,但几乎可以肯定的是,其他原因迟早会让你陷入严重的麻烦。
这个问题是基于意见的滥用的主题。
有时我注意到这样的评论:
或
关于OP使用铸造的问题。评论本身包含指向此问题的超链接。
这在任何可能的方式上都是不恰当和不正确的。当它真正取决于自己的编码风格时,没有对错。
为什么会这样?
它基于两个原因:
这个问题确实是基于意见的。从技术上讲,这个问题应该在几年前就以意见为基础。一个“我该做”或“我不要”或等效的“我应该”或“我不应该”的问题,如果没有自己意见的态度,你就无法集中回答。关闭问题的原因之一是因为它“可能会导致基于意见的答案”,正如这里所展示的那样。
许多答案(包括最明显和最被接受的@unwind答案)要么完全或几乎完全基于意见(例如,如果你进行强制转换或重复,就会将神秘的“混乱”添加到你的代码中,这将是不好的),并且表现出一种明确而集中的倾向,即省略强制转换。他们一方面争论演员的冗余,但更糟糕的是,他们争论解决由编程本身的错误/失败引起的错误 - 如果想使用 .
#include <stdlib.h>
malloc()
我想对所讨论的一些观点提出一个真实的看法,而不是我个人的看法。有几点需要特别注意:
这样一个非常容易陷入自己观点的问题需要一个中立的利弊的答案。不仅是缺点或优点。
此答案中列出了优缺点的良好概述:
https://stackoverflow.com/a/33047365/12139179
(我个人认为这是迄今为止最好的答案,因为这个原因。
最多会遇到一个原因来解释演员的遗漏,因为演员可能隐藏了一个错误。
如果有人使用隐式声明,则返回(隐式函数自 C99 以来已从标准中消失)和 ,如本问题所示
malloc()
int
sizeof(int) != sizeof(int*)
为什么此代码在 64 位架构上出现段错误,但在 32 位架构上工作正常?
演员会隐藏一个错误。
虽然这是真的,但它只显示了故事的一半,因为演员阵容的省略只会是一个更大的错误的前瞻性解决方案——不包括在使用 .
stdlib.h
malloc()
这永远不会是一个严重的问题,如果你,
使用符合 C99 或更高版本的编译器(推荐使用,并且应该是强制性的),并且
当您想在代码中使用时,不要忘记包含,这本身就是一个巨大的错误。
stdlib.h
malloc()
有些人争论 C 代码的 C++ 合规性,因为强制转换在 C++ 中是强制性的。
首先要说的是:使用 C++ 编译器编译 C 代码不是一个好的做法。
C和C++实际上是两种完全不同的语言,具有不同的语义。
但是,如果您真的想要/需要使 C 代码符合 C++,反之亦然,请使用编译器开关而不是任何强制转换。
由于演员表倾向于被宣布为多余甚至有害,我想关注这些问题,这些问题给出了为什么演员表有用甚至必要的充分理由:
- 当代码(分别是分配的指针的类型(以及强制转换的类型))发生更改时,强制转换可能是无益的,尽管在大多数情况下这不太可能。然后,您还需要维护/更改所有强制转换,如果您在代码中对内存管理函数进行了几千次调用,这确实可以总结并降低维护效率。
总结:
事实是,如果分配的指针指向具有基本对齐要求的对象(包括所有对象中的大多数),则根据 C 标准(自 ANSI-C (C89/C90) 以来),强制转换是多余的。
在这种情况下,您不需要进行强制转换,因为指针会自动对齐:
“未指定通过连续调用 aligned_alloc、calloc、malloc 和 realloc 函数分配的存储顺序和连续性。如果分配成功,则返回的指针会适当地对齐,以便可以将其分配给指向具有基本对齐要求的任何类型的对象的指针,然后用于访问此类对象或分配的空间中的此类对象的数组(直到显式解除分配空间)。
来源:C18, §7.22.3/1
“基本对齐是小于或等于的有效对齐。所有存储持续时间的对象的实现应支持基本对齐。以下类型的对齐要求应为基本对齐:
_Alignof (max_align_t)
— 所有原子、合格或不合格的基本类型;
— 所有原子、限定或非限定的枚举类型;
— 所有原子指针、合格指针或非限定指针类型;
— 元素类型具有基本对齐要求的所有数组类型;77 千米赛
— 第 7 条中指定为完整对象类型的所有类型;
- 所有结构或并集类型:其所有元素都具有具有基本对齐要求的类型,并且其元素均未具有指定非基本对齐的对齐说明符。
- 如 6.2.1 所述,后面的声明可能会隐藏前面的声明。
来源:C18, §6.2.8/2
但是,如果为具有扩展对齐要求的实现定义的对象分配内存,则需要强制转换。
扩展对齐方式由大于 的对齐方式表示。它由实现定义是否支持任何扩展对齐方式以及支持这些对齐方式的存储持续时间。具有扩展对齐要求的类型是过度对齐类型。58)
_Alignof (max_align_t)
源。C18,§6.2.8/3
其他一切都取决于具体用例和自己的意见。
请注意您的教育方式。
我建议你先仔细阅读到目前为止所做的所有答案(以及他们可能指向失败的评论),然后建立你自己的观点,如果你或如果你没有在特定案例中投出结果。malloc()
请注意:
这个问题没有正确和错误的答案。这是一个风格问题,你自己决定你选择哪种方式(当然,如果你不是被教育或工作强迫的话)。请注意这一点,不要让欺骗您。
最后一点:我最近投票决定将这个问题作为基于意见的问题,这确实是多年来需要的。如果您获得了关闭/重新打开的特权,我也想邀请您这样做。
评论
int x, y; x = (int)y;
主要问题是获得合适的尺寸。malloc
返回的内存形式是非类型化的,并且由于简单的强制转换,它不会神奇地获得有效的类型。malloc()
我想这两种方法都很好,选择应该取决于程序员的意图。
- 如果为某个类型分配内存,则使用强制转换。
ptr = (T*)malloc(sizeof(T));
- 如果为给定指针分配内存,则不要使用强制转换。
ptr = malloc(sizeof *ptr);
广告 1
第一种方法通过为给定类型分配内存,然后强制转换内存以确保将其分配给正确的指针来确保大小正确。如果使用了不正确的类型,则编译器将发出警告/错误。如果类型发生更改,则编译器将指出代码需要重构的位置。ptr
ptr
此外,第一种方法可以组合成一个类似于 C++ 运算符的宏。new
#define NEW(T) ((T*)malloc(sizeof(T)))
...
ptr = NEW(T);
此外,如果 是 ,则此方法有效。ptr
void*
广告 2
第二种方法不关心类型,它通过从指针的类型中获取大小来确保正确的大小。这种方法的主要优点是每当类型发生变化时都会自动调整存储大小。
它可以在重构时节省一些时间(或错误)。ptr
缺点是该方法不起作用,但如果是,但它可能被认为是一件好事。并且它不适用于 C++,因此不应在 C++ 程序将使用的标头中的内联函数中使用。ptr
void*
就个人而言,我更喜欢第二种选择。
评论