0
typedef struct Tuple
{
    int row;
    int col;
    int data;
} Tuple;

int** createMatrix(int r, int c)
{
    int** mat = (int**)malloc(sizeof(int*) * r);
    for (int i = 0; i < r; i++)
        mat[i] = malloc(sizeof(int) * c);
    return mat;
}

Tuple* createTuple(int** mat, int r, int c)
{
    int count = 0;
    for (int i = 0; i < r; i++)
        for (int j = 0; j < c; j++)
            if (mat[i][j])
                count++;

    Tuple* tuple = (Tuple*)malloc(sizeof(Tuple) * count);
    tuple[0].col = c;
    tuple[0].row = r;
    tuple[0].data = count;
    int k = 1;
    for (int i = 0; i < r; i++)
        for (int j = 0; j < c; j++)
            if (mat[i][j])
            {
                tuple[k].col = j;
                tuple[k].row = i;
                tuple[k++].data = mat[i][j];
            }
    return tuple;
}

Tuple* fastTranspose(Tuple* tuple)
{
    Tuple* trans = (Tuple*)calloc((tuple[0].data + 1),sizeof(Tuple) );
    trans[0].col = tuple[0].row;
    trans[0].row = tuple[0].col;
    trans[0].data = tuple[0].data;
    int* rowTerms = (int*)malloc(sizeof(int) * tuple[0].col);
    int* startingPos = (int*)malloc(sizeof(int) * tuple[0].col);
    for (int i = 0; i < tuple[0].col; i++)
        rowTerms[i] = 0;
    for (int i = 1; i <= tuple[0].data; i++)
        rowTerms[tuple[i].col]++;
    startingPos[0] = 1;
    for (int i = 1; i < tuple[0].col; i++)
        startingPos[i] = startingPos[i - 1] + rowTerms[i - 1];

    for (int i = 1; i <= tuple[0].data; i++)
    {
        int j = startingPos[tuple[i].col]++;
        trans[j].col = tuple[i].row;
        trans[j].row = tuple[i].col;
        trans[j].data = tuple[i].data;
    }
    free(rowTerms);
    free(startingPos);
    return trans;
}

void printTuple(Tuple* tuple)
{
    printf("\nRow Col Val:\n");
    for (int i = 0; i <= tuple[0].data; i++)
    {
        printf("%3d%4d%4d\n", tuple[i].row, tuple[i].col, tuple[i].data);
    }
}

int main()
{
    int** mat = createMatrix(3, 4);
    printf("Enter data:\n");
    for (int i = 0; i < 3; i++)
        for (int j = 0; j < 4; j++)
            scanf("%d", &mat[i][j]);
    Tuple* tuple = createTuple(mat, 3, 4);
    printf("Original tuple:");
    printTuple(tuple);
    Tuple* trans = fastTranspose(tuple);
    printf("Tuple transpose:");
    printTuple(trans);
    free(trans);
    free(tuple);
    for (int i = 0; i < 3; i++)
        free(mat[i]);
    free(mat);
}

Why is the breakpoint triggered at the first line of the function fastTranspose even though I haven't put one there?

I looked at similar problems but was not able to find a solution. It is a program for finding the transpose of a matrix. We first dynamically allocate a two-dimensional matrix. Then it is converted to sparse matrix representation form which uses a struct to store values of the row, column and data. I tried printing the values of the matrix and the tuple. It worked. But when entering the fast transpose function a breakpoint is suddenly triggered.

We usually put a breakpoint in the visual studio by ourselves. Why is one put by the IDE?

Tech Trivia
  • 65
  • 1
  • 6

1 Answers1

0

Visual Studio put a breakpoint there to be helpful because you got an error at that place. If I do it on a somewhat recent Linux (Mint 20) with a somewhat recent clang (12.0.0) I got the following output:

