Click here to Skip to main content
15,887,027 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I've been trying to debug a program I wrote that fills out vertices for an icosahedron (based off of a tutorial I found online). When running the program I get segmentation faults, specifically in functions where I have to access array indexes, and I don't know how to solve it, so I created a MRE (minimal reproducible example) of the program, with the most important parts to show where the errors are, and the usage of the problematic functions.

C
#include <stdlib.h>
#include <math.h>

void icosahedron(int radius, float *vertices);
void subdivide(float *vertices, unsigned int *indices, int subdivision_amount, size_t vertices_arr, size_t indices_arr, int radius);
void compute_half_vertex(float v1[3], float v2[3], float new_v[3], int radius);
void add_vertices(float v1[3], float v2[3], float v3[3], float *vertices);
void add_indices(unsigned int i1, unsigned int i2, unsigned int i3, unsigned int *indices);

int main(void) 
{
    int radius = 1;
    float vertices[60];
    unsigned int indices[60];

    icosahedron(radius, vertices);
    subdivide(vertices, indices, 1, sizeof(vertices), sizeof(indices), radius);

    return 0;
}

void icosahedron(int radius, float *vertices)
{
    float H_ANGLE = M_PI / 180 * 72;
    float V_ANGLE = atanf(1.0f / 2);

    int i1, i2;
    float z, xy;    
    float h_angle_1 = -M_PI / 2 - H_ANGLE / 2;
    float h_angle_2 = -M_PI / 2;

    vertices[0] = 0;
    vertices[1] = 0;
    vertices[2] = radius;
    
    for (int i = 1; i <= 5; ++i)
    {
        i1 = i * 3;
        i2 = (i + 5) * 3;

        z = radius * sinf(V_ANGLE);
        xy = radius * cosf(V_ANGLE);

        vertices[i1] = xy * cosf(h_angle_1);
        vertices[i2] = xy * cosf(h_angle_2);
        vertices[i1 + 1] = xy * sinf(h_angle_1);
        vertices[i2 + 1] = xy * sinf(h_angle_2);
        vertices[i1 + 2] = z;
        vertices[i2 + 2] = -z;

        h_angle_1 += H_ANGLE;
        h_angle_2 += H_ANGLE;
    }

    i1 = 11 * 3;
    vertices[i1] = 0;
    vertices[i1 + 1] = 0;
    vertices[i1 + 2] = -radius;
}

void subdivide(float* vertices, unsigned int* indices, int subdivision_amount, size_t vertices_arr, size_t indices_arr, int radius)
{
    float* tmp_vertices;
    unsigned int* tmp_indices;
    float new_v1[3], new_v2[3], new_v3[3];
    float *v1, *v2, *v3;
    unsigned int index;

    for (int i = 1; i <= subdivision_amount; ++i)
    {
        tmp_vertices = vertices;
        tmp_indices = indices;
        vertices = NULL;
        indices = NULL;
        index = 0;

        for (int j = 0; j < sizeof(tmp_indices); j += 3)
        {
            v1 = &tmp_vertices[(int)tmp_indices[j] * 3];
            v2 = &tmp_vertices[(int)tmp_indices[j + 1] * 3];
            v3 = &tmp_vertices[(int)tmp_indices[j + 2] * 3];

            compute_half_vertex(v1, v2, new_v1, radius);
            compute_half_vertex(v2, v3, new_v2, radius);
            compute_half_vertex(v1, v3, new_v3, radius);

            add_vertices(v1, new_v1, new_v3, vertices);
            add_vertices(new_v1, v2, new_v2, vertices);
            add_vertices(new_v1, new_v2, new_v3, vertices);
            add_vertices(new_v3, new_v2, v3, vertices);

            add_indices(index, index + 1, index + 2, indices);
            add_indices(index + 3, index + 4, index + 5, indices);
            add_indices(index + 6, index + 7, index + 8, indices);
            add_indices(index + 9, index + 10, index + 11, indices);
            index += 12;
        }
    }
}

