C语言 在for循环中获得分段错误

mpbci0fu  于 2023-10-16  发布在  其他
关注(0)|答案(2)|浏览(122)

我读了一篇关于为我的一个项目创建球体的文章,可以找到here,并决定尝试将其移植到c语言,我正在使用的语言。我选择了二十面体方法,你从一个二十面体开始,然后从那里细分。问题是,我得到了seg故障,但它发生时,我试图细分它。但是如果我保持原样,不尝试细分,代码编译良好,没有错误。

  1. #include <stdio.h>
  2. #include <stdbool.h>
  3. #include <math.h>
  4. #include <stdint.h>
  5. struct Point
  6. {
  7. double x;
  8. double y;
  9. double z;
  10. };
  11. struct Triangle
  12. {
  13. int v1;
  14. int v2;
  15. int v3;
  16. };
  17. // normalizes a vertex and then adds it to an array
  18. int add_vertex(struct Point verts[], struct Point point)
  19. {
  20. double length = (point.x * point.x) + (point.y * point.y) + (point.z * point.z);
  21. int index = 0;
  22. verts[index].x = point.x / length;
  23. verts[index].y = point.y / length;
  24. verts[index].z = point.z / length;
  25. return index++;
  26. }
  27. // gets the middle of a triangle face but checks to make sure that no duplicate vertices are created
  28. int get_middle_point(int p1, int p2, struct Triangle inds[], struct Point verts[], int cache[])
  29. {
  30. // check to see if the middle point is already calculated
  31. uint64_t key = (p1 < p2) ? (p1, p2) : (p2, p1);
  32. if (cache[key] != 0)
  33. {
  34. return cache[key];
  35. }
  36. // it isn't so calculate it
  37. struct Point point1 = verts[p1];
  38. struct Point point2 = verts[p2];
  39. struct Point middle;
  40. middle.x = (point1.x + point2.x) / 2.0;
  41. middle.y = (point1.y + point2.y) / 2.0;
  42. middle.z = (point1.z + point2.z) / 2.0;
  43. // add middle vertex to unit sphere
  44. int i = add_vertex(verts, middle);
  45. return i;
  46. }
  47. // creates icosphere by subdividing icosahedron
  48. void create_icosphere(int recursion_level, struct Point verts[], struct Triangle inds[])
  49. {
  50. // cache middle points
  51. int middle_point_cache[1000];
  52. // golden ratio
  53. float t = 1 + sqrt(5) * 0.5;
  54. // icosahedron cartisean coordinates from wikipedia
  55. add_vertex(verts, (struct Point){-1, t, 0});
  56. add_vertex(verts, (struct Point){1, t, 0});
  57. add_vertex(verts, (struct Point){-1, -t, 0});
  58. add_vertex(verts, (struct Point){1, -t, 0});
  59. add_vertex(verts, (struct Point){0, -1, t});
  60. add_vertex(verts, (struct Point){0, 1, t});
  61. add_vertex(verts, (struct Point){0, -1, -t});
  62. add_vertex(verts, (struct Point){0, 1, -t});
  63. add_vertex(verts, (struct Point){t, 0, -1});
  64. add_vertex(verts, (struct Point){t, 0, 1});
  65. add_vertex(verts, (struct Point){-t, 0, -1});
  66. add_vertex(verts, (struct Point){-t, 0, 1});
  67. // icosahedron indices
  68. // 5 faces around point 0
  69. inds[0] = (struct Triangle){0, 11, 5};
  70. inds[1] = (struct Triangle){0, 5, 1};
  71. inds[2] = (struct Triangle){0, 1, 7};
  72. inds[3] = (struct Triangle){0, 7, 10};
  73. inds[4] = (struct Triangle){0, 10, 11};
  74. // 5 adjacent faces
  75. inds[5] = (struct Triangle){1, 5, 9};
  76. inds[6] = (struct Triangle){5, 11, 4};
  77. inds[7] = (struct Triangle){11, 10, 2};
  78. inds[8] = (struct Triangle){10, 7, 6};
  79. inds[9] = (struct Triangle){7, 1, 8};
  80. // 5 faces around point 3
  81. inds[10] = (struct Triangle){3, 9, 4};
  82. inds[11] = (struct Triangle){3, 4, 2};
  83. inds[12] = (struct Triangle){3, 2, 6};
  84. inds[13] = (struct Triangle){3, 6, 8};
  85. inds[14] = (struct Triangle){3, 8, 9};
  86. // 5 adjacent faces
  87. inds[15] = (struct Triangle){4, 9, 5};
  88. inds[16] = (struct Triangle){2, 4, 11};
  89. inds[17] = (struct Triangle){6, 2, 10};
  90. inds[18] = (struct Triangle){8, 6, 7};
  91. inds[19] = (struct Triangle){9, 8, 1};
  92. // subdivide icosahedron
  93. for (int i = 0; i < recursion_level; i++)
  94. {
  95. // create new array for newly created triangle faces
  96. struct Triangle faces_2[1000];
  97. int faces_2_index = 0;
  98. for (int j = 0; j < 20; j++)
  99. {
  100. struct Triangle tri = inds[j];
  101. // get the centroid of each triangle face
  102. int a = get_middle_point(tri.v1, tri.v2, faces_2, verts, middle_point_cache);
  103. int b = get_middle_point(tri.v2, tri.v3, faces_2, verts, middle_point_cache);
  104. int c = get_middle_point(tri.v3, tri.v1, faces_2, verts, middle_point_cache);
  105. // add new faces to new faces array
  106. faces_2[faces_2_index++] = (struct Triangle){tri.v1, a, c};
  107. faces_2[faces_2_index++] = (struct Triangle){tri.v2, b, a};
  108. faces_2[faces_2_index++] = (struct Triangle){tri.v3, c, b};
  109. faces_2[faces_2_index++] = (struct Triangle){tri.v1, tri.v2, tri.v3};
  110. }
  111. // copy over contents from new array to indices
  112. for (int j = 0; j < faces_2_index; j++)
  113. {
  114. inds[j] = faces_2[j];
  115. }
  116. }
  117. }
  118. int main()
  119. {
  120. // example usage. works with a subdivision level of 0 but anything higher causes a seg fault
  121. struct Point vertices[1000];
  122. struct Triangle indices[1000];
  123. create_icosphere(2, vertices, indices);
  124. printf("No errors\n");
  125. return 0;
  126. }

