我是 Linux 新手。我gcc
在 Ubuntu 12.04 LTS 上使用 (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3。当我使用指针编译 c 程序时,我收到-Wformat
如下所示的警告。但如果我执行a.out
文件,我会得到正确的结果。有人能告诉我为什么我会收到这条消息并建议我应该怎么做才能克服吗?
我的测试程序:
#include<stdio.h>
void main(void)
{
int x=10,y,z;
int *p=&x ;
printf("\n\np = %u\n",p);
printf("\n*p = %u\n",*p);
printf("\n&x = %u\n",&x);
printf("\n&y = %u\n",&y);
printf("\n&z = %u\n",&z);
printf("\n&p = %u\n\n",&p);
}
输出:
qust1-Array.c:11:2: warning: format ‘%u’ expects argument of type ‘unsigned int’, but argument 2 has type ‘int *’ [-Wformat]
qust1-Array.c:14:2: warning: format ‘%u’ expects argument of type ‘unsigned int’, but argument 2 has type ‘int *’ [-Wformat]
qust1-Array.c:15:2: warning: format ‘%u’ expects argument of type ‘unsigned int’, but argument 2 has type ‘int *’ [-Wformat]
qust1-Array.c:16:2: warning: format ‘%u’ expects argument of type ‘unsigned int’, but argument 2 has type ‘int *’ [-Wformat]
qust1-Array.c:17:2: warning: format ‘%u’ expects argument of type ‘unsigned int’, but argument 2 has type ‘int **’ [-Wformat]
答案1
你之所以收到警告,是因为你使用错误的格式说明符在 中printf()
。p
是一个整数指针。&p
是指针的地址。&x
和&y
是整数的地址。这些都是内存中的地址,而不是变量的值。说明符%u
用于无符号整数的值。因此,您在编译器期望橙子的地方打印了苹果。地址比存储在变量中的某些值短。使用%u
实际上会将地址值打印为十进制(极不寻常)以及位于内存中该地址后面的一些数据。编译器会抱怨,因为这可能不是您想要做的。要打印地址,请使用说明符%p
,如下所示:
printf("\n&x = %p\n",&x);
除此之外,您的变量是有符号整数,因此应使用%i
而不是%u
。%u
的格式说明printf()
符仅适用于正整数。 对于较小的正值,%i
和%u
都可以互换。显示警告是因为变量类型与其说明符不匹配,这在某些情况下会导致问题。
从变量类型来看这会更合理:
printf("\n\np = %p\n", p); // p is a pointer so %p would print the address
printf("\n*p = %i\n", *p); // the data saved at that address is an integer
// so %i is appropriate if you dereference the
// pointer with the star "*p"
printf("\n&x = %p\n", &x); // &x gives the address of the integer variable x
// so %p is the specifier for that address
printf("\n&y = %p\n", &y);
printf("\n&z = %p\n", &z);
printf("\n&p = %p\n\n", &p); // &p gives the address, where the pointer p is
// stored -> still an address -> %p is the right
// specifier
关于有符号和无符号整数以及指针的一些背景知识:
C 使用相同的 32 位(或根据系统架构的不同,另一个 2 的幂)来存储无符号和有符号整数。因此最高unsigned int
是 2 32 -1 或二进制符号:
2 32 -1 = (111111111111111111111111111111111111) 2 <- (无符号)
数字 1 在二进制中如下所示:
1 = (00000000000000000000000000000000001) 2 <- (无符号)
现已常规有符号整数也需要存储负数,但仍然在相同的 32 位空间中。如果你将数字的符号存储在第一位,那么你将丢失整整一个位。这将是浪费,例如零会有两种表现形式作为 + 和 - 零。为了规避这个问题有符号整数中的负数的存储方式略有不同:要将数字编码为有符号整数,您需要将 32 位数的可能范围减半。这是 2 (32-1) ,然后使用该新数字的常规二进制表示。因此,1 的编码方式与无符号整数的编码方式相同。我们有:
2 (32-1) = (111111111111111111111111111111111111) 2 <-有符号
...
1 = (10000000000000000000000000000000001) 2 <- 有符号
0 = (10000000000000000000000000000000000) 2 <- 有符号
-1 = (011111111111111111111111111111111111) 2 <- 有符号
...
-2 (32-1) = (0000000000000000000000000000000000) 2 <-有符号
现在,您已经编码了相同数量的整数,但有符号整数的最大数仅为 2 (32-1),而无符号整数的最大数是 2 32 -1。这称为过量钾或者偏移二进制表示负数。大多数系统使用二进制补码,其中第一个最高有效位被反转。
要查看此内容,请设置x=-1;
然后printf("%u",x)
。您将获得以下输出:
2147483648
用二进制表示就是 2 32-1或 (0111111111111111111111111111111111111) 2。用二进制补码表示就是:
4294967295
或 2 32 -1。这等于二进制的 (111111111111111111111111111111111111) 2,因此与上面的超额 K 值 2147483648 相比,它的第一位是反转的。
所以这是如何数据存储。当你想到在哪里内存中的物理位必须有地址。由于物理位的数量非常多,因此您需要以多于一位的块来寻址它们。如果您创建了一个指针,则指针地址处的物理内存将保存内存中的另一个地址。因此,房子是一个物理对象,就像 PC 内存中的一个位一样。一张纸就是一个指针。它比房子小,但可以保存一所房子或其他房子的地址。在这个比喻中,上面你会试图拆除那张纸而不是实际的房子,而它实际上是一座山……