【问题标题】:Why do I get a segmentation fault by declaring a 2d array in c?为什么通过在 c 中声明二维数组会出现分段错误?
【发布时间】:2020-09-05 07:41:23
【问题描述】:

我是线程新手,我有一个程序使用线程从 2d 数组中找到最小数,然后,它会找到数组中其他元素与最小数的距离并将它们存储在另一个数组。

用户应该输入数组的大小和他想要使用的线程数。

我尝试了下面的一维数组程序,它工作得很好。当我将它转换为适用于二维数组时,它开始崩溃并引发分段错误。但是,我找不到 2d 声明的哪一部分是错误的。

非常感谢任何帮助。

这是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <pthread.h>

struct Parameters
{
    // input
    int s,p; //n is size of array, p is number of threads
    int** array; //array with elements
    int start;
    int end;

    // output
    int smallest;
    int pos; //position if minimum
    int** B; //array that holds the distances
};

void* min(void* args)
{
    struct Parameters* p = (struct Parameters*)args;
    int **array = p->array;
    int **B1 = p->B;
    int start = p->start;
    int end = p->end;
    int smallest = array[start][start];
    int pos = p->pos;
    int distance;

    //find the smallest
    for (int i = start; i < end; i++)
    {
        for(int j = start; j < end; j++)
        {
            if (array[i][j] < smallest)
            {
                smallest = array[i][j];
                pos = i;
            }
        }  
    }

    //find the distances
    for(int i = 0; i < ((struct Parameters*)args) -> s; i++)
    {
        for(int j = 0; j < ((struct Parameters*)args) -> s; j++)
        {
            distance = abs(pos - i);
            B1[i][j] = distance;
        }
    }


    params->smallest = smallest;
    params->B = B1;

    return NULL;
}

int main()
{
    int smallest,pos;
    int s,p;

    struct Parameters *ptr = (struct Parameters *)malloc(sizeof(struct Parameters));

    if(ptr == NULL)
    {
        printf("Not enough. Try again \n");
        exit(0);
    }

    printf("Type s\n");
    scanf("%d",&(ptr->s));


    printf("Type p\n");
    scanf("%d", &(ptr->p));

    // declare an array of threads and associated parameter instances
    pthread_t threads[(ptr->p)];
    struct Parameters thread_parameters[(ptr->p)] ;

    int arr[ptr->s][ptr->s];
    int B2[ptr->s][ptr->s];

    // intialize the array    
    for(int i=0; i< ptr->s; i++)
    {
        for(int j=0; j< ptr->s; j++)
        {
        printf("Type a \n");
        scanf("%d",&arr[i][j]);
        }
    }

    // smallest needs to be set to something
    smallest = arr[0][0];

    // start all the threads
    for (int i = 0; i < ptr->p; i++)
    {
        memcpy(arr, thread_parameters[i].array, sizeof(arr));
        thread_parameters[i].s = ptr->s;
        memcpy(Bb, thread_parameters[i].B, sizeof(B2));
        thread_parameters[i].start = i * (ptr->s / ptr->p);
        thread_parameters[i].end = (i+1) * (ptr->s / ptr->p);
        pthread_create(&threads[i], NULL, min, &thread_parameters[i]);
    }

    // wait for all the threads to complete
    for (int i = 0; i < ptr->p; i++)
    {
        pthread_join(threads[i], NULL);
    }

    // Now aggregate the "smallest" and "largest" results from all thread runs    
    for (int i = 0; i < ptr->p; i++)
    {
        if (thread_parameters[i].smallest < smallest)
        {
            smallest = thread_parameters[i].smallest;
        }
    }

    printf("Smallest is %d\n", smallest);

    thread_parameters[ptr->p].B[ptr->s][ptr->s];

    for (int i = 0; i < 1; i++)
    {
        for(int j = 0; j < ptr->s;j++)
        {
            for(int k = 0; k < ptr->s; k++)
            {
                printf("Element %d is %d away from min\n",j,thread_parameters[i].B[j][k]);
            }
        }
   }

    return 0;
}

谢谢!!

【问题讨论】:

  • 取决于数组有多大,因为可变长度数组是在堆栈上声明的,如果程序用尽堆栈内存来分配您的数组,运行时的唯一选择是失败并显示分段错误。
  • 解决问题的一种方法是使用malloc 或其他内存分配函数的堆分配数组。
  • 感谢您的回答,请问如何使用malloc?我试过了,但我不认为它是在正确的地方
  • 让我写一个完整的答案
  • 非常感谢您,这将非常有帮助:)

标签: c arrays unix pthreads


【解决方案1】:

您的代码问题也可能来自:

memcpy(arr, thread_parameters[i].array, sizeof(arr));
...
memcpy(Bb, thread_parameters[i].B, sizeof(B2));

由于thread_parameters[i].arraythread_parameters[i].B 未分配,如果您只读取数组,则仅通过地址传递它们可能会很好

thread_parameters[i].array = arr

但对于thread_parameters[i].B,您需要分配数组并执行深层复制(memcpy 不起作用)


下面的文字没有回答这个问题,但确实提供了一些关于 VLA 使用的见解

声明可变长度数组导致分段的一个原因是该值太大而无法在堆栈上分配数组(某些编译器选择此选项,此选择可能有性能原因)。
从在堆栈上分配内存的失败中彻底恢复的选择并不多,因为在运行时在同一堆栈上下文中几乎没有办法清理堆栈内存。

您可以通过在堆上分配二维数组来缓解此问题,其中一些策略可用here(thanks @Lundin)here

int** alloc_2d_int_array(size_t rows, size_t cols) {
     int **result = malloc(rows * sizeof(int *));
     if(result == NULL) {
          // could not allocate more memory
          return NULL;
     }
     size_t row_size = cols * sizeof(int); 
     for(int i=0; i < rows; ++i) {
         result[i] = malloc(row_size);
         if(result[i] == NULL) {
              // could not allocate more memory
              // cleanup
              return NULL;
         }
     }
     return result;
}

上面的实现没有经过测试,但是确实可以编译,还是有整数溢出的风险。

然后使用上面的define函数如下:

int **arr = alloc_2d_int_array(ptr->s, ptr->s);
int **B2 = alloc_2d_int_array(ptr->s, ptr->s);

更容易实现(参见here(thanks @Lundin)

int **arr = malloc(sizeof(int[ptr->s][ptr->s]);
int **B2 = malloc(sizeof(int[ptr->s][ptr->s]);

【讨论】:

  • 非常感谢!那真的很有帮助!当我在 main 中声明数组时应该使用它吗?
  • 又是另外一回事,所以最好分配内存而不是声明数组[arr_size][arr_size] 对吧?
  • 最后提供了一些例子,
  • 在堆栈上分配确实有一些优势(您会获得一些性能提升,具体取决于堆栈上的数据将如何缓存),但在堆上分配是更安全且更可控的方法。
  • 与其推荐那个极客网站,不如改成Correctly allocating multi-dimensional arrays
猜你喜欢
  • 1970-01-01
  • 2012-12-31
  • 1970-01-01
  • 2011-03-04
  • 2020-05-26
  • 1970-01-01
  • 1970-01-01
  • 2021-03-17
  • 1970-01-01
相关资源
最近更新 更多