0

安德烈·卡尔波夫(Andrey Karpov)题为“关于size_tptrdiff_t的博文中,他总结道

正如读者所见,使用 ptrdiff_t 和 size_t 类型为 64 位程序提供了一些优势。但是,它不是用 size_t 替换所有无符号类型的全面解决方案。首先,它不能保证程序在 64 位系统上的正确运行。其次,很可能由于这种替换,会出现新的错误,违反数据格式兼容性等等。您不应该忘记,在此替换之后,程序所需的内存大小也会大大增加。必要内存大小的增加会减慢应用程序的工作,因为缓存将存储更少的正在处理的对象。

我不理解这些说法,也没有在文章中看到它们,

“很有可能因为这个替换,会出现新的错误,违反数据格式兼容性等等。”

这怎么可能,在迁移和类型迁移导致错误之前怎么可能没有错误?目前尚不清楚类型 (size_tptrdiff_t) 何时似乎比它们要替换的类型更具限制性。

您不应该忘记,在此替换之后,程序所需的内存大小也会大大增加。

我不清楚所需的内存大小如何或为什么会“大大”增加或根本增加?我明白,如果它确实如此,安德烈的结论就会随之而来。

4

3 回答 3

3

这篇文章包含非常可疑的说法。

首先size_t是返回的类型sizeofuintptr_t是一个整数类型,可以存储任何指向void.

该文章声称size_t并且uintptr_t是同义词。他们不是。例如,在具有大内存模型的分段 MSDOS 上,数组中元素的最大数量将适合size_t16 位,但指针需要 32 位。它们现在是我们常见的 Windows、Linux 平面内存模型的同义词。

更糟糕的是声称您可以将指针存储在 中ptrdiff_t,或者它将与 同义intptr_t

的大小总是size_tptrdiff_t指针的大小一致。因此,正是这些类型应该用作大型数组的索引、指针存储和指针运算。

这根本不是真的。ptrdiff_t是指针减法的值的类型,但是指针减法在两个指针都指向同一个对象或紧随其后时才定义,而不仅仅是内存中的任何位置。

