我正在尝试实现一个将两个动态矩阵(AxB)相乘的函数,并返回一个指向动态分配的产品(C)的指针。该函数的参数是:
由于产品的行数与A的行数和B的列数相同,因此使用a_rows和b_cols来确定要分配的产品的大小。
功能如下所示:
double** MatrixMultiply(
double** a,
const uint32_t a_rows,
const uint32_t a_cols,
double** b,
const uint32_t b_cols) {
double** c;
c = (double**) calloc(a_rows, sizeof(double**)); //product matrix C has
//matrix A rows and Matrix B cols
for(int32_t i = 0; i < a_rows; i++) {
c[i] = (double*) calloc(b_cols, sizeof(double*));
}
for(uint32_t i = 0; i < a_rows; i++) {
for(uint32_t j = 0; j < b_cols; j++) {
for(uint32_t k = 0; k < a_cols; k++) {
c[i][j] += a[i][k] * b[k][j];
}
}
}
return c;
}
问题在于,每当我运行程序时,它都会被函数崩溃,因为我认为是某种分段错误。但是,每当我运行调试器并逐步执行每一行时,它都不会显示已发生任何错误。
对我来说,奇怪的是当我改变&#34; a_rows&#34;和&#34; b_cols&#34;对于常数(例如,20),该函数正确运行。什么可能导致这个问题?它怎么能修复?任何帮助将不胜感激。
答案 0 :(得分:4)
您正在分配错误的内存量。
c[i] = (double*) calloc(b_cols, sizeof(double*));
这为b_cols
个双指针分配内存,而不是b_cols
个双精度。
这是使用typedef(<type>)
时的典型错误,一个人犯了错误
很简单,因为您在不需要的地方添加*
,或者在需要时忘记添加*
。
最好使用sizeof *var
而不是sizeof(<type>)
:
int *arr = malloc(size * sizeof *arr);
sizeof *arr
无论类型如何都返回确切的字节数。它
也有好处,如果你以后碰巧改变了类型
变量,你不必担心改变里面的表达式
sizeof
。
正确的方法是:
c = calloc(a_rows, sizeof *c);
if(c == NULL)
{
// error handling
}
for(int32_t i = 0; i < a_rows; i++) {
c[i] = calloc(b_cols, sizeof *c[i]);
if(c[i] == NULL)
{
// error handling
}
}
同样don't cast malloc并始终检查结果是否为NULL
。如果
您没有检查结果是NULL
,那么您将尝试访问NULL
指针是未定义的行为,将导致段错误。也没有
忘记之后释放记忆。
答案 1 :(得分:3)
你的代码错了。 calloc
(和malloc
)可能会失败,你总是应该处理它。你不需要强制转换它,第二个参数是每个单元格的大小(不是指向它的指针)。
你应该有的最小值
double** c= calloc(a_rows, sizeof(double*));
if (c==NULL) { perror("calloc c"); exit(EXIT_FAILURE); }
同样
c[i] = calloc(b_cols, sizeof(double));
if (c[i]==NULL) { perror("calloc c row"); exit(EXIT_FAILURE); };
请注意,对于每个calloc
,第二个参数是一个单元格的大小(不是指向它的指针)。第一个参数是单元格的数量(您确定要b_cols
,而不是a_cols
吗?只有您可以知道。
对于c
作为一个整体的分配,在 practice 中,所有指针的大小都相同,因此sizeof(double**)
通常是相同的值(我的Linux上的8个字节/ x86-64)而不是sizeof(double*)
。
您应该编译所有警告和调试信息:gcc -Wall -Wextra -g
。一些编译器(GCC的最新版本)可能已经警告过你。您可以使用valgrind来运行(带有检查)您的程序(可能已检测到缓冲区溢出)。当然你也应该use the gdb
debugger。
但是,每当我运行调试器并逐步执行每一行时,它都不会显示已发生任何错误。
在Linux(或MacOSX)上,您可以设置系统以便进行核心转储(确保核心文件有足够大的限制,可能内置ulimit
bash
}),并用gdb
分析核心转储的后验;见core(5),gdb(1),signal(7)。并且gdb
能够运行您的程序直到它崩溃。