请参阅以下代码段:
int len = -2;
char* buff = (char*) malloc(len+4);
if (len > sizeof(buff))
puts("ERROR!");
else
puts("OK!");
使用GCC 4.8.2在Ubuntu-14.04(64位)上编译和运行此代码,打印ERROR
。
我使用以下代码打印len
和sizeof(buf)
的值:
printf("len = %d, size = %lu", len, sizeof(buff));
并打印:
len = -2, size = 8
更改len
的值不会影响sizeof(buff)
的值,即使是len
也不会产生影响。
如果我没有弄错,值8
是我的64位机器上指针地址的长度,无论我给malloc
什么,它都是常量。如果是这样,我有两个问题:
1)为什么以上if
语句打印ERROR
? (因为-2不大于8 !!)
2)为什么以下代码不打印8
?
char array[10];
printf("%lu", sizeof(array));
此代码打印数组的长度。来自char[]
的{{1}}和char*
之间有什么区别?我知道第一个是在堆栈上分配的,后者是在堆上动态分配的,但无论如何它们都是系统内存的指针地址。我不理解malloc
相对于来自sizeof
的{{1}}和char[]
的不同行为!这似乎不一致!
答案 0 :(得分:4)
您的代码有什么问题?
int len = -2;
char* buff = (char*) malloc(len+4); /* don't cast malloc not wrong, but might hide bugs. */
if (len > sizeof(buff)) /* sizeof() is unsigned and len is signed */
puts("ERROR!");
else
puts("OK!");
您将signed
值与unsigned
值进行比较,并且因为unsigned
wrap around
unsigned int x = -1;
然后x > 0
总是正确的,而且我认为x > len + 4
几乎肯定是你要比较的,但肯定是x > sizeof(char *)
x > sizeof(buff)
这意味着你的情况
另外,sizeof()
给出了类型的大小,在你的情况下buff
是一个指针,然后它是一个指针的大小,让代码工作吗
使用gcc
警告,它会告诉您signed
unsigned
的比较。
gcc -Wall -Wextra -Werror
注意,-Werror
会将警告视为错误,并在发出警告时中止编译。
如果你想测试一下,就这样试试吧
int len = -2;
char* buff = (char*) malloc(len+4); /* don't cast malloc not wrong, but might hide bugs. */
if (len > (int)sizeof(buff)) /* sizeof() is unsigned and len is signed */
puts("ERROR!");
else
puts("OK!");
但请记住,无论sizieof(buff)
的值如何,您机器中的8
都为len
,您无法计算malloc
ed块的长度,你需要存储它的长度以供后续使用。
如果需要,可以创建一个结构来保存长度和数据。
答案 1 :(得分:4)
if (len > sizeof(buff))
sizeof
生成类型为size_t
的值,该值为无符号。将其与负int
进行比较时,负值将提升为非常大的无符号值。 (无符号类型始终使用modular arithmetic,并且它们在二进制运算中胜过签名类型。)因此,-2
“大于”sizeof
可以获得的任何内容。
更改len的值对sizeof(buff)的值没有影响,即使对于正len也没有影响。
sizeof(buff)
是指针的大小,而不是分配块的大小。您需要将其保存在自己的变量中,因为C不会跟踪分配大小。
来自
char[]
的{{1}}和char*
之间有什么区别?
malloc
是一个数组,其大小取决于其中的元素数量。 char[]
是一个指针。数组可以在带有指针的上下文中使用,但这不会使它本身成为指针。
char*
或sizeof( &* array )
都与sizeof( & array[0] )
具有相同的值。大小是变量的属性,而不是内存块的属性。
答案 2 :(得分:2)
1)为什么以上if语句打印ERROR? (因为-2不是 大于8 !!)
在if语句的条件表达式
中if (len > sizeof(buff))
puts("ERROR!");
类型size_t
(通常定义为unsigned long
)对应于运算符sizeof
的返回值的类型,其排名高于类型为{{1的变量len的类型}}。因此,要根据通常的算术转换的规则将公共类型len转换为类型int
,并将其视为无符号整数值,该值大于sizeof(buff)返回的值
[注意:size_t
的内部表示可以看起来(为简单起见我只使用一个字节),如
-2
而11111110
看起来像是
8
因此,如果将00001000
的内部表示视为某些无符号值,则-2
显然大于-2
。 - 结束注释]
来自C标准(6.3.1.8常规算术转换)
1许多期望算术类操作数的运算符会导致 以类似的方式转换和生成结果类型。目的是为了 确定操作数和结果的通用实数类型。为了 指定的操作数,每个操作数都被转换,不改变类型 域,对应的实际类型是普通实数的类型 类型。除非另有明确说明,否则常见的实际类型也是如此 结果的相应实际类型,其类型域为 操作数的类型域,如果它们是相同的,并且是复杂的 除此以外。这种模式称为通常的算术转换:
和
否则,如果两个操作数都有有符号整数类型或两者都有 无符号整数类型,操作数类型为较小的整数 转换等级转换为具有更大的操作数的类型 秩。
否则,如果具有无符号整数类型的操作数具有等级 那么,大于或等于另一个操作数的类型的等级 带有符号整数类型的操作数转换为 具有无符号整数类型的操作数。
如果您将8
定义为具有类型len
,那么条件将等于false是可能的,因为可能发生long long的等级大于{的等级{1}}通常被定义为类型long long
尝试以下代码段
size_t
2)为什么以下代码不打印8?
unsigned long
运算符long long int len = -2;
char* buff = (char*) malloc(len+4);
if (len > sizeof(buff))
puts("ERROR!");
else
puts("OK!");
以字节为单位返回用作运算符操作数的对象的大小。变量数组定义为10个char类型元素的数组。在任何实现中,sizeof(char)等于1.因此10 * sizeof(char)将导致10。
考虑到数组不是指针。