另一方面ptrdiff_t,可以选择大于size_t- 这是因为如果您有一个大小大于元素的数组,则从MAX_SIZE / 2指向最后一个元素的指针中减去指向第一个元素的指针或仅超出将具有未定义的行为,如果ptrdiff_t是相同的宽度size_t。Inded,标准确实说size_t只能是 16 位宽,但ptrdiff_t必须至少是17 ]( http://port70.net/~nsz/c/c11/n1570.html#7.20.3 )。

在 Linux 上ptrdiff_t并且size_t大小相同 - 并且可以在 32 位 Linux 上分配大于PTRDIFF_MAXelements的对象。正如评论中指出的那样,标准ptrdiff_t甚至不需要与 . 等级相同size_t,尽管这样的实现将是纯粹的邪恶。

如果一个人要听从建议并使用size_tptrdiff_t存储指针,那肯定不会走对路


至于声称

您不应该忘记,在此替换之后,程序所需的内存大小也会大大增加。

我反对这种说法 - 与一般 64 位对齐、堆栈对齐和迁移到 64 位环境中固有的 64 位指针所增加的消耗相比,内存需求的增加相当温和.

至于声称

“很有可能因为这个替换,会出现新的错误,违反数据格式兼容性等等。”

这当然是真的,但如果你编写这样的错误代码,你很可能会不小心“修复”过程中的旧错误,signed/unsigned int例如:

int A = -2;
unsigned B = 1;
int array[5] = { 1, 2, 3, 4, 5 };
int *ptr = array + 3;
ptr = ptr + (A + B); //Error
printf("%i\n", *ptr);

其中原始代码和新代码都将具有未定义的行为(越界访问数组元素),但新代码在 64 位平台上似乎也是“正确的”。

于 2018-07-07T06:57:11.700 回答
1

那么任何更改都可能会引入错误。具体来说,我可以想象改变大小可能会破坏对类型应用不那么严格的地方(例如,假设 int 或 long 与它们不是的指针相同)。写入文件的任何二进制结构都不能直接读取,并且任何 RPC 都可能失败,具体取决于协议。

随着大多数内存对象的大小增加,内存需求显然会增加。大多数数据将在 64 位边界上对齐,这意味着更多的“洞”。堆栈使用将增加,可能导致更频繁的缓存未命中。

虽然所有的概括都可能是对的或错的,但找出答案的唯一方法是对手头的系统进行一些适当的分析。

于 2018-07-07T03:47:29.970 回答
0

作为一般命题,使用size_tandptrdiff_t比使用简单的unsigned intand更受欢迎intsize_t并且ptrdiff_t几乎是编写健壮且可广泛移植的程序的唯一方法。

然而:没有免费的午餐。正确使用size_t也需要一些工作——只是,如果你知道你在做什么,它比试图在不使用size_t.

此外,size_t还有无法使用%dor打印的问题%u。理想情况下,您想使用%zu,但不幸的是,并非所有实现都支持它。

如果您有一个不使用的大型且编写不佳的程序,那么size_t它可能充满了错误。其中一些错误将被掩盖或解决。如果您尝试将其更改为 use size_t,则该程序的某些解决方法将失败,可能会发现曾经隐藏的错误。最终你会解决这些问题并实现你想要的更强大、更可靠和更便携的程序,但这个过程将是一个艰难的过程。我怀疑这就是作者所说的“很可能由于这种替换,会出现新的错误”。

将程序更改为使用size_t有点像尝试const在所有正确的地方添加。你做了你认为需要做的改变,然后重新编译,你得到一堆错误和警告,你修复这些并重新编译,你得到一堆更多的错误和警告,等等。这至少是一个麻烦,而且有时需要大量的工作。但如果您想让代码更加健壮和可移植,这通常是唯一的方法。

问题的很大一部分是让编译器满意。它会警告一堆东西,你通常会想要修复它抱怨的所有东西,即使它抱怨的一些东西是棘手的,不太可能引起问题。但是说“是的,我可以忽略这个特别的警告”是很危险的,所以最后,正如我所说,你通常会想要解决所有问题。

作者最抢眼的说法是

程序所需的内存大小也将大大增加。

我怀疑这是夸大其词——在大多数情况下,我怀疑内存会“大大”增加——但它可能至少会增加一点点。问题是在 64 位系统上,size_t并且ptrdiff_t很可能是 64 位类型。如果出于某种原因你有这些的大型数组,或者包含这些的大型结构数组,并且如果你以前使用过一些 32 位类型(可能是普通的intunsigned int),是的,你会看到内存增加。

然后你会想问,我真的需要能够描述 64 位大小吗? 64 位编程为您提供了两件事:(a) 能够寻址超过 4Gb 的内存,以及 (b) 能够拥有大于 4Gb 的单个对象。如果您希望总数据使用量大于 4Gb,但您不需要单个对象大于 4Gb,并且如果您不想一次从文件中读取超过 4Gb 的数据(使用单readfread调用,即),您实际上并不需要到处都是 64 位大小的变量。

因此,为了避免臃肿,您可能会做出明智的选择,例如,unsigned int(甚至unsigned short)而不是size_t在某些地方使用。作为一个简单的例子,如果你有

size_t x = sizeof(int);
printf("%zu\n", x);

你可以把它改成

unsigned int x = sizeof(int);
printf("%u\n", x);

没有任何可移植性损失,因为我可以非常自信地保证您的代码永远不会在具有 34359738368 位int的机器上运行(或者至少在我们的有生之年:-) )。

但是最后一个例子,尽管它是微不足道的,但也说明了其他容易侵入的问题。类似的代码

unsigned int x = sizeof(y);
printf("%u\n", x);

显然不是很安全,因为无论如何y,它都有可能太大以至于它的大小不适合无符号整数。因此,如果您或您的编译器真的关心类型正确性,那么在分配size_tunsigned int. 要关闭这些警告,您可能需要显式强制转换,如

unsigned int x = (unsigned int)sizeof(int);

可以说,这个演员阵容非常合适。编译器的运行假设是任何对象都可能非常大,任何将 asize_t插入 a 的尝试都unsigned int可能会丢失数据。演员说你已经考虑过这个案子:你说,“是的,我知道,但在这种情况下,我知道它不会溢出,所以请不要再警告我这个案子了,但是请务必警告我任何其他人,这可能不那么安全。”

PS我被否决了,所以如果我给了错误的印象,让我明确一点(正如我在开篇所说的那样)size_t并且ptrdiff_t非常受欢迎。一般来说,有充分的理由使用它们,没有充分的理由不使用它们。(说到这里,卡尔波夫也不是说不使用它们——只是强调了一些可能会出现的问题。)

于 2018-07-07T10:29:38.830 回答