何时施放size_t

时间:2015-06-27 20:25:44

标签: c casting

当程序中存在size_tintunsigned long int等其他数据类型时,我对如何使用unsigned long long int感到有些困惑。我试着最低限度地说明我的困惑。想象一下我使用的程序

void *calloc(size_t nmemb, size_t size)

分配一个数组(一维或多维)。让calloc()的来电取决于nrowsizeof(unsigned long int)sizeof(unsigned long int)显然很好,因为它会返回size_t。但是让nrow使其需要类型unsigned long int。在这种情况下我该怎么办?我是否nrow来自calloc()unsigned long int的{​​{1}}来电?{/ p>

另一个案例是

size_t

char *fgets(char *s, int size, FILE *stream) 期望类型fgets()作为其第二个参数。但是,如果我传递一个数组,让我们说int,因为它是第一个参数并使用save来传递数组的大小?我是否将来电sizeof(save)转为sizeof()?这将是危险的,因为int无法保证从int获得所有可能的回报。

在这两种情况下我该怎么办?播放,或者忽略来自sizeof()等工具的可能警告?

以下是关于splint的示例(为清楚起见,我明确省略了错误检查!):

calloc()

以下是long int **arr; unsigned long int mrow; unsigned long int ncol; arr = calloc(mrow, sizeof(long int *)); for(i = 0; i < mrow; i++) { arr[i] = calloc(ncol, sizeof(long int)); } 的示例(为清晰起见,再次省略了错误处理!):

fgets()

4 个答案:

答案 0 :(得分:3)

  

我对其他数据类型如何使用size_t感到有些困惑   int,unsigned long int和unsigned long long int存在于a中   程序

忽略警告永远不是一个好主意。警告会引起您注意代码中可能存在问题的区域。最好花几分钟时间来了解警告告诉你的内容 - 然后修复它,然后在你遇到角落情况时陷入困境并陷入未定义的行为。

size_t本身就像其他任何数据类型一样。虽然它可能会有所不同,但通常只有unsigned int覆盖可以由int表示的正值范围,包括0(类型大小旨在跨平台保持一致) ,每个上的实际字节可能不同)。您选择的数据类型是编程的基本和基本部分。您可以根据变量可以表示的值范围(或应限于表示)来选择类型。因此,如果您处理的任何内容不能为负,那么unsignedsize_t是正确的选择。然后,该选择允许编译器帮助识别代码可能导致违反的区域。

当您在每次编译时都应该使用警告(例如-Wall -Wextra)进行编译时,您将收到有关数据类型使用中可能存在的冲突的警告。 (即signedunsigned值之间的比较等...)这些重要

几乎所有的现代 x86&amp; x86_64 计算机对签名值使用 twos-compliment 表示。简单来说,这意味着如果有符号数的最左边的位是1,则值为负数。这里有一个微妙的陷阱,你可能在混合/铸造或比较不同类型的数字时陷入其中。如果您选择将unsigned号码转换为signed号码并且该号码恰好填充了最重要的位数,那么您的大号码就会变得非常小。

  

在这两种情况下我该怎么办?投射,或者只是忽略可能   警告...