我将问题缩小到细分循环或get_middle_point函数(在细分循环中调用)。但我不知道为什么会有错误,甚至如何解决它们。这是我的编译器目前针对细分循环内的seg错误error in nested subdivide loop所指向的位置。

mfuanj7w

mfuanj7w1#

在细分循环中,我创建了一个新数组

  1. struct Triangle faces_2[20 * recursion_level];

但随后写出出界,

  1. faces_2[faces_2_index++] = (struct Triangle){tri.v1, a, c};
  2. faces_2[faces_2_index++] = (struct Triangle){tri.v2, b, a};
  3. faces_2[faces_2_index++] = (struct Triangle){tri.v3, c, b};
  4. faces_2[faces_2_index++] = (struct Triangle){tri.v1, tri.v2, tri.v3};

所以为了解决我的问题,我只需要增加

  1. struct Triangle faces_2[1000];

而且很管用我保持它1000作为一个样本大小,但现在很快就会改变。

l7wslrjt

l7wslrjt2#

下面的代码不太可能实现您所认为的功能

  1. int add_vertex(struct Point verts[], struct Point point) {
  2. double length = (point.x * point.x) + (point.y * point.y) + (point.z * point.z);
  3. int index = 0;
  4. verts[index].x = point.x / length;
  5. verts[index].y = point.y / length;
  6. verts[index].z = point.z / length;
  7. return index++;
  8. }

这段代码接受一个数组(指向内存中某个位置的指针)和一个“point”。使用点信息计算长度(三维空间中的距离)。然后在0处创建一个“索引”,并将verts数组的第零个元素的字段设置为按长度标准化的值。到目前为止,一切顺利(假设这是你的意图)。
然后你增加'index',它不是无效的,但它是无用的。你在函数中声明了index,使它成为一个堆栈分配的变量。一旦函数返回,所有这些值就消失了。下一次调用该函数时,您将再次设置第零个元素。给定的函数将永远不会设置除元素[0]以外的任何内容。
你可以做几件事来纠正这一点,但它们都会影响调用代码的结构。一件事是将index作为函数参数传入。即在每次调用中指定索引值。如果你想增加索引,你需要将索引值作为指针(int *index)传递,而不是在函数内部初始化它。
简单地让函数返回一个Point结构可能会更干净。但是要做到这一点,你需要了解C中的内存是如何工作的。您可以参考此问题以获得帮助:Return a struct from a function in C
简而言之,C变量(通常)存在于“堆”内存中(由malloc/alloc/free等函数控制)或堆栈中,它们只存在于包含函数的上下文中。Seg-faults发生在您超出不属于您的内存时。通常在步进数组的末尾时(相对于零)。
通常,一个好的调试器可以帮助你理解错误发生在哪里,但不能理解为什么。“为什么”往往很简单,但可能会变得非常复杂。仍然知道在哪里可以帮助你追溯到为什么。

展开查看全部

相关问题