因此,我已经尝试了一段时间的指针,并提出了一些问题。在下面的代码中,首先,当我打印每个内存地址时:
pHours ++-pHours-pHours + sizeof(char),得到以下结果:
1989086632-1989086636-1989086636.
pHours ++的值不应该比pHours的值大而不是小4个单位(顺便说一句,C通常不分配1个字节而不是4个字节来存储字符吗?)
此外,为什么pHours + sizeof(char)的值与pHours的相同?在我下面编写的代码中,很明显,加上sizeof(char)确实会更改指针指向的地址,因为当我在函数的最后一个打印语句中忽略它时,会遇到分段错误(* nHours没有价值)。
另外,为什么当我编写nHours ++时却出现分段错误,而对skata(skata ++)或pHours(pHours ++)却如此却没有呢?
哦,还有两件事。 Visual Studio的编译器给我一个错误,因为在使用它们之前不初始化* nHours和* skata,但允许使用在线编译器进行初始化。为什么这是VS中的强制性做法?
另外,为什么最好使用格式字符串%p而不是%d来引用内存地址?
目前为止就这样了。先感谢您! :)
#include <stdio.h>
#define MinutesPerHour 60
void ConvertTimeToHM(int time, int *pHours, int *pMinutes);
int main()
{
int time, hours, minutes;
printf("Enter a time duration in minutes: ");
scanf("%d", &time);
ConvertTimeToHM(time, &hours, &minutes);
printf("HH:MM format: %d:%02d\n", hours, minutes);
return 0;
}
void ConvertTimeToHM(int time, int *pHours, int *pMinutes)
{
*pHours = time / MinutesPerHour;
int *nHours, *skata, *kHours;
*pMinutes = time % MinutesPerHour;
//nHours = pHours;
//nHours++;
skata++;
printf("%d %d %d\n", pHours++, pHours, pHours + sizeof(char));
skata = nHours + 3 * sizeof(char);
*skata = 19;
printf("%d\n", *(nHours + 3 * sizeof(char)));
}
答案 0 :(得分:0)
此外,为什么pHours + sizeof(char)的值与pHours的值相同?
因为您在pHours
语句中通过尝试更新++
来调用了未定义行为,并且在没有中间序列的情况下将其值用于计算点(也用于错误的转换说明符)。不能保证函数参数从左到右进行求值,也不能保证%f
的结果立即得到求值。
将其分解为多个语句,您将看到不同的结果。
Visual Studio的编译器给我一个错误,因为在使用它们之前不初始化* nHours和* skata,但允许使用在线编译器进行初始化。为什么在VS中必须这样做?
要求编译器发布语法错误和约束违规的诊断-除此之外,这取决于各个供应商。
此外,为什么最好使用格式字符串%p而不是%d来引用内存地址?
出于相同的原因,我们将%s
用作双精度,将%c
用作字符串,将{{1}}用作单个字符-因为语言规范是这样说的。指针不是整数;尽管它们可能在许多系统上都有完整的表示形式,但这并不能保证。
答案 1 :(得分:0)
pHours ++的值不应为4个单位
我不确定您正在阅读哪本书,但是不管它是什么,它都不适合您...
给出int *p;
,p+0
等效于&p[0]
,p+1
等效于&p[1]
,依此类推。从逻辑上讲,(p + 1) - (p + 0)
必须为1
,但是1
并不表示char
;它表示一个int
,因为这就是您要指向的类型。
另一方面,如果要在pHours
之前和之后将char *
强制转换为void *
(或pHours
),则可能可以看到更多与您期望的类似的东西……但是可能也可能是1
,例如CHAR_BIT
是32。
(顺便说一句,C通常不分配1个字节而不是4个字节来存储字符吗?)
是的,在C语言中,char
总是 恰好占据一个字节,因此sizeof (char)
总是 1
... 但是字节不必是八位字节;您可以使用CHAR_BIT
获得字节中的位数,根据C标准,该位数必须至少为8,并且必须为16
和32
一些系统,所以...
比pHours的值大而不是小的?
p++
的值是递增前的p
。递增后,++p
的值为p
。
此外,为什么pHours + sizeof(char)的值与pHours的值相同?
在下面的代码中,您将调用未定义的行为,因此不应过多地阅读它。
printf("%d %d %d\n", pHours++, pHours, pHours + sizeof(char));
您需要注意,未指定参数的评估顺序,因此下一个参数中的pHours++
后跟pHours
是未定义的行为(即,可能导致不可预测的结果,包括崩溃,安全性受到损害,等等)……还告诉printf
打印一个int
值,然后传递一个int *
值:这也是UB。您可能打算写:printf("%p", (void *) pHours++); printf("%p %p\n", pHours, pHours + sizeof (char));
。
此外,为什么在编写nHours ++时出现分段错误,而对skata(skata ++)或pHours(pHours ++)执行相同操作却没有?
未定义的行为并不意味着会产生细分错误;这将是定义的行为。未定义的行为意味着...好吧,让我们从未定义开始...缺乏定义...对吗?这意味着它没有任何意义。我要在这里说的是您的代码的行为没有任何意义。允许 进行段错误,但实际上不是必不可少的。
这说明了缓冲区溢出,不一定导致段错误;他们可能会因为产品正常运行多年而没有被注意,并且没有人对其进行探测,因此,黑客发现了一个可疑的段错误,看起来可疑的缓冲区溢出……并设法渗透到您的代码库中……不,段错误肯定不是 not 在这里是必需的...但是没有任何逻辑上的行为。
您从哪里得到期望?我担心您实际上可能不是正在阅读,但是是猜测 ...请告诉我您不是猜测 ...这并不安全那样学习C!相信我;我去过那里并且做到了,而您也收到了一些与我相同的误解。如果我是对的,而您不是在读一本书,那就考虑出去找一本K&R2E的副本。记得做练习。
Visual Studio的编译器给我一个错误,因为在使用它们之前不初始化* nHours和* skata,但允许使用在线编译器进行初始化。为什么在VS中必须这样做?
Visual Studio正在检测使用未初始化值的未定义行为...并且在这一点上,Visual Studio不需要遵循C标准,因为您已经超出了C领域,因此可以在那时停止编译。
此外,为什么最好使用格式字符串%p而不是%d来引用内存地址?
%p
告诉printff
期望一个void *
参数,这很重要,因为printff
需要知道它正在对void *
参数进行操作... %d
告诉printff
期望一个int
参数...两种类型具有不同的用例。不要混在一起。如果传递了错误的参数类型,这是未定义的行为。