每次遇到来自编译器的警告时,您都会执行操作。你分析导致警告的原因,然后你修复它(或者如果你不能解决它 - (即来自你无法访问的某些库) - 你能够很好地理解警告,你可以知道你不会遇到任何可能导致不确定行为的角落案件,做出明智的决定而无视它。

在您的示例中(虽然两者都不会产生警告,但可能会出现在某些编译器上):

arr = calloc (mrow, sizeof(long int *));

sizeof(long int *)的范围是多少?嗯 - 这是指针大小的范围。那是什么? (4 bytes上的x868 bytes上的x86_64。所以值的范围是4-8,是的,如果需要,可以通过强制转换为size_t来正确修复,或者更好:

arr = calloc (mrow, sizeof *arr);

看下一个例子:

char save[22];
...
fgets(save, sizeof(save), stdin)

这里sizeof save的可能范围又是多少?来自22 - 22。所以,是的,如果产生警告,抱怨sizeof返回long unsignedfgets int 22int可以投放到{{1} }}

答案 1 :(得分:1)

我的另一个回答太长了,所以这里的篇幅很短。

  1. 声明自然和适当类型的变量。让编译器处理大多数转换。如果您有某些尺寸或可能尺寸,请继续使用size_t。 (同样,如果您有涉及文件大小或偏移的内容,请使用off_t。)
  2. 尽量不要混合有符号和无符号类型。
  3. 如果您因为较大的类型被转换为可能较小的类型而收到有关可能的数据丢失的警告,并且如果您无法更改类型以使警告消失,则首先(a)说服自己在实践中,这些值实际上不会实际溢出较小的类型,然后(b)添加一个显式的下变换强制转换以使警告消失,并且为了额外的信用(c)添加一个断言来记录并强制执行您的假设: / LI>

    assert(size_i_need <= SIZE_MAX);
    char *buf = malloc((size_t)size_i_need);
    

答案 2 :(得分:1)

  

何时施放size_t

你不应该。

在适当的地方使用它。

  • (正如您已经注意到的)libc-library函数告诉您这是哪种情况。
  • 另外用它来索引数组。

如果有疑问的类型符合您的程序需求,您可以根据Steve Summitanswer找到有用的断言语句,如果它失败则重新开始您的程序设计。

Dan Saks "Why size_t matters""Further insights into size_t"

答案 3 :(得分:0)

一般来说,你是对的,你不应该忽视这些警告!一般来说,如果可以的话,你应该回避明确的演员阵容,因为他们可能会使你的代码不那么可靠,或者沉默警告,这些警告真的试图告诉你重要的事情。

我认为,大多数情况下,编译器应该为您做正确的事情。例如,malloc()期望size_t,并且编译器从函数原型中知道它的确如此,所以如果你写的话

int size_i_need = 10;
char *buf = malloc(size_i_need);

编译器将根据需要插入从int到size_t的适当转换。 (我不相信我在这里有警告我也要担心。)

如果您使用的变量已经unsigned,那就更好了!

同样,如果你要写

fgets(buf, sizeof(buf), ifp);

编译器将再次插入适当的转换。在这里,我想我看到了你所得到的,64位编译器可能会发出关于从long到int的下转换的警告。现在我想起来了,我不确定为什么我没有这个问题,因为这是一个常见的习语。

(您还询问了如何将unsigned long传递给malloc,并且在size_t小于long的机器上,我想这也可能会给您发出警告。那是你担心的吗?)

如果你有一个你无法避免的缩小尺寸,并且你的编译器或其他工具警告它,你想要安全地摆脱警告 ,你可以使用演员表和断言。也就是说,如果你写

unsigned long long size_i_need = 23;
char *buf = malloc(size_i_need);

这可能会在size_t为32位的机器上发出警告。所以你可以用一个演员来沉默警告(假设你的无符号long long值永远不会太大),然后通过调用assert来支持你的假设:

unsigned long long size_i_need = 23;
assert(size_i_need <= SIZE_MAX);
char *buf = malloc((size_t)size_i_need);

根据我的经验,最大的麻烦就是将这些东西打印出来。如果你写

printf("int size = %d\n", sizeof(int));

printf("string length = %d\n", strlen("abc"));
在64位计算机上,现代编译器通常(并且正确地)警告您&#34; format指定类型&#39; int&#39;但是这个论点有类型&#39; unsigned long&#39;&#34;等等。您可以通过两种方式解决此问题:将值转换为与printf格式匹配,或更改printf格式以匹配值:

printf("int size = %d\n", (int)sizeof(int));
printf("string length = %lu\n", strlen("abc"));

在第一种情况下,您假设sizeof的结果适合int(这可能是一个安全的赌注)。在第二种情况下,您假设size_t实际上是unsigned long,这在64位编译器上可能是正确的,但在某些其他情况下可能不正确。因此,在第二种情况下使用显式强制转换实际上更安全:

printf("string length = %lu\n", (unsigned long)strlen("abc"));

最重要的是,像size_t这样的抽象类型不能与printf一起使用;这是我们可以看到cout << "string length = " << strlen("abc") << endl的C ++输出样式有其优势的地方。

要解决此问题,我们可以保证一些特殊的printf修饰符与size_t匹配,我认为off_t和其他一些抽象类型,尽管它们并非如此众所周知。 (我不确定在哪里查找它们,但是当我一直在撰写这个答案时,一些评论者已经提醒过我了。)所以打印这些东西的最佳方式(如果你记得的话,除非你使用旧的编译器,否则

printf("string length = %zu\n", strlen("abc"));

底线:

  1. 您显然不必担心将普通int或普通unsigned传递给期望calloc的{​​{1}}这样的函数。
  2. 调用可能导致向下转换的内容时,例如将size_t传递给size_t fgets为64位但size_t为32,或者传递{{} 1}}到int unsigned long long只有32位,您可能会收到警告。如果你不能使传入的类型更小(在一般情况下,你将无法做到),你几乎没有选择让警告静音但是插入一个投。在这种情况下,为了严格正确,您可能需要添加一些断言。
  3. 所有这些都说明了,我不确定我是否真的回答了你的问题,所以如果你想进一步澄清,请询问。