Вопрос:

Используют ли статически распределенные массивы в C всю свою память, даже если некоторые их элементы не указаны?

c

43 просмотра

5 ответа

124 Репутация автора

Рассмотрим следующий код:

int array[1000000];
array[1] = 1;
array[10] = 10;

Мы статически выделили память для этого массива размером 1000000. Но все ли это используется?

Я имею в виду, если бы мы сделали это вместо:

int *array = malloc(sizeof(int) * 1000000);
array[1] = 1;
array[10] = 10;

Тогда кажется, что мы фактически используем все эти 1000000 пробелов: поскольку мы их выделили, они не могут быть использованы для чего-либо еще в памяти. Но для статически размещенного массива вещи неинициализированы, поэтому они все еще могут храниться в оставшихся 999998 местах, которые не имеют заданного значения.

По сути, я спрашиваю: будет int array[num]использовать меньше памяти, чем int array = malloc(sizeof(int) * num).

Автор: herophant Источник Размещён: 11.08.2019 07:49

Ответы (5)


0 плюса

10761 Репутация автора

Вот

int array[1000000];

используете ли вы / обращаетесь только к нескольким его элементам, таким как array[1]и array[10], но во время компиляции сами 1000000*4байты памяти были зарезервированы или фиксированы, arrayт.е. компилятор не может быть использован для других целей. Неинициализированный массив не имеет никакого значения в резервировании памяти для array.

По сути, я спрашиваю: будет int array[num]использовать меньше памяти, чем int array = malloc(sizeof(int) * num). ?

Нет , и для статического, и для динамического массива изначально резервируется одинаковое количество байтов, поэтому нет смысла предполагать, что компилятор выполнит какую-то оптимизацию и будет использовать неиспользуемые байты для каких-то других целей , если только вы не используете realloc()динамически создан буфер.

Автор: Achal Размещён: 11.08.2019 08:01

0 плюса

11562 Репутация автора

Нет, он не использует меньше памяти.

Компилятор резервирует место для статических переменных во время компиляции и сборки исполняемого файла в сегменте данных исполняемого файла.

Обратите внимание, что есть два раздела сегмента данных - инициализированный раздел и неинициализированный раздел.

// example code in global scope 
// `n` stored in initialised section of data segment 
int n = 0; 

// `count` stored in un-initialised section of data segment   
int count;

Кроме того , статические (и глобальные) переменные, которые явно инициализируются в программе , инициализируются. Из стандарта:

Объекты со статической продолжительностью хранения (3.7.1) должны быть инициализированы нулями (8.5) перед любой другой инициализацией. Обнуление инициализации и инициализация с постоянным выражением вместе называются статической инициализацией; все остальные инициализации динамические инициализации

Автор: suspectus Размещён: 11.08.2019 08:05

0 плюса

39296 Репутация автора

Если у вас есть int array[1000000];и используется только несколько его начальных членов, то в некоторых случаях (если arrayэто либо staticлокальный, либо локальный тип, если он является глобальным, и вы статически связываетесь с оптимизацией времени соединения), ваша цепочка инструментов может уменьшить / исключить массив под как будто правило . (Обратите внимание, что глобальные и статические переменные в действительности не инициализированы - стандарт C требует, чтобы они были инициализированы нулем.)

Gcc и clang делают это, и clang делает это даже с mallocмассивами до такой степени, что вся malloc-freeпара может быть устранена:

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

//gcc and clang optimize out the array, only using
//the constants 1 and 10
int pr(void)
{
   int array[1000000];
   array[1] = 1;
   array[10] = 10;
   return printf("%d %d", array[1], array[10]);
}
//clang optimizes out the dynamic allocation of array,
//only using the constants 1 and 10
int pr1(void)
{
   int r;
   int *array = malloc(1000000);
   if(!array) return -1;
   array[1] = 1;
   array[10] = 10;
   r = printf("%d %d", array[1], array[10]);

   free(array);
   return r;
}

Проверьте это на https://gcc.godbolt.org/z/UmiA34 .

Однако такие оптимизации непереносимы и раздражительны. Самые простые вещи, такие как передача указателя на массив функции, определенной в другом модуле перевода, могут их отключить. Я бы не стал на них полагаться.

Автор: PSkocik Размещён: 11.08.2019 08:22

0 плюса

68433 Репутация автора

Поведение программы будет немного отличаться, если вы определите массив как глобальный неинициализированный массив intили выделите его malloc()во время выполнения, но в обоих случаях ОС выделит 1000000 * sizeof(int)байты адресного пространства процесса для использования вашей программой.

В зависимости от реализации ОС и ее использования malloc(), это адресное пространство может оставаться виртуальным, пока программа не осуществит к нему фактический доступ. Если вы обращаетесь только к первому и одиннадцатому элементам массива, возможно, что для массива будет отображена только одна страница ОЗУ. Также возможно, что malloc()не удастся выделить эту память, если процесс настроен на выполнение с более низким пределом выделения памяти.

Не полагайтесь на это поведение и выделяйте только объем памяти, который вам нужен в любой момент времени. Если эта сумма является переменной, выделите ее во время выполнения с помощью malloc()или calloc().

Автор: chqrlie Размещён: 11.08.2019 08:25

0 плюса

91519 Репутация автора

Язык программирования C определяется в терминах абстрактной машины . Поведение программы описывается так, как оно происходит в абстрактной машине, которая имеет те же характеристики, что и целевая среда. Стандарт C определяет, что в этой абстрактной машине хранилище гарантированно зарезервировано для объектов на весь срок их службы, поэтому

int array[1000000];

будет иметь sizeof (int) * 1000000байтовую память, зарезервированную для его времени жизни (то есть до конца области действия, в которой был определен массив), так же как и объект, выделенный с помощью

int *array = malloc(sizeof (int) * 1000000);

где время жизни заканчивается на соответствующем free. Это теория.


Однако в стандарте говорится, что любой компилятор соответствует, даже если он создает программу, которая при запуске ведет себя так, как будто она была запущена на абстрактной машине в соответствии с ее правилами. Это называется правилом « как будто» . Так что на самом деле, если вы напишите что-то вроде

for (int i = 0; i < 100; i++) {
     int *p = malloc(sizeof (int) * 1000000);
}

компилятор может создать исполняемый файл, который вообще не вызывается, mallocпоскольку возвращаемое значение не используется. Или, если вы просто используете p[0]его, вы можете заметить, что на самом деле вы можете жить с ним int p_0и использовать его для всех расчетов. Или что-нибудь промежуточное.

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

Автор: Antti Haapala Размещён: 11.08.2019 08:49
Вопросы из категории :
32x32