英文:
Inverted struct order when assigning values to nested structs
问题
I see the issue in your code. It looks like the problem lies in how you're accessing the vertex coordinates in your loop. Instead of:
printf("Vertex coords = %d, %d, %d \n\n", triangleTest.vertex[loop]);
You should access each coordinate individually, like this:
printf("Vertex coords = %d, %d, %d \n\n", triangleTest.vertex[loop].x, triangleTest.vertex[loop].y, triangleTest.vertex[loop].z);
This will correctly print the coordinates as you intended.
英文:
I'm compiling C code with nested structs for a 3D engine, and curiously the values I'm assigning to the structures are being inverted when the program is run.
Example code:
#include <stdio.h>
struct vertexes // Creates a structure vertexes with 3 coordinates in space
{
unsigned short x, y, z; // 2 bytes variables, restricted to 0-65535
};
struct triangles // Creates a structure triangles
{
struct vertexes vertex[3]; // A triangle is made of 3 vertexes, so we create p arrays of three vertexes
};
main()
{
struct vertexes vertexTest = {1, 2, 3};
printf("x = %d, y = %d, z = %d \n\n", vertexTest.x, vertexTest.y, vertexTest.z);
struct triangles triangleTest = {{{1, 2, 3}, {0, 100, 0}, {100, 100, 0}}};
for (int loop = 0; loop < 3; ++loop)
{
printf ("Loop %d \n", loop);
printf("Vertex coords = %d, %d, %d \n\n", triangleTest.vertex[loop]);
}
}
The output I get:
x = 1, y = 2, x = 3 (which is fine)
Loop 0
Vertex coords = 3, 2, 1 (here lies the problem: I've assigned 1, 2, 3!)
Loop 1
Vertex coords = 0, 100, 0 (well, you can't really invert this)
Loop 2
Vertex coords = 0, 100, 100 (again, inverted: I've assigned 100, 100, 0)
What am I doing wrong?
答案1
得分: 3
问题出在这里:
printf("Vertex coords = %d, %d, %d \n\n", triangleTest.vertex[loop]);
这两个操作:
* 在堆栈上放置了一个 `struct vertexes` 类型的结构
* 告诉 `printf` 从堆栈上弹出3个整数
这段代码基本上假定你的平台将变长参数按照与你原始结构相同的顺序和对齐方式放在堆栈上。在很多平台上,[第一个参数被最后压入](https://stackoverflow.com/questions/18690322/what-is-the-point-of-passing-arguments-in-the-reverse-order-in-c)。
行为将依赖于平台。在我的系统上(Ubuntu Linux),我得到以下结果:
x = 1, y = 2, z = 3
循环 0
Vertex coords = 131073, 131073, 131072
循环 1
Vertex coords = 6553600, 6553600, 6553600
循环 2
Vertex coords = 6553700, 6553700, 6553600
也就是说,只是一堆垃圾。
你该怎么修复这个问题呢?你需要让 `printf` 的格式字符串和你提供给 `printf` 的内容匹配。
printf("Vertex coords = %d, %d, %d \n\n",
triangleTest.vertex[loop].x,
triangleTest.vertex[loop].y,
triangleTest.vertex[loop].z
);
顺便说一句,你确定已经打开了编译器警告吗?任何编译器都应该能够在 `printf` 的参数数量和格式说明符的数量不匹配时给出警告。当我在gcc上开启警告时,它显示以下内容:
test320.c: In function ‘main’:
test320.c:24:34: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘struct vertexes’ [-Wformat=]
24 | printf("Vertex coords = %d, %d, %d \n\n", triangleTest.vertex[loop]);
| ~^ ~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| int struct vertexes
test320.c:24:38: warning: format ‘%d’ expects a matching ‘int’ argument [-Wformat=]
24 | printf("Vertex coords = %d, %d, %d \n\n", triangleTest.vertex[loop]);
| ~^
| |
| int
test320.c:24:42: warning: format ‘%d’ expects a matching ‘int’ argument [-Wformat=]
24 | printf("Vertex coords = %d, %d, %d \n\n", triangleTest.vertex[loop]);
| ~^
| |
| int
你的编译器可能有类似的功能。
你真的应该打开警告。它们会节省很多时间。当我学习C语言时,我曾经认为警告并不是很重要。但当我花了[三天来调试一个程序](https://stackoverflow.com/questions/17287918/finline-functions-breaks-my-code),我本可以在三分钟内捕捉到问题,只要我启用了警告。
英文:
The problem lies here:
printf("Vertex coords = %d, %d, %d \n\n", triangleTest.vertex[loop]);
This does two things:
- puts a structure of type
struct vertexes
on the stack - tells
printf
to pop 3 integers off the stack
This code is essentially assuming your platform puts varargs on the stack in the same order and alignment as your original structure. On a lot of platforms, the first argument is pushed last.
The behavior will be platform dependent. On my system, (Ubuntu Linux) I get the following result:
x = 1, y = 2, z = 3
Loop 0
Vertex coords = 131073, 131073, 131072
Loop 1
Vertex coords = 6553600, 6553600, 6553600
Loop 2
Vertex coords = 6553700, 6553700, 6553600
i.e. just a bunch of garbage.
How do you fix this? You need to make the format string for printf
, and the thing you're providing to printf
match.
printf("Vertex coords = %d, %d, %d \n\n",
triangleTest.vertex[loop].x,
triangleTest.vertex[loop].y,
triangleTest.vertex[loop].z
);
Speaking of making sure these match, do you have compiler warnings turned on? Any compiler should be able to flag when the number of arguments to printf
, and the number of format specifiers don't match. When I turn on the warnings on gcc, it shows the following:
test320.c: In function ‘main’:
test320.c:24:34: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘struct vertexes’ [-Wformat=]
24 | printf("Vertex coords = %d, %d, %d \n\n", triangleTest.vertex[loop]);
| ~^ ~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| int struct vertexes
test320.c:24:38: warning: format ‘%d’ expects a matching ‘int’ argument [-Wformat=]
24 | printf("Vertex coords = %d, %d, %d \n\n", triangleTest.vertex[loop]);
| ~^
| |
| int
test320.c:24:42: warning: format ‘%d’ expects a matching ‘int’ argument [-Wformat=]
24 | printf("Vertex coords = %d, %d, %d \n\n", triangleTest.vertex[loop]);
| ~^
| |
| int
Your compiler probably has something similar.
You should really turn on warnings. They save a lot of time. I used to think, when I was learning C, that warnings didn't really matter. That changed when I spent three days debugging a program that I could've caught in three minutes had I enabled warnings.
答案2
得分: 2
printf("顶点坐标 = %d, %d, %d \n\n",
triangleTest.vertex[loop].x,
triangleTest.vertex[loop].y,
triangleTest.vertex[loop].z);
英文:
You have to explicitly print each vertex's coordinates like so:
printf("Vertex coords = %d, %d, %d \n\n",
triangleTest.vertex[loop].x,
triangleTest.vertex[loop].y,
triangleTest.vertex[loop].z);
答案3
得分: 2
每个%d
的printf
格式说明符,你必须传递正好一个相应的int
参数。
在这行代码中:
printf("Vertex coords = %d, %d, %d \n\n", triangleTest.vertex[loop]);
你违反了这个规则,因为你使用了三个%d
说明符,但只有一个参数,并且类型也是错误的。它的类型是struct vertexes
,而不是int
类型。
由于你违反了这个规则,你的程序有未定义的行为,这意味着任何事情都可能发生,包括你在问题中描述的行为。
关于你测试程序时数值顺序被倒置的可能解释,请参考其他答案。然而,你不能依赖于数值被倒置,因为如上所述,在调用未定义行为时,任何事情都可能发生。
如果你希望程序的行为是可预测的,那么你不应该触发未定义的行为,可以像下面这样遵循上述规则:
printf(
"Vertex coords = %d, %d, %d \n\n",
triangleTest.vertex[loop].x
triangleTest.vertex[loop].y
triangleTest.vertex[loop].z
);
尽管这些参数的类型是unsigned short
而不是int
,但这是可以接受的,因为将unsigned short
传递给printf
时,它会被隐式提升为int
类型。
然而,我建议对于unsigned short
数据类型使用%hu
而不是%d
,因为这样,你可以在printf
和scanf
系列函数中使用相同的格式说明符,而且不需要考虑int
的提升问题。
有关格式说明符的更多信息,请参阅printf
和scanf
的文档。
英文:
For every %d
printf
format specifier that you use, you must pass exactly one corresponding int
argument.
In the line
printf("Vertex coords = %d, %d, %d \n\n", triangleTest.vertex[loop]);
you are violating this rule, because you are using three %d
specifiers, but only one argument, which is also of the wrong type. It is of type struct vertexes
, not of type int
.
Due to you violating this rule, your program has undefined behavior, which means that anything can happen, including the behavior you describe in the question.
See one of the other answers for a possible explanation of why the order of the values are inverted, when you test your program. However, you cannot rely on the values being inverted, because, as mentioned above, anything may happen when invoking undefined behavior.
If you want the behavior of your program to be reliably predictable, then you should not invoke undefined behavior, by following the rule mentioned above, for example like this:
printf(
"Vertex coords = %d, %d, %d \n\n",
triangleTest.vertex[loop].x
triangleTest.vertex[loop].y
triangleTest.vertex[loop].z
);
Although these arguments are of type unsigned short
instead of int
, this is acceptable, because when passing an unsigned short
to printf
, it will get implicitly promoted to int
.
However, I would recommend using %hu
instead of %d
for the unsigned short
data type, because that way, you can use the same format specifier for both the printf
and scanf
family of functions and you don't have to take int
promotions into account.
See the documentation for printf
and scanf
for further information on the format specifiers.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论