Это не приведет к конкуренции в кодовом гольф-соревновании и заимствует предыдущие ответы, но ясно показывает, как добавляется ключ и как работает соединение. Это создает 2 новых фрейма данных из списков, а затем добавляет ключ для декартового продукта.
Моим вариантом использования было то, что мне понадобился список всех идентификаторов магазина в течение каждой недели в моем списке. Итак, я создал список всех недель, которые я хотел иметь, а затем список всех идентификаторов хранилища, которые я хотел сопоставить им.
Слияние, которое я выбрал влево, но будет семантически таким же, как внутренний в этой настройке. Вы можете увидеть это в документации по слиянию , в которой говорится, что он имеет декартово произведение, если комбинация клавиш появляется более двух раз в обеих таблицах - это то, что мы установили.
days = pd.DataFrame({'date':list_of_days})
stores = pd.DataFrame({'store_id':list_of_stores})
stores['key'] = 0
days['key'] = 0
days_and_stores = days.merge(stores, how='left', on = 'key')
days_and_stores.drop('key',1, inplace=True)
{
void *mem = malloc(1024+16);
void *ptr = ((char *)mem+16) & ~ 0x0F;
memset_16aligned(ptr, 0, 1024);
free(mem);
}
{
void *mem = malloc(1024+15);
void *ptr = ((uintptr_t)mem+15) & ~ (uintptr_t)0x0F;
memset_16aligned(ptr, 0, 1024);
free(mem);
}
первый шаг должен выделить достаточно запасного места на всякий случай. Так как память должна составить выровненных 16 байтов (подразумевать, что ведущий адрес байта должен быть кратным 16), добавляя, что 16 дополнительных байтов гарантируют, что у нас есть достаточно пространства. Где-нибудь в первых 16 байтах, существует выровненный указатель 16 байтов. (Обратите внимание, что malloc()
, как предполагается, возвращает указатель, который является достаточно хорошо выровненный для [1 159] любой цель. Однако значение 'любого', прежде всего, для вещей как основные типы — long
, double
, long double
, long long
, и указатели на объекты и указатели на функции. Когда Вы делаете более специализированные вещи, как игра с графическими системами, им может быть нужно более строгое выравнивание, чем остальная часть системы — следовательно вопросы и ответы как это.)
следующий шаг должен преобразовать пустой указатель на символьный указатель; GCC, несмотря на это, Вы, как предполагается, не делаете адресную арифметику с указателями на пустых указателях (и GCC имеет предупреждение опций сказать Вам при злоупотреблении его). Тогда добавьте 16 к указателю запуска. Предположим malloc()
, возвратил Вас невозможно плохо выровненный указатель: 0x800001. Добавление этих 16 дает 0x800011. Теперь я хочу округлить в меньшую сторону до 16-байтовой границы —, таким образом, я хочу сбросить последние 4 бита к 0. 0x0F имеет последний набор на 4 бита одному; поэтому, ~0x0F
имеет весь набор битов одному кроме последних четырех. Выполнение операции "И", которое с 0x800011 дает 0x800010. Можно выполнить итерации по другим смещениям и видеть, что та же арифметика работает.
последний шаг, free()
, легок: Вы всегда, и только, возвращаетесь к [1 114] значение, что один из [1 115], calloc()
или realloc()
возвратил Вам —, что-либо еще - бедствие. Вы правильно обеспечили mem
для содержания того значения — спасибо. Бесплатные выпуски это.
Наконец, если Вы знаете о внутренностях своей системы malloc
пакет, Вы могли бы предположить, что это могло бы хорошо возвратиться, 16 байтов выровняли данные (или это могли бы быть выровненных 8 байтов). Если бы это были выровненных 16 байтов, то Вы не должны были бы укорачивать со значениями. Однако это - изворотливый и непортативный — другой malloc
, пакеты имеют различное минимальное выравнивание и поэтому принятие одной вещи, когда это делает что-то другое привело бы к дампам ядра. В широких пределах это решение является портативным.
Кто-то еще упомянул posix_memalign()
как другой способ получить выровненную память; это не доступно везде, но могло часто реализовываться с помощью этого в качестве основания. Обратите внимание, что было удобно, что выравнивание было питанием 2; другое выравнивание более грязно.
Еще один комментарий — этот код не проверяет что выделение, за которым следуют.
указал, что Вы не можете сделать операций битовой маски на указателях, и, действительно, GCC (3.4.6 и 4.3.1 протестированных) действительно жалуется как этот. Так, исправленная версия абсолютного кода — преобразованный в основную программу, следует. Я также брал на себя смелость добавления всего 15 вместо 16, как был указан. Я использую uintptr_t
, так как C99 был вокруг достаточно долго, чтобы быть доступным на большинстве платформ. Если бы это не было для использования [1 123] в эти printf()
операторы, это было бы достаточно к [1 125] вместо того, чтобы использовать #include <inttypes.h>
. [Этот код включает фиксацию, на которую указывают [1 151] C.R. , который повторял мнение, сначала высказанное [1 152] счет K много лет назад, который мне удалось пропустить до сих пор.]
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void memset_16aligned(void *space, char byte, size_t nbytes)
{
assert((nbytes & 0x0F) == 0);
assert(((uintptr_t)space & 0x0F) == 0);
memset(space, byte, nbytes); // Not a custom implementation of memset()
}
int main(void)
{
void *mem = malloc(1024+15);
void *ptr = (void *)(((uintptr_t)mem+15) & ~ (uintptr_t)0x0F);
printf("0x%08" PRIXPTR ", 0x%08" PRIXPTR "\n", (uintptr_t)mem, (uintptr_t)ptr);
memset_16aligned(ptr, 0, 1024);
free(mem);
return(0);
}
И вот незначительно более обобщенная версия, которая будет работать на размеры, которые являются питанием 2:
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void memset_16aligned(void *space, char byte, size_t nbytes)
{
assert((nbytes & 0x0F) == 0);
assert(((uintptr_t)space & 0x0F) == 0);
memset(space, byte, nbytes); // Not a custom implementation of memset()
}
static void test_mask(size_t align)
{
uintptr_t mask = ~(uintptr_t)(align - 1);
void *mem = malloc(1024+align-1);
void *ptr = (void *)(((uintptr_t)mem+align-1) & mask);
assert((align & (align - 1)) == 0);
printf("0x%08" PRIXPTR ", 0x%08" PRIXPTR "\n", (uintptr_t)mem, (uintptr_t)ptr);
memset_16aligned(ptr, 0, 1024);
free(mem);
}
int main(void)
{
test_mask(16);
test_mask(32);
test_mask(64);
test_mask(128);
return(0);
}
Для преобразования test_mask()
в функцию выделения общего назначения единственное возвращаемое значение от средства выделения должно было бы закодировать адрес выпуска, как несколько человек указали в их ответах.
прокомментированный Uri: Возможно, у меня есть проблема понимания прочитанного этим утром, но если вопрос об интервью конкретно говорит: "Как Вы выделили бы 1 024 байта памяти", и Вы ясно выделяете больше, чем это. Это не было бы автоматическим отказом от интервьюера?
Мой ответ не впишется в комментарий с 300 символами...
Это зависит, я предполагаю. Я думаю, что большинство людей (включая меня) ответило на вопрос для значения, "Как Вы выделите место, в котором 1 024 байта данных могут быть сохранены, и где базовый адрес является кратным 16 байтам". Если интервьюер действительно имел в виду, как можно выделить 1 024 байта (только) и иметь его выровненных 16 байтов, то опции более ограничены.
Однако, если бы интервьюер ожидал любой из тех ответов, я ожидал бы, что они распознают, что это решение отвечает на тесно связанный вопрос, и затем повторно структурировать их вопрос указать на разговор в корректном направлении. (Далее, если бы интервьюер добрался действительно stroppy, то я не хотел бы задания; если ответ на недостаточно точное требование подстрелен в провокационных сообщениях без исправления, то интервьюер не кто-то, на кого безопасно работать.)
заголовок вопроса недавно изменился. Это было , Решают выравнивание памяти в вопросе об интервью C, который озадачил меня . Пересмотренный заголовок (, Как выделить выровненную память, только пользующуюся стандартной библиотекой? ), требует, чтобы немного пересмотренный ответ — это приложение обеспечил его.
C11 (ISO/IEC 9899:2011) добавленная функция aligned_alloc()
:
7.22.3.1
aligned_alloc
функцияОписание
Резюме#include <stdlib.h> void *aligned_alloc(size_t alignment, size_t size);
Эти
aligned_alloc
функция выделяет место для объекта, выравнивание которого определяется [1 131], чей размер определяется [1 132], и чье значение неопределенно. Значение [1 133] должно быть допустимым выравниванием, поддерживаемым реализацией, и значение [1 134] должно быть интегралом несколько из [1 135].Возвраты
aligned_alloc
функция возвращает или нулевого указателя или указатель на выделенное место.
И POSIX определяет posix_memalign()
:
#include <stdlib.h> int posix_memalign(void **memptr, size_t alignment, size_t size);
ОПИСАНИЕ
Эти
posix_memalign()
функция должна выделитьsize
байты, выровненные на границе, определенной [1 140], и должна возвратить указатель на выделенную память в [1 141]. Значение [1 142] должно быть питанием два несколько из [1 143].После успешного завершения, значение, на которое указывают [1 144], должно быть кратным [1 145].
, Если размер пространства, которое требуют, 0, поведение определяется реализацией; значение, возвращенное в [1 146], должно быть или нулевым указателем или уникальным указателем.
Эти
free()
функция должна освободить память, которая была ранее выделена [1 148].ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
После успешного завершения,
posix_memalign()
должно возвратить нуль; иначе код ошибки должен быть возвращен для указания на ошибку.
Или или оба из них мог использоваться для ответа на вопрос теперь, но только функция POSIX была опцией, когда на вопрос первоначально ответили.
Негласно, новая выровненная функция памяти делает почти такое же задание, как обрисовано в общих чертах в вопросе, кроме у них есть способность вызвать выравнивание более легко и отслеживать запуск выровненной памяти внутренне так, чтобы код не имел дело с особенно —, это просто освобождает память, возвращенную функцией выделения, которая использовалась.
Возможно, они были бы удовлетворены знанием memalign? И поскольку Jonathan Leffler указывает, существует две более новых предпочтительных функции для знания о.
ой, флорин избил меня к нему. Однако, если Вы читаете страницу справочника, я связался с, Вы, скорее всего, поймете пример, предоставленный более ранним плакатом.
Вот альтернативный подход к вокруг' части. Не наиболее блестяще кодированное решение, но это сделало задание, и этот тип синтаксиса немного легче помнить (плюс, работал бы на значения выравнивания, которые не являются питанием 2). Эти uintptr_t
бросок был необходим для успокоения компилятора; адресная арифметика с указателями не очень любит разделение или умножение.
void *mem = malloc(1024 + 15);
void *ptr = (void*) ((uintptr_t) mem + 15) / 16 * 16;
memset_16aligned(ptr, 0, 1024);
free(mem);
Вы могли также попробовать posix_memalign()
(на платформах POSIX, конечно).
Три немного отличающихся ответа, зависящие, как Вы смотрите на вопрос:
1) Достаточно хороший для точного вопроса, который задают, решение Jonathan Leffler, за исключением того, что окружить к 16 выровненному, Вам только нужны 15 дополнительных байтов, не 16.
А:
/* allocate a buffer with room to add 0-15 bytes to ensure 16-alignment */
void *mem = malloc(1024+15);
ASSERT(mem); // some kind of error-handling code
/* round up to multiple of 16: add 15 and then round down by masking */
void *ptr = ((char*)mem+15) & ~ (size_t)0x0F;
B:
free(mem);
2) Для более универсальной функции выделения памяти, вызывающая сторона не хочет должной быть отслеживать два указателя (один для использования и один для свободного). Таким образом, Вы храните указатель на 'реальный' буфер ниже выровненного буфера.
А:
void *mem = malloc(1024+15+sizeof(void*));
if (!mem) return mem;
void *ptr = ((char*)mem+sizeof(void*)+15) & ~ (size_t)0x0F;
((void**)ptr)[-1] = mem;
return ptr;
B:
if (ptr) free(((void**)ptr)[-1]);
Примечание, что, в отличие от этого (1), где только 15 байтов были добавлены к мадам, этот код мог на самом деле , уменьшает выравнивание, если Ваша реализация, оказывается, гарантирует 32-байтовое выравнивание от malloc (вряд ли, но в теории реализация C могла иметь выровненный тип 32 байтов). Это не имеет значения, ли все, что Вы делаете, назвать memset_16aligned, но если Вы используете память для структуры тогда, это могло бы иметь значение.
я не уверен пренебрежительный, что хорошая фиксация для этого (кроме предупредить пользователя, что возвращенный буфер не обязательно подходит для произвольных структур), так как нет никакого способа определить программно, какова определенная для реализации гарантия выравнивания. Я предполагаю запуск, Вы могли выделить два или больше 1-байтовых буфера и предположить, что худшее выравнивание, которое Вы видите, является гарантируемым выравниванием. Если Вы неправы, Вы тратите впустую память. Любой с лучшей идеей, скажите так...
[ Добавленный : 'стандартный' прием должен создать объединение, 'вероятно, чтобы быть максимально выровненным типы' для определения необходимого выравнивания. Максимально выровненные типы, вероятно, будут (в C99) 'long long
', 'long double
', 'void *
', или' void (*)(void)
'; если Вы включаете <stdint.h>
, Вы могли бы, по-видимому, использовать' intmax_t
' вместо [1 111] (и, на Питании 6 (AIX), машины, intmax_t
дадут Вам 128-разрядный целый тип). Требования выравнивания для того объединения могут быть определены путем встраивания его в структуру с единственным символом, сопровождаемым объединением:
struct alignment
{
char c;
union
{
intmax_t imax;
long double ldbl;
void *vptr;
void (*fptr)(void);
} u;
} align_data;
size_t align = (char *)&align_data.u.imax - &align_data.c;
Вы тогда использовали бы большее из требуемого выравнивания (в примере, 16) и align
значение, вычисленное выше.
На (64-разрядном) Солярисе 10, кажется, что основное выравнивание для результата malloc()
является кратным 32 байтам.
]
На практике, выровненные средства выделения часто берут параметр для выравнивания, а не этого быть предрасположенное. Таким образом, пользователь передаст в размере структуры, о которой они заботятся (или наименьшее количество питания 2 больших, чем или равный этому), и все будут хорошо.
3) Использование, что обеспечивает Ваша платформа: posix_memalign
для POSIX, _aligned_malloc
в Windows.
4) при использовании C11, тогда самое чистое - портативный и краткий - опция состоит в том, чтобы использовать стандартную библиотечную функцию aligned_alloc
, которая была представлена в этой версии спецификации языка.
size =1024;
alignment = 16;
aligned_size = size +(alignment -(size % alignment));
mem = malloc(aligned_size);
memset_16aligned(mem, 0, 1024);
free(mem);
Hope этот - самая простая реализация, сообщите мне свои комментарии.
На фронте заполнения 16 и 15 байтов фактическое число, которое вам нужно добавить, чтобы получить выравнивание N, составляет max (0, NM) , где M - естественное выравнивание распределителя памяти (и оба являются степенями 2).
Поскольку минимальное выравнивание памяти любого распределителя составляет 1 байт, 15 = max (0,16-1) - консервативный ответ. Однако, если вы знаете, что ваш распределитель памяти будет выдавать вам 32-битные адреса, выровненные по int (что довольно часто), вы могли бы использовать 12 в качестве контактной площадки.
Это не важно для данного примера, но может быть важно для встроенной системы с 12 КБ ОЗУ, где учитывается каждый сохраненный int.
Лучший способ реализовать его, если вы действительно собираетесь сохранить каждый возможный байт, - это использовать макрос, чтобы вы могли передать ему свою собственную память выравнивание. Еще раз, это, вероятно, полезно только для встроенных систем, где вам нужно сохранить каждый байт.
В приведенном ниже примере для большинства систем значение 1 вполне подходит для MEMORY_ALLOCATOR_NATIVE_ALIGNMENT
, однако для нашей теоретической встроенной системы с 32-битное выравнивание, следующее может сэкономить крошечный бит драгоценной памяти:
#define MEMORY_ALLOCATOR_NATIVE_ALIGNMENT 4
#define ALIGN_PAD2(N,M) (((N)>(M)) ? ((N)-(M)) : 0)
#define ALIGN_PAD(N) ALIGN_PAD2((N), MEMORY_ALLOCATOR_NATIVE_ALIGNMENT)
К сожалению, в C99 довольно сложно гарантировать любое выравнивание таким образом, чтобы его можно было переносить в любую реализацию C, соответствующую C99. Почему? Поскольку указатель не обязательно является «байтовым адресом», это можно представить с помощью плоской модели памяти. Также не гарантируется представление uintptr_t , которое в любом случае является необязательным типом.
Возможно, мы знаем о некоторых реализациях, которые используют представление для void * (и по определению также char * ), который является простым байтовым адресом, но для C99 он непрозрачен. нам, программистам. Реализация может представлять указатель с помощью набора { сегмента , смещения }, где смещение могло иметь неизвестное выравнивание «в действительности». Почему, указатель может даже быть некоторой формой значения поиска в хеш-таблице или даже значением поиска в связанном списке. Он мог кодировать информацию о границах.
В недавнем черновике C1X для стандарта C мы видим ключевое слово _Alignas . Это может немного помочь.
Единственная гарантия, которую дает нам C99, заключается в том, что функции выделения памяти будут возвращать указатель, подходящий для присвоения указателю, указывающему на любой тип объекта. Поскольку мы не можем указать выравнивание объектов, мы не можем реализовать наши собственные функции распределения, отвечающие за выравнивание, четко определенным переносимым способом.
Было бы хорошо ошибиться в этом утверждении.