当程序中存在size_t
,int
和unsigned long int
等其他数据类型时,我对如何使用unsigned long long int
感到有些困惑。我试着最低限度地说明我的困惑。想象一下我使用的程序
void *calloc(size_t nmemb, size_t size)
分配一个数组(一维或多维)。让calloc()
的来电取决于nrow
和sizeof(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()
答案 0 :(得分:3)
我对其他数据类型如何使用size_t感到有些困惑 int,unsigned long int和unsigned long long int存在于a中 程序
忽略警告永远不是一个好主意。警告会引起您注意代码中可能存在问题的区域。最好花几分钟时间来了解警告告诉你的内容 - 然后修复它,然后在你遇到角落情况时陷入困境并陷入未定义的行为。
size_t
本身就像其他任何数据类型一样。虽然它可能会有所不同,但通常只有unsigned int
覆盖可以由int
表示的正值范围,包括0
(类型大小旨在跨平台保持一致) ,每个上的实际字节可能不同)。您选择的数据类型是编程的基本和基本部分。您可以根据变量可以表示的值范围(或应限于表示)来选择类型。因此,如果您处理的任何内容不能为负,那么unsigned
或size_t
是正确的选择。然后,该选择允许编译器帮助识别代码可能导致违反的区域。
当您在每次编译时都应该使用警告(例如-Wall -Wextra
)进行编译时,您将收到有关数据类型使用中可能存在的冲突的警告。 (即signed
和unsigned
值之间的比较等...)这些重要!
几乎所有的现代 x86&amp; x86_64 计算机对签名值使用 twos-compliment 表示。简单来说,这意味着如果有符号数的最左边的位是1
,则值为负数。这里有一个微妙的陷阱,你可能在混合/铸造或比较不同类型的数字时陷入其中。如果您选择将unsigned
号码转换为signed
号码并且该号码恰好填充了最重要的位数,那么您的大号码就会变得非常小。
在这两种情况下我该怎么办?投射,或者只是忽略可能 警告...
每次遇到来自编译器的警告时,您都会执行操作。你分析导致警告的原因,然后你修复它(或者如果你不能解决它 - (即来自你无法访问的某些库) - 你能够很好地理解警告,你可以知道你不会遇到任何可能导致不确定行为的角落案件,做出明智的决定而无视它。
在您的示例中(虽然两者都不会产生警告,但可能会出现在某些编译器上):
arr = calloc (mrow, sizeof(long int *));
sizeof(long int *)
的范围是多少?嗯 - 这是指针大小的范围。那是什么? (4 bytes
上的x86
或8 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 unsigned
和fgets
int
22
,int
可以投放到{{1} }}
答案 1 :(得分:1)
我的另一个回答太长了,所以这里的篇幅很短。
size_t
。 (同样,如果您有涉及文件大小或偏移的内容,请使用off_t
。)
assert(size_i_need <= SIZE_MAX);
char *buf = malloc((size_t)size_i_need);
答案 2 :(得分:1)
何时施放size_t
你不应该。
在适当的地方使用它。
如果有疑问的类型符合您的程序需求,您可以根据Steve Summit的answer找到有用的断言语句,如果它失败则重新开始您的程序设计。
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"));
底线:
int
或普通unsigned
传递给期望calloc
的{{1}}这样的函数。size_t
传递给size_t
fgets
为64位但size_t
为32,或者传递{{} 1}}到int
unsigned long long
只有32位,您可能会收到警告。如果你不能使传入的类型更小(在一般情况下,你将无法做到),你几乎没有选择让警告静音但是插入一个投。在这种情况下,为了严格正确,您可能需要添加一些断言。所有这些都说明了,我不确定我是否真的回答了你的问题,所以如果你想进一步澄清,请询问。