Являются ли "malloc (sizeof (struct a *))" и "malloc (sizeof (struct a))" одинаковыми? - PullRequest
5 голосов
/ 06 января 2012

Этот вопрос является продолжением сбой вызова Malloc, но работает в других местах

Я попробовал следующую программу и обнаружил, что она работает (т. Е. Не дает сбоя - и это также упоминалось в вышеупомянутой ссылке). Возможно, мне повезло, что он работает, но я ищу разумное объяснение у экспертов SO, почему это работает?!

Вот некоторые основные принципы распределения memory с использованием malloc() w.r.t structures и pointers

  • malloc(sizeof(struct a) * n) выделяет n количество элементов типа struct a. И эта ячейка памяти может быть сохранена и доступна с помощью pointer-to-type-"struct a". В основном struct a *.
  • malloc(sizeof(struct a *) * n) выделяет n количество элементов типа struct a *. Каждый элемент может затем указывать на элементы типа struct a. В основном malloc(sizeof(struct a *) * n) выделяет array(n-elements)-of-pointers-to-type-"struct a". И выделенная ячейка памяти может быть сохранена и доступна с помощью pointer-to-(pointer-to-"struct a"). В основном struct a **.

Итак, когда мы создаем array(n-elements)-of-pointers-to-type-"struct a", это

  1. действительно, чтобы присвоить это struct a * вместо struct a **?
  2. действительно для доступа / отмены ссылки на выделенный array(n-elements)-of-pointers-to-type-"struct a" с использованием pointer-to-"struct a"?

data * array = NULL;

if ((array = (data *)malloc(sizeof(data *) * n)) == NULL) {
    printf("unable to allocate memory \n");
    return -1; 
}   

Фрагмент кода выглядит следующим образом:

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

int main(void) 
{
    typedef struct { 
        int value1;
        int value2;
    }data;

    int n = 1000;
    int i;
    int val=0;

    data * array = NULL;

    if ((array = (data *)malloc(sizeof(data *) * n)) == NULL) {
        printf("unable to allocate memory \n");
        return -1; 
    }   
    printf("allocation successful\n");

    for (i=0 ; i<n ; i++) {
        array[i].value1 = val++;
        array[i].value2 = val++;
    }   

    for (i=0 ; i<n ; i++) {
        printf("%3d %3d %3d\n", i, array[i].value1, array[i].value2);
    } 

    free(array);
    printf("freeing successful\n");

    return 0;
}

EDIT: ОК, если я сделаю следующее, ошибка

data * array = NULL;
if ((array = (data *)malloc(sizeof(data *) * n)) == NULL) {

Есть ли способ перехватить (во время компиляции, используя любые GCC флаги) эти типы непреднамеренных программных опечаток, которые могут работать время от времени и могут сорваться в любое время! Я скомпилировал это с помощью -Wall и не нашел предупреждений!

Ответы [ 4 ]

7 голосов
/ 06 января 2012

номер

sizeof(struct a*) - размер указателя .
sizeof(struct a) - это размер всей структуры.

5 голосов
/ 06 января 2012

Кажется, есть фундаментальное недоразумение.

malloc (sizeof (struct a) * n) выделяет n номеров типа struct a.

Нет, это именно то, что обычно используют, как после такого звонка. malloc(size) выделяет область памяти размером size байт. То, что вы делаете с этим регионом, полностью зависит от вас. Единственное, что имеет значение, это то, что вы не выходите за пределы выделенной памяти. Предполагая 4 байта float и int и 8 байтов double, после успешного malloc(100*sizeof(float)); вы можете использовать первые 120 из 400 байтов в качестве массива 15 double с, следующие 120 в качестве массива 30 float с, затем поместите массив из 20 char с сразу за этим и заполните оставшиеся 140 байтов 35 int с, если хотите. Это совершенно безобидное определенное поведение.

malloc возвращает void*, который может быть неявно приведен к указателю любого типа, поэтому

some_type **array = malloc(100 * sizeof(data *)); // intentionally unrelated types

совершенно нормально, это может быть не тот объем памяти, который вы хотели. В этом случае это очень вероятно, потому что указатели имеют одинаковый размер независимо от того, на что они указывают.

Скорее всего, неправильный объем памяти будет

data *array = malloc(n * sizeof(data*));

как у тебя было. Если вы используете выделенный фрагмент памяти в качестве массива n элементов типа data, существует три варианта

  1. sizeof(data) < sizeof(data*). Тогда ваша единственная проблема в том, что вы тратите немного места.
  2. sizeof(data) == sizeof(data*). Все в порядке, места не теряется, как будто у вас вообще не было опечаток.
  3. sizeof(data) > sizeof(data*). Затем вы получите доступ к памяти, к которой вы не должны были обращаться при прикосновении к последующим элементам массива, что является неопределенным поведением. В зависимости от различных вещей, которые могли бы последовательно работать, как если бы ваш код был корректным, немедленно приводить к аварийному завершению работы с сегфоутом или чем-то промежуточным (технически это могло бы вести себя так, что не может быть разумно помещено между этими двумя, но это было бы необычно). 1041 *

Если вы намеренно делаете это, зная, что применяются пункты 1. или 2., это плохо практика, но не ошибка. Если вы делаете это непреднамеренно, это ошибка независимо от того, какой пункт применяется, безвредный, но трудно найти, когда применяется 1. или 2., вредный, но обычно легче обнаружить в случае 3.

В твоих примерах. data было 4 соотв. 8 байтов (вероятно), что в 64-битной системе помещает их в 1. соотв. 2. с большой вероятностью в 32-битной системе на 2 соотв. 3.

Рекомендуемый способ избежать таких ошибок -

type *pointer = malloc(num_elems * sizeof(*pointer));
1 голос
/ 06 января 2012

Допустимо ли присвоить это структуре * вместо структуры **?

Ну, технически говоря, это действительно правильно, но неправильно (UB) разыменовывать такой указатель. Вы не хотите этого делать.

допустимо для доступа / отмены ссылки на выделенный массив (n-элементов) -of-pointers-to-type- "struct a" с использованием указателя на "struct a"?

Нет, неопределенное поведение.

1 голос
/ 06 января 2012

Этот array = (data *)malloc(sizeof(data *) * n) выделяет sizeof(data*) ( указатель ) для структуры data, если вы хотите сделать это, вам нужно, чтобы ваш array был data** array.

В вашем случае вы хотите, чтобы указатель указывал на sizeof(data), структуру в памяти, а не на другой указатель. Для этого потребуется data** (указатель на указатель).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...