Nano Hash - криптовалюты, майнинг, программирование

Статическая инициализация массива структур в C

У меня есть вопрос относительно инициализации массива структур в C. Поиск в Google показал мне, что у многих людей были очень похожие вопросы, но они не были полностью идентичными.

По сути, у меня есть глобальный массив структур типа «memPermissions», показанный ниже. Этот массив требует, чтобы все поля "address" и "ownerId" были инициализированы до -1 при выполнении программы.

typedef struct memPermissions {
    int address;
    int ownerId;
} *test;

Проблема в том, что размер массива определяется с помощью #define, поэтому я не могу просто пойти:

#define numBoxes 5

struct memPermissions memPermissions[numBoxes] = {
{-1, -1},
...
{-1, -1}
};

Я попытался:

struct memPermissions memPermissions[numBoxes] = {-1, -1};

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

Есть ли способ инициализировать все элементы этого массива структур без цикла?

Привет, - Джош


  • Если все числа равны -1 и все переменные равны int (только int, short, long, long long или char), вы можете memset массива присвоить -1 21.11.2011
  • Что плохого в использовании цикла? 21.11.2011
  • Похоже, вы пытаетесь инициализировать массив указателей? :) 21.11.2011
  • @Shahbaz: memset работает побайтно, а не int-для-int. 21.11.2011
  • @larsmans, да, но четыре байта -1 все равно -1 21.11.2011
  • Цикл, вероятно, лучшее решение, его легче всего читать, и в любом случае это то, что делает машина. 21.11.2011
  • @Josh: в чем проблема с циклом? 21.11.2011
  • @MartinBeckett: Нет, если у вас есть значение статического массива, оно обычно не устанавливается циклом. Он устанавливается во время компиляции программы, и массив существует в образе программы. Он загружается в оперативную память во время загрузки программы. 21.11.2011
  • Обратите внимание, что ваш typedef называет указатель на этот тип структуры как test, но затем ваш код продолжает использовать struct memPermissions. Лучше использовать простую 'struct memPermissions { ... };, чем неиспользуемую typedef. Имя test также не является явно хорошим выбором для указателя, даже если вам нужно имя для указателя (что лично мне не очень нравится). 21.11.2011
  • Цикл - это вариант, но я новичок в C и надеялся, что мое невежество не позволит мне узнать о более чистом решении. 21.11.2011
  • почему бы не использовать вместо этого {0, 0}? Было бы быстрее инициализировать и так же маловероятно, что это будут действительные данные? 21.11.2011
  • Может быть, есть хитрый способ сделать это с помощью макросов, но я недостаточно разбираюсь в макропроцессоре C, чтобы попытаться это сделать. 21.11.2011
  • Как сказал горячий лизун, я уверен, что вы могли бы найти какое-то злоупотребление процессором, чтобы сделать это. 21.11.2011
  • Вы, вероятно, не можете использовать препроцессор для решения этой проблемы, потому что он запрещает прямую рекурсию и не имеет других средств итерации. 21.11.2011

Ответы:


1

Единственным решением, которое приходит на ум, было бы инициализировать его где-нибудь простым циклом.

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

Однако вы можете обойти эту проблему, используя 0 для целей, которые в настоящее время выполняет ваш -1.