$ echo -e '1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12' | ./so_tmat 
Enter data:
Original tuple:
Row Col Val:
  3   4  12
  0   0   1
  0   1   2
  0   2   3
  0   3   4
  1   0   5
  1   1   6
  1   2   7
  1   3   8
  2   0   9
  2   1  10
  2   2  11
  2   3  12
tuple[0].data + 1 = 13, sizet(Tuple) = 12
so_tmat: malloc.c:2379: sysmalloc: Assertion `(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed.
Aborted (core dumped)

GDB's (GNU debugger) backtrace points to the same line as VS did.

And if I (your mileage may vary because: Google) copy and paste it as it is into Google I got Why do I get a C malloc assertion failure? as the first hit. The accepted answer points in the right way and we shall ask Valgrind for some hints.

$ echo -e '1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12' |  valgrind  --track-origins=yes --leak-check=full --show-leak-kinds=all  ./so_tmat 
==16047== Memcheck, a memory error detector
==16047== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==16047== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==16047== Command: ./so_tmat
==16047== 
Enter data:
==16047== Invalid write of size 8
==16047==    at 0x40144D: createTuple (so_tmat.c:38)
==16047==    by 0x40144D: main (so_tmat.c:89)
==16047==  Address 0x4a796a0 is 0 bytes after a block of size 144 alloc'd
==16047==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==16047==    by 0x401377: createTuple (so_tmat.c:28)
==16047==    by 0x401377: main (so_tmat.c:89)
==16047== 
==16047== Invalid write of size 4
==16047==    at 0x401452: createTuple (so_tmat.c:39)
==16047==    by 0x401452: main (so_tmat.c:89)
==16047==  Address 0x4a796a8 is 8 bytes after a block of size 144 alloc'd
==16047==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==16047==    by 0x401377: createTuple (so_tmat.c:28)
==16047==    by 0x401377: main (so_tmat.c:89)
==16047== 

    ...

We can stop here because we found the reason: you have a buffer overflow because you start with count = 0 in createMatrix(int r, int c) which results in the lack of the memory necessary for the last Tuple. Change it to count = 1 and it should work.

Let's check:

$ echo -e '1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12' |  valgrind  --track-origins=yes --leak-check=full --show-leak-kinds=all  ./so_tmat 
==16215== Memcheck, a memory error detector
==16215== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==16215== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==16215== Command: ./so_tmat
==16215== 
Enter data:
Original tuple:
Row Col Val:
  3   4  13
  0   0   1
  0   1   2
  0   2   3
  0   3   4
  1   0   5
  1   1   6
  1   2   7
  1   3   8
  2   0   9
  2   1  10
  2   2  11
  2   3  12
==16215== Invalid read of size 4
==16215==    at 0x4014A0: printTuple (so_tmat.c:78)
==16215==    by 0x4014A0: main (so_tmat.c:91)
==16215==  Address 0x4a796b4 is 8 bytes after a block of size 156 alloc'd
==16215==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==16215==    by 0x40137A: createTuple (so_tmat.c:28)
==16215==    by 0x40137A: main (so_tmat.c:89)

    ...

There is still a problem and it is in printTuple(Tuple* tuple) now, because we changed the initialization of count but did not adapt the value in tuple[0].data so the upper limit in the line for (int i = 0; i <= tuple[0].data; i++) is too large. Either correct it here or there, does not matter much, I would correct the upper limit in the loop because tuple[0].data holds the correct number of non-zero entries. So change the loop to for (int i = 0; i < tuple[0].data; i++)

Check again:

$ echo -e '1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12' |  valgrind  --track-origins=yes --leak-check=full --show-leak-kinds=all  ./so_tmat 
==16328== Memcheck, a memory error detector
==16328== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==16328== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==16328== Command: ./so_tmat
==16328== 
Enter data:
Original tuple:
Row Col Val:
  3   4  13
  0   0   1
  0   1   2
  0   2   3
  0   3   4
  1   0   5
  1   1   6
  1   2   7
  1   3   8
  2   0   9
  2   1  10
  2   2  11
  2   3  12
tuple[0].data + 1 = 14, sizet(Tuple) = 12
==16328== Invalid read of size 4
==16328==    at 0x4015B1: fastTranspose (so_tmat.c:56)
==16328==    by 0x4015B1: main (so_tmat.c:92)
==16328==  Address 0x4a796b0 is 4 bytes after a block of size 156 alloc'd
==16328==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==16328==    by 0x40137A: createTuple (so_tmat.c:28)
==16328==    by 0x40137A: main (so_tmat.c:89)
==16328== 
==16328== Invalid read of size 4
==16328==    at 0x4017D0: fastTranspose (so_tmat.c:63)
==16328==    by 0x4017D0: main (so_tmat.c:92)
==16328==  Address 0x4a796b0 is 4 bytes after a block of size 156 alloc'd
==16328==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==16328==    by 0x40137A: createTuple (so_tmat.c:28)
==16328==    by 0x40137A: main (so_tmat.c:89)
==16328== 
==16328== Invalid read of size 4
==16328==    at 0x4017DF: fastTranspose (so_tmat.c:64)
==16328==    by 0x4017DF: main (so_tmat.c:92)
==16328==  Address 0x4a796ac is 0 bytes after a block of size 156 alloc'd
==16328==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==16328==    by 0x40137A: createTuple (so_tmat.c:28)
==16328==    by 0x40137A: main (so_tmat.c:89)
==16328== 
==16328== Invalid read of size 4
==16328==    at 0x4017ED: fastTranspose (so_tmat.c:66)
==16328==    by 0x4017ED: main (so_tmat.c:92)
==16328==  Address 0x4a796b4 is 8 bytes after a block of size 156 alloc'd
==16328==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==16328==    by 0x40137A: createTuple (so_tmat.c:28)
==16328==    by 0x40137A: main (so_tmat.c:89)

   ...

Looking at the lines in question (your line numbers may be different): it is the same upper limit problem as before. Change it and check again.

$ echo -e '1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12' |  valgrind  --track-origins=yes --leak-check=full --show-leak-kinds=all  ./so_tmat 
==16420== Memcheck, a memory error detector
==16420== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==16420== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==16420== Command: ./so_tmat
==16420== 
Enter data:
Original tuple:
Row Col Val:
  3   4  13
  0   0   1
  0   1   2
  0   2   3
  0   3   4
  1   0   5
  1   1   6
  1   2   7
  1   3   8
  2   0   9
  2   1  10
  2   2  11
  2   3  12
tuple[0].data + 1 = 14, sizet(Tuple) = 12
Tuple transpose:
Row Col Val:
  4   3  13
  0   0   1
  0   1   5
  0   2   9
  1   0   2
  1   1   6
  1   2  10
  2   0   3
  2   1   7
  2   2  11
  3   0   4
  3   1   8
  3   2  12
==16420== 
==16420== HEAP SUMMARY:
==16420==     in use at exit: 0 bytes in 0 blocks
==16420==   total heap usage: 10 allocs, 10 frees, 5,548 bytes allocated
==16420== 
==16420== All heap blocks were freed -- no leaks are possible
==16420== 
==16420== For lists of detected and suppressed errors, rerun with: -s
==16420== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Nice, no problems anymore. For that exact input at least. But I squeezed a little printf("tuple[0].data + 1 = %d, sizet(Tuple) = %zu\n",tuple[0].data + 1, sizeof(Tuple) ); in before the calloc() and it says

tuple[0].data + 1 = 14, sizet(Tuple) = 12

That is one Tuple too much; save some memory and change it by removing the + 1 in calloc. That's it.

I used Unix tools here, most of them are available for Windows, but VS has all necessary tools, too, they just have different names and get used differently. Try to find them and check your original code (of which you know the errors now) with them to get familiar with that toolchain. You really need that knowledge!

deamentiaemundi
  • 5,502
  • 2
  • 12
  • 20