void compute_half_vertex(float v1[3], float v2[3], float new_v[3], int radius)
{
    new_v[0] = v1[0] + v2[0];
    new_v[1] = v1[1] + v2[1];
    new_v[2] = v1[2] + v2[2];
    float scale = radius / sqrtf((new_v[0] * new_v[0]) + (new_v[1] * new_v[1]) + (new_v[2] * new_v[2]));
    new_v[0] *= scale;
    new_v[1] *= scale;
    new_v[2] *= scale;
}

// first problematic function
void add_vertices(float v1[3], float v2[3], float v3[3], float *vertices)
{
    vertices[0] = v1[0];
    vertices[1] = v1[1];
    vertices[2] = v1[2];
    vertices[3] = v2[0];
    vertices[4] = v2[1];
    vertices[5] = v2[2];
    vertices[6] = v3[0];
    vertices[7] = v3[1];
    vertices[8] = v3[2];
}

// second problematic function
void add_indices(unsigned int i1, unsigned int i2, unsigned int i3, unsigned int *indices)
{
    indices[0] = i1;
    indices[1] = i2;
    indices[2] = i3;
}


EDIT: I forgot to post the error message
here it is
through the use of breakpoints that's the same error I get all throughout the
add_vertices
and
add_indices
functions.

EDIT 2: Thanks to some debugging, I narrowed down the problem to the subdivision loop in the subdivide function. It's not the other functions.

What I have tried:

Running GDB on the code to get more information on the errors, just got the location of the seg faults instead.
Posted
Updated 24-Jul-23 6:49am
v4
Comments
Graeme_Grant 23-Jul-23 2:50am    
Post the full error message. There is usual more information than just "Index out of bounds". Use the green "Improve question" link to add the missing information.
[no name] 23-Jul-23 5:02am    
A quick glance at the code I can see:

vertices = NULL;

Then proceeds to append 8 floats to the null pointer. The variable "indices" has a similar issue.

I'm on my TV right now, could be other issues.
Graeme_Grant 23-Jul-23 5:04am    
Do not reply to me, the OP won't see it

"Index out of bounds" means that: you are trying to access an element of an array with an index that is negative, or larger than the number of elements in the array, minus 1 (Because in C, valid indexes for an array on N elements start from 0 and go up to but not including N).

Compiling does not mean your code is right! :laugh:
Think of the development process as writing an email: compiling successfully means that you wrote the email in the right language - English, rather than German for example - not that the email contained the message you wanted to send.

So now you enter the second stage of development (in reality it's the fourth or fifth, but you'll come to the earlier stages later): Testing and Debugging.

Start by looking at what it does do, and how that differs from what you wanted. This is important, because it give you information as to why it's doing it. For example, if a program is intended to let the user enter a number and it doubles it and prints the answer, then if the input / output was like this:
Input   Expected output    Actual output
  1            2                 1
  2            4                 4
  3            6                 9
  4            8                16
Then it's fairly obvious that the problem is with the bit which doubles it - it's not adding itself to itself, or multiplying it by 2, it's multiplying it by itself and returning the square of the input.
So with that, you can look at the code and it's obvious that it's somewhere here:
C++
private int Double(int value)
   {
   return value * value;
   }

Once you have an idea what might be going wrong, start using the debugger to find out why. Put a breakpoint on the first line of the method, and run your app. When it reaches the breakpoint, the debugger will stop, and hand control over to you. You can now run your code line-by-line (called "single stepping") and look at (or even change) variable contents as necessary (heck, you can even change the code and try again if you need to).
Think about what each line in the code should do before you execute it, and compare that to what it actually did when you use the "Step over" button to execute each line in turn. Did it do what you expect? If so, move on to the next line.
If not, why not? How does it differ?
Hopefully, that should help you locate which part of that code has a problem, and what the problem is.

So start with the actual error message as it tell you where the error occurred - file and line number at least. Run your app in the debugger and put a breakpoint on that line. Then when it hits the breakpoint, look at the variables involved and work out which is out of bounds. Then you can start looking back through your code (using more runs through the debugger with breakpoints in different places as needed) to work out why it held that value.

