Почему после добавления вывода строки ошибка исчезла?
Моя программа создает матрицу (размер из аргументов командной строки), заполняет ее и печатает:
#include <stdio.h>
#include <stdlib.h>
int **matrix_maker(int lines, int colons)
/*Функция создает матрицу. Внутри функции проблема*/
{
int **tmp = NULL;
/*Без следующей ЧУДОСТРОКИ программа отказывается работать*/
printf("%d\n", 1);
tmp = (int**)malloc(lines*sizeof(int*) + lines*colons*sizeof(int));
for(int i = 0; i < lines; ++i){
*(tmp+i) = (int*)(tmp+colons*(i+1));
}
return tmp;
}
int str_to_unsigned_int(const char *str)
/*Функция превращает строку в число (для аргументов командной строки)*/
{
int num = 0;
for(; *str; ++str){
if( (*str)<'0' || (*str)>'9' ){
fprintf(stderr, "Not a number\n");
return -1;
}
num = num*10 + (*str)-'0';
}
return num;
}
void matrix_filler(int **matrix, int lines, int colons)
/*Функция заполняет матрицу (Чем именно - не важно, программа учебная)*/
{
for(int i = 0; i < lines; i++)
for(int j = 0; j < colons; j++){
matrix[i][j] = (lines*i+colons*j+i*j)/lines+colons;
}
}
void matrix_printer(int **matrix, int lines, int colons)
/*Функция печатает матрицу*/
{
for(int i = 0; i < lines; i++){
for(int j = 0; j < colons; j++){
printf("%d ", matrix[i][j]);
}
printf("\n");
}
}
int main(int argc, const char **argv)
{
if(argc < 3){
fprintf(stderr, "To few arguments\n");
return 1;
}
int lines;
int colons;
lines = str_to_unsigned_int(*(argv+1));
colons = str_to_unsigned_int(*(argv+2));
if(lines == -1 || colons == -1){
return 2;
}
int **matrix = NULL;
matrix = matrix_maker(lines, colons);
matrix_filler(matrix, lines, colons);
matrix_printer(matrix, lines, colons);
return 0;
}
В этой программе есть функция matrix_maker, которая собственно создает матрицу int-ов размером lines на colons. Чтобы не вызывать функцию malloc много раз (lines+1, если быть точным), я резервирую память сразу под матрицу нужного размера один раз, а потом просто заполняю первые lines ячеек указателями на строки матрицы.
А тепер вопрос:
Почему без ЧУДОСТРОКИ вывод моей функции (в терминале при запуске программы):
malloc(): corrupted top size
Aborted (core dumped)
Но если я просто вставлю мою отладочною ЧУДОСТРОКУ, то функция выводит в терминале:
1 /*Это та единица, которую печатает ЧУДОСТРОКА*/
12 13 14 15 16 17 18 19 20 21 22 23
13 14 15 16 17 18 19 20 21 22 23 24
14 15 16 17 18 19 21 22 23 24 25 26
15 16 17 18 20 21 22 23 25 26 27 28
16 17 18 20 21 22 24 25 26 28 29 30
17 18 19 21 22 24 25 26 28 29 31 32
18 19 21 22 24 25 27 28 30 31 33 34
19 20 22 23 25 26 28 30 31 33 34 36
20 21 23 25 26 28 30 31 33 35 36 38
21 22 24 26 28 29 31 33 35 36 38 40
22 23 25 27 29 31 33 34 36 38 40 42
23 24 26 28 30 32 34 36 38 40 42 44
Как так, и чего я не понимаю?
Ответы (2 шт):
Смотрим сюда:
tmp = (int**)malloc(lines*sizeof(int*) + lines*colons*sizeof(int));
Т.е. выделено вначале место под lines указателей, а потом
for(int i = 0; i < lines; ++i){
*(tmp+i) = (int*)(tmp+colons*(i+1));
}
вы их расставляете... Но давайте посмотрим, куда смотрит первый же указатель:
*tmp = (int*)(tmp + colons);
А должен? Как минимум на место после lines указателей, т.е.
*tmp = (int*)(tmp + lines);
Вы просто неверно написали работу с памятью...
Посмотрите вот этот ответ, часть, начинающуюся со слов
Еще один вариант - выделение одним большим куском, чтобы и освобождать один раз. При этом вначале хранятся указатели на строки, а затем - данные.
Видите отличие моего кода от вашего?...
Вот так будет правильнее:
int ** tmp = malloc(lines*(sizeof(int*)+colons*sizeof(int)));
for(int i = 0, ofs = lines*sizeof(int*); i < lines; i++)
tmp[i] = (int*)((char*)tmp + ofs + i*colons*sizeof(int));
Задумка понятна - выделить один буфер для lines указателей, за которым идут lines x colons значений, затем дать указателям указывать на соотв. строку.
Смещение нулевого указателя в буфере должно быть sizeof(int *) * lines байт, первого sizeof(int *) * lines + sizeof(int) * colons, второго - sizeof(int *) * lines + sizeof(int) * colons * 2.
Рассмотрим, что происходит на строке *(tmp+i) = (int*)(tmp+colons*(i+1));. Смещение нулевого указателя будет sizeof(int *) * colons * 1 байт, первого - sizeof(int *) * colons * 2 байт, второго - sizeof(int *) * colons * 3 байт. Как видно, значения указателей начинают разезжаться с самого начала, а потом и вовсе выходят за пределы буфера (подразумевая 64-битный указатель), что при обращении к ним выливается в чтение за пределами буфера, что является Неопределенным Поведением.