20.11.2011
  • Проблема была лучше всего решена в моем случае с использованием третьего поля в структуре, инициализированной до 0. К сожалению, я не мог использовать ни одно из двух существующих полей, потому что 0 был возможным значением для них, которое можно было установить позже в ходе выполнения программы. Однако, поскольку ваш ответ помог мне решить эту проблему, я принял его. Спасибо! 21.11.2011

  • 2

    Стандарт C99 добавил всевозможные полезные способы инициализации структур, но не предоставил оператора повторения (который был в Фортране с незапамятных времен, но, возможно, именно поэтому он почему не был добавлен).

    Если вы используете достаточно новую версию GCC и можете позволить себе использовать непереносимое расширение, тогда GCC предоставляет расширение. В руководстве GCC 8.1.0 (§6.27 Назначенные инициализаторы), в нем говорится:

    Чтобы инициализировать диапазон элементов одним и тем же значением, напишите «[first ... last] = value». Это расширение GNU. Например,

    int widths[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 };
    

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

    Итак, используя это в вашем примере:

    struct memPermissions memPermissions[numBoxes] =
    {
        [0..numBoxes-1] = {-1, -1},    // GCC extension
    };
    

    Я бы хотел, чтобы это было в стандарте C; это было бы так полезно!


    Без использования этого или других подобных механизмов, специфичных для компилятора, единственным выбором будет цикл. Для сложного инициализатора со многими полями, а не с одинаковыми значениями, вы, вероятно, можете использовать:

    #include <string.h>
    #include "memperm.h"  // Header declaring your types and variables
    
    static int initialized = 0;
    // -2 so the initialization isn't uniform and memset() is not an option
    static const struct memPermissions initPermissions = { -1, -2 };
    struct memPermissions memPermissions[numBoxes];
    
    void initialize_permissions(void)
    {
        if (initialized == 0)
        {
            for (int i = 0; i < numBoxes; i++)
                memmove(&memPermissions[i], &initPermissions, sizeof(initPermissions));
            initialized = 1;
        }
    }
    

    Вы также можете использовать здесь memcpy() — нет опасности перекрытия двух переменных.

    Теперь вам просто нужно убедиться, что initialize_permissions() вызывается перед использованием массива - желательно только один раз. Это также могут быть специфичные для компилятора механизмы.

    Вы можете использовать локальную переменную в функции initialize_permissions() вместо инициализированной статической постоянной переменной — просто убедитесь, что ваш компилятор не инициализирует ее каждый раз, когда вызывается функция.

    Если у вас есть компилятор C99, вы можете использовать составной литерал вместо константы:

    void initialize_permissions(void)
    {
        if (initialized == 0)
        {
            for (int i = 0; i < numBoxes; i++)
                memmove(&memPermissions[i],&(struct memPermissions){ -1, -2 },
                        sizeof(memPermissions[0]));
            initialized = 1;
        }
    }
    
    20.11.2011

    3

    Вы можете написать внешнюю программу, которой передается нужное количество элементов. Эта программа должна вызываться вашим Makefile или его эквивалентом. Программа напишет для вас включаемый файл с необходимым количеством значений -1, а также расширением #define.

    20.11.2011
  • Это действительно кажется излишеством 21.11.2011

  • 4

    Если у вас есть доступная стандартная библиотека, вы можете использовать memset в сочетании с sizeof(struct memPermissions) * numBoxes для заполнения массива любым унифицированным значением байта. Поскольку -1 равно 0xFFFFFFFF на многих платформах, это может сработать для вас.

    20.11.2011
  • Близко, но вам нужно учитывать размер int, и вам нужно написать -1 для них обоих. 21.11.2011

  • 5

    Если действительно важно не использовать цикл, вы можете сделать что-то довольно странное и использовать/злоупотреблять memset, предполагая, что это доступно.

    Примечание Memset может быть реализован с использованием цикла, поэтому это может быть спорным.

    memset(memPermissions, 0xFF, sizeof(memPermissions)*numBoxes*2*sizeof(int));
    

    Время 2 необходимо для обоих членов структуры (т.е. для двух из них).

    Это плохо спроектировано, поскольку зависит от того, что структура не дополняется и не выравнивается, что компилятор может делать в соответствии со спецификациями C.

    (Используя то, что -1 обычно равно 0xFFFFFFFF для отрицательных целых чисел с двумя дополнениями на 32-битных процессорах, с 32-битным int. Спасибо @James за указание на это.)

    Хотя я подозреваю, что в большинстве случаев код будет реализован в виде небольшого, быстрого, жесткого цикла (rep movsd для x86) на языке ассемблера во всех случаях, кроме самых тривиальных (очень маленькие значения numBoxes).

    20.11.2011
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

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

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..