This is a skill, and it's one which is well worth developing as it helps you in the real world as well as in development. And like all skills, it only improves by use!
Sorry, but we can't do that for you: time to learn a new (and very useful) skill: debugging!
 
Share this answer
 
I think the problem is in how you call the function :
C++
float vertices[60];
unsigned int indices[60];

icosahedron(radius, vertices);
subdivide(vertices, indices, 1, sizeof(vertices), sizeof(indices), radius);
The sizeof operator returns the number of bytes in an item, not the number of array elements so the value passed to subdivide is 240, not 60. There is a _countof macro available in many (most?) compilers that will do that. If it is not available in yours, here is how it is implemented :
C++
#ifndef _countof
#define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))
#endif
Another option is to use a constant value like this :
C++
const int VertexCount=60;
float vertices[ VertexCount ];
unsigned int indices[ VertexCount ];

subdivide( vertices, indices, 1, VertexCount, VertexCount, radius );
I always tell the compiler to use warning level 4 with my code and it would tell me that in the function :
C++
void subdivide(float* vertices, unsigned int* indices, int subdivision_amount, size_t vertices_arr, size_t indices_arr, int radius)
the arguments vertices_arr and indices_arr are not used. On second thought, that means this is not the problem. Not directly, that is.

As has been mentioned, I think your best is to use a debugger or, at least, to do some printf-style debugging and display every index used in the code.

ETA: I was curious so I had a go at this and I found a few problems. One is the indices array was not initialized so it had invalid values. Another is null pointers were passed to add_indices. I adjusted the code to this and it runs but I'm not sure it works correctly :
void subdivide(       float* vertices
                    , unsigned int* indices
                    , int subdivision_amount
                    , int radius
                    , int vertCount
                    )
{
    float new_v1[3], new_v2[3], new_v3[3];
    float *v1, *v2, *v3;
    unsigned int index;

    for( int i = 1; i <= subdivision_amount; ++i )
    {
        index = 0;

        for( int j = 0; j < vertCount; j += 3 )
        {
            v1 = & vertices[ indices[ j ] * 3 ];
            v2 = & vertices[ indices[ j + 1 ] * 3 ];
            v3 = & vertices[ indices[ j + 2 ] * 3 ];

            compute_half_vertex( v1, v2, new_v1, radius );
            compute_half_vertex( v2, v3, new_v2, radius );
            compute_half_vertex( v1, v3, new_v3, radius );

            add_vertices( v1,     new_v1, new_v3, vertices );
            add_vertices( new_v1, v2,     new_v2, vertices );
            add_vertices( new_v1, new_v2, new_v3, vertices );
            add_vertices( new_v3, new_v2, v3,     vertices );

            add_indices( index,     index + 1,  index + 2,  indices );
            add_indices( index + 3, index + 4,  index + 5,  indices );
            add_indices( index + 6, index + 7,  index + 8,  indices );
            add_indices( index + 9, index + 10, index + 11, indices );
            index += 12;
        }
    }
}


void TestSubdivide() 
{
    const int VertCount = 60;
    int radius = 1;
    float vertices[ VertCount ] = { 0 };
    unsigned int indices[ VertCount ] = { 0 };

    icosahedron( radius, vertices );
    subdivide( vertices, indices, 1, radius, VertCount );
}
The indices array never gets set to anything other than zeros but the vertices array has some non-zero values in it. I am not sure if this is what you want or not. Also, I don't see what effect the subdivision_amount parameter will have.
 
Share this answer
 
v6
Comments
CPallini 24-Jul-23 2:04am    
5.
Accessing an array with some invalid index is a severe bug not only because the runtime crashed, but you ask for data which doesnt exist.

So handle these cases. Most common handling is to check whether you index is equal or higher zero AND isnt higher than elements in the array. One way is to use constants for size, or you some class to have a count or track count yourself.

tip: if this check fails you may have found another bug. Track him down to find the cause.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900