所以我有main
:
#define NUM 5
int main()
{
int a[NUM]={20,-90,450,-37,87};
int *p;
for (p=a; (char *)p < ((char *)a + sizeof(int) * NUM); ) //same meaning: for (p=a; p<a+NUM;)
*p++ = ++*p < 60 ? *p : 0; //same meaning: *(p++)=++(*p)<60?*p:0;
for(p=a; (char *)p < ((char *)a + sizeof(int) * NUM); )
printf("\n %d ", *p++);
return 0;
}
我需要找出输出是什么。 因此,在尝试理解之后,我没有任何想法,我运行它,这是输出:
21
-89
0
-36
0
所以我很乐意解释如何解决这类问题(我很快就会参加考试,我可能会看到这类问题......)
编辑:
一开始我想了解第一个for
语句在做什么:
这跳1 integer
?这会在街区内发生什么?
*p++
和++*p
答案 0 :(得分:4)
问题类似于Why are these constructs (using ++) undefined behavior in C?,但由于?:
运算符内的(微妙)序列点而不是完全重复。
由于程序包含未定义的行为,因此没有可预测的输出。
虽然由于++*p
运算符的内部序列点,子表达式*p
以明确定义的方式与?:
进行排序,但对于其他运算符则不然子表达式的组合。最值得注意的是,未指定操作数到=
的评估顺序:
C11 6.5.15 / 3:
对操作数的评估是不合理的。
*p++
未按++*p
排序。子表达式的评估顺序未指定,并且由于同一变量存在多个未测序的副作用,因此行为未定义。
同样,*p++
与*p
无关。这也会导致不确定的行为。
总结:代码被破坏并且充满了错误。任何事情都可能发生。无论谁给你你的任务都是无能的。
答案 1 :(得分:3)
一开始我想了解第一个声明做什么
这就是人们所谓的代码混淆......困难的部分显然是这个:
(char *)p < ((char *)a+sizeof(int)*NUM);
好的,我们将p转换为指向char的指针,然后将它与从数组a中检索到的另一个指针进行比较,该指针指向第一个元素经过a:sizeof(int)*NUM
是数组的大小 - 我们可以得到它只需sizeof(a)
,(char*)p < (char*)a + sizeof(a)
请注意,如果指针没有指向同一个数组或者指向后者的末尾,那么比较除(in-)相等之外的指针是未定义的行为(尽管如此,它们也是如此)。
通常,如果您愿意,可以将此比较设为p < a + sizeof(a)/sizeof(*a)
(或sizeof(a)/sizeof(a[0])
。
*p++
递增指针并在之后取消引用它,它是p = p + 1; *p = ...
的缩写。 ++*p
,另一方面,首先取消引用指针并递增它指向的值(注意差异为*++p
,另一个变体 - 你能自己得到它吗?),i。即它相当于*p = *p + 1
。
整行*p++ = ++*p<60 ? *p : 0;
然后将执行以下操作:
*p
*p
p
然而,这是未定义的行为,因为p
的读写访问之间没有序列点;您不知道是否首先评估作业的左侧或右侧,在前一种情况下,我们将分配a[i] = ++a[i + 1]
,在后一种情况下,a[i] = ++a[i]
!您可能已经与另一个编译器获得了不同的输出!!!
然而,这些只是两个最可能的输出 - 实际上,如果落入未定义的行为,任何可能会发生,编译器可能只是忽略有问题的代码,决定不做任何事情(只是作为第一条指令从主右侧退出),程序可能会崩溃,甚至可能关掉太阳......
请注意,具有未定义行为的单个位置会导致整个程序本身具有未定义的行为!
答案 2 :(得分:0)
真正理解这种东西(内存管理,指针行为等)的唯一方法就是试验自己。无论如何,我闻到有人试图看起来聪明愚弄学生,所以我会尽量澄清一些事情。
int a[NUM]={20,-90,450,-37,87};
int *p;
内存中的这种结构类似于:
这创建了一个五int
的向量,到目前为止,非常好。鉴于数据,明显的举措是在使用p
的元素上运行。您将执行以下操作:
for(p = a; p < (a + NUM); ++p) {
printf("%d ", *p);
}
然而,要注意的第一个变化是两个循环都将指针转换为char
。所以,他们会:
for (p=a;(char *)p<((char *)a+sizeof(int)*NUM); ++p) {
printf("%d ", *p);
}
而不是使用指向a
的指针指向int
,代码会将p
转换为指向char
的指针。说你的机器是32位的。那么int
可能会占用四个字节。当p
是int
的指针时,当你执行++p
时,你会有效地转到a
中的下一个元素,因为透明地你的编译器将跳转四个字节。如果您将int
指针转换为char
,则无法添加NUM
并假设您已成为数组的末尾:char
存储在一个byte,所以((char *)p) + 5
将指向a
的第二个元素中的第二个字节,前提是它指向a
的开头。这是你必须调用sizeof(int)
并将其乘以NUM
的方法,以获得数组的结尾。
最后,臭名昭着的*p++ = ++*p<60 ? *p : 0;
。这对于面对学生来说是不公平的,因为正如其他人已经指出的那样,该代码的行为是不确定的。让我们通过表达来表达。
++*p
表示&#34;访问p
并在结果中添加1。如果p
指向a
的第一个位置,则结果为21. ++*p
不仅会返回21,还会在您拥有20的位置的内存中存储21。如果你只回到21,你会写; *p + 1
。
++*p<60 ? *p : 0
表示&#34;如果p
指向的值永久加1的结果小于60
,则返回该结果,否则返回0。
*p++ = x
表示&#34;将x
的值存储在p
指向的内存地址中,然后递增p
。这就是为什么你在++p
循环的增量部分找不到p++
或for
的原因。
现在关于整个指令(*p++ = ++*p<60 ? *p : 0;
),它是未定义的行为(查看@Lundin的答案以获取更多详细信息)。总之,最明显的问题是您不知道将首先评估赋值运算符周围的哪个部分(左侧或右侧)。你甚至不知道如何评估赋值运算符右边的表达式中的子表达式(哪个顺序)。
你怎么能解决这个问题?这实际上非常简单:
for (p=a;(char *)p<((char *)a+sizeof(int)*NUM); ++p) {
*p = (*p + 1) <60 ? (*p + 1) : 0;
}
更具可读性。更好的是:
for (p = a; p < (a + NUM); ++p) {
*p = (*p + 1) <60 ? (*p + 1) : 0;
}
希望这有帮助。
答案 3 :(得分:0)
简短回答:因为这行
*p++ = ++*p<60 ? *p : 0;
无法说出程序的行为方式。当我们在右侧访问*p
时,是否使用p
的旧值或新值,即在左侧的p++
之前或之后获取对吗? C中没有任何规则可以告诉我们。相反,有一条规则说,由于这个原因,代码未定义。
不幸的是,提出这个问题的人并不理解这一点,认为&#34;棘手&#34;代码行这可以解决问题,而不是不惜一切代价避免的事情。