C语言 在动态内存分配后打印数组元素时出错

gorkyyrv  于 2023-04-29  发布在  其他
关注(0)|答案(5)|浏览(269)

我是新来的,正在尝试向量、数组和动态内存。
我尝试首先初始化一个数组,分配一些内存给它,然后给一个单元格一个值,并尝试在屏幕上打印它。它不会指出任何错误,程序只是在几秒钟后结束,没有输出。

int main(){
    int **m;
    m=(int **)malloc(10*10*sizeof(int));
    m[3][3]=3;
    printf("%d", m[3][3]);
}

这是在Dev-C++中,在C文件中,如果它是相关的。

qoefvg9y

qoefvg9y1#

你已经分配了100个int,这将给予你一个指向int的指针。你不能用这种方式来下标。你的C编译器应该已经给你这个错误了。
如果你想动态分配一个“二维”int数组,你需要显式地这样做,分配一个10个int指针的数组,然后为每个指针分配一个动态分配的10个int数组。

  • malloc结果没有错误检查。*
int **m = malloc(10 * sizeof(int *));
for (size_t i = 0; i < 10; i++) {
    m[i] = malloc(10 * sizeof(int));
}

但是在这种情况下没有理由不在堆栈上分配。由于它是在main中声明的,因此当将指向它的指针传递给其他函数时,将不会出现生存期问题,并且在堆栈上分配的数据量也不会太大。

int m[10][10];
kse8i1jr

kse8i1jr2#

根据[]下标运算符的定义,表达式

a[b]

相当于:

*(a+b)

这意味着表达式

m[3][3]

相当于:

*( (*(m+3)) + 3 )

由于您将m定义为指向int的指针,因此表达式m[3][3]将执行以下操作:它将把m视为指向int *类型数组的第一个元素的指针。它将尝试读取此数组的第四个元素。它将把第四个元素当作指向int类型数组的第一个元素的指针,并尝试读取该数组的第四个元素。
但是,m[3]有一个不确定的(“垃圾”)值,所以m[3]不会指向有效的数组。因此,通过解引用表达式m[3][3]中的这个无效指针,您的程序正在调用undefined behavior,这意味着任何事情都可能发生,包括您在问题中描述的行为。
因为你用了那句台词

m=(int **)malloc(10*10*sizeof(int));

我假设您的意图不是让m指向元素类型为int *的数组的第一个元素,而是让m指向int元素的2D数组。换句话说,m应该指向数组的第一个元素,其元素不是指针,而是数组。
为了实现这一点,必须以不同的方式声明变量m
那条线

int **m;

将声明变量m为指向int的指针。这不是你想要的您希望它指向2D数组的第一个子数组,因此您希望声明一个指向10个int元素的数组的指针。为此,必须使用以下语法:

int (*m)[10];

括号很重要,因为如果没有它们,你将声明一个int *类型的10个元素的数组,但你想声明一个指向10个int元素的数组的指针。
你的程序应该看起来像这样:

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
    int (*m)[10];

    m = malloc( 10*10*sizeof(int) );
    if ( m == NULL )
    {
        fprintf( stderr, "Memory allocation failure!\n" );
        exit( EXIT_FAILURE );
    }

    m[3][3]=3;
    printf( "%d", m[3][3] );

    free( m );
}

此程序具有以下输出:

3

请注意,在C中,与C++相比,generally discouraged没有必要转换malloc的结果。
此外,通常最好检查malloc的返回值,以验证内存是否已成功分配。此外,当您不再需要内存时,您通常应该使用free内存。

sxpgvts3

sxpgvts33#

类型int**表示类型pointer to an (int *),* 不是 * 二维数组。
所以m[3][3] = 3;在逻辑上等价于:

int * p = m[3];   // get the fourth pointer in the array-of-pointers-to-int
 p[3] = 3;         // set to 3 the fourth pointer in the int-array (p) points to

...这可能是好的,如果这样的数组存在,数组中的指针实际上指向有效的内存位置。然而,在提交的代码中,整个缓冲区此时仍未初始化(即使初始化了也不会包含预期的指针数据),因此取消引用p会调用未定义的行为。

jm81lzqq

jm81lzqq4#

其他答案都很棒。此外,这里有一些关于为什么的详细说明

m=(int **)malloc(10*10*sizeof(int));

这不是正确的用法。malloc只接受一个size_t参数(您想要分配的字节数),您已经给了它这个参数,但是您以错误的方式导出了它。你应该根据 * 被指向的对象 * 来分配空间,这是指针类型的“上一级”。在这里,你已经声明了m是一个int**类型,这意味着它将指向int*类型,这就是你应该用来派生malloc的大小:

m=(int **)malloc(10*sizeof(int*));

这是分配内存时始终遵循的范例,馈送到sizeof的东西应该是被指向的东西。使用上面的mallocm现在指向足够的空间来容纳10个int指针,或10个int* s。
到目前为止一切都很好,但有一个更好的实践方法来做到这一点:

m=(int **)malloc(10*sizeof(*m));

这里,*m是所指向的类型。而且无论m类型是 都将始终为真。如果m将类型更改为

struct myStruct** m;

然后你需要把转换从(int**)改为(struct myStruct**),同样的,把参数改为sizeof。在sizeof的参数中使用*<myVar>意味着,如果变量的类型发生了变化,维护工作会减少。
最后是no need to cast the return from malloc。上述优点之一是,如果m的类型发生变化,则会导致不必要的维护。最后,我们只剩下:

m = malloc(10 * sizeof(*m));

假设malloc没有返回NULL指针(你应该总是检查),你有足够的空间容纳10个int指针。现在,你可以像其他答案中所示的那样循环遍历,并为这10个int指针中的每一个分配空间:

for (int i=0; i<10; i++)
{
   m[i] = malloc(10 * sizeof(*(m[i])));
   // check if m[i] is NULL, handle that error as you want
   // assuming not NULL, m[i] now points to enough space for 10 ints
}

如上所述,m[i]int*类型,所以*(m[i])int类型。但是如果m的类型发生了变化,sizeof的所有参数都会“自动”更新。

sz81bmfz

sz81bmfz5#

您可以用途:

int (*m)[10] = malloc(10 * 10 * sizeof(m[0][0]));

现在,您可以直接索引:

m[3][3] = 37;
printf("%d\n", m[3][3]);

如果你的编译器支持VLA(可变长度数组),你可以有可变大小的数组:

int rows = 10;
int cols = 7;
int (*m)[cols] = malloc(rows * cols * sizeof(m[0][0]));
m[3][3] = 37;
printf("%d\n", m[3][3]);

行数和列数可以在运行时计算,而不需要在编译时设置。
在这两种情况下,使用free(m)释放分配的内存就足够了。

相关问题