2011-11-08 3 views
0

Из чистого любопытства я начал играть с массивом способами, которые я никогда раньше не использовал. Я попытался сделать массив структур данных, и установите его равным другой:Заменить массив другим массивом в C

typedef struct _test { 
    float value; 
} test; 

достаточно просто struct, поэтому я попытался это:

test struct1[10]; 
test struct2[20]; 
struct1 = struct2; 

Я не думаю, что это будет работать, и это Бесполезное Даже компилировать. Но это меня очень интересует. Можно ли взять массив из 10 и увеличить размер до 20 при копировании данных?

Objective-C

Я на самом деле делает это с Objective-C, так что я хотел бы услышать от людей, Objective-C, а также. Я хочу посмотреть, можно ли изменить размер struct1 в этом файле.

@interface Object : NSObject { 
    test struct1; 
} 

Помните: Это лишь из любопытства, так что все открыто для обсуждения.

ответ

1

В С, я считаю, что это хорошее место для использования функции realloc. Однако он будет работать только с динамически распределенными массивами. Невозможно изменить память, выделенную struct1 декларацией test struct1[10];.

+0

realloc работает с динамически выделенными указателями, а не на массивах (которые выделяются компилятором). – zmbq

+0

Хм, да. Я считаю, что это то, что я сказал. –

+0

Итак, если бы я использовал функцию 'malloc',' realloc' был бы хорошим вариантом? – Justin

1

В массивах C есть константы, вы не можете изменить их значение (то есть их адрес) вообще, и вы не можете изменить их размер.

+0

Это может быть так, но все же интересно подумать об этом и найти способ решения этой проблемы. – Justin

1

Очевидно, что если вы объявите массив с фиксированным размером, test struct1[10], то его нельзя изменить. Что вам нужно сделать, это объявить его как указатель:

test *struct1; 

Затем вы должны использовать malloc выделить массив и может использовать realloc, чтобы изменить его размер, сохраняя содержимое исходного массива.

struct1 = malloc(10*sizeof(*struct1)); 
//initialize struct1 ... 
test *struct2 = realloc(struct1, 20*sizeof(*struct1)); 
+0

Важно отметить, что большинство людей тогда помещают 'free()' там. Будет ли это еще лучше, если вы захотите использовать переменную массива позже? – Justin

+0

Что бы вы бесплатно?Предположительно вы хотите использовать struct2. Очевидно, что вы не можете использовать что-то после бесплатного. –

+0

Хорошо, но я просто говорю, что если, например, 'struct2' использовался только как способ увеличить размер' struct1', он должен быть освобожден. Правильно ли я, или это объяснение многих ошибок? – Justin

2

Вы не можете сделать это на C со статическими массивами, но вы можете сделать это с помощью динамически распределенных массивов. Например,

float *struct1, *struct2, *struct3; 
if(!(struct1 = malloc(10 * sizeof(float))) { 
    // there was an error, handle it here 
} 
if(!(struct2 = realloc(struct1, 20 * sizeof(float))) { 
    // there was an error, handle it here 
    // struct1 will still be valid 
} 
if(!(struct3 = reallocf(struct2, 40 * sizeof(float))) { 
    // there was an error, handle it here 
    // struct2 has been free'd 
} 
+0

Довольно интересно. Если бы вы пробовали этот путь, не было бы возможным создать массив безграничного размера, основанный на других переменных? Для интенсивного, если я сделал 'void setSize (int size)', его можно было разместить на максимальном размере (что бы это ни было) z – Justin

+0

Хороший вопрос: вам нужно проверить, что malloc и realloc могут фактически распределять объем пространства, for - они возвращают NULL, была ошибка. Я отредактировал вышеупомянутую запись, чтобы отразить это. –

+0

Спасибо за информацию! Я вижу, как это может помочь в будущем. – Justin

2

Как уже отмечалось, массивы, выделенные таким образом, являются статическими и не могут быть изменены. Вы должны использовать указатели (выделение массива с malloc или calloc), чтобы иметь изменяемый размер массива, а затем вы можете использовать realloc. Вы должны использовать free, чтобы избавиться от него (иначе вы будете утечка памяти). В C99 размер вашего массива можно вычислить во время выполнения, когда его выделено (в C89 его размер должен был вычисляться во время компиляции), но не может быть изменен после выделения. В C++ вы должны использовать std::vector. Я подозреваю, что Objective-C имеет нечто вроде вектора C++.

Но если вы хотите, чтобы скопировать данные от одного массива, а другой в C, используйте memcpy:

/* void *memcpy(void *dest, const void *src, size_t n) 
    note that the arrays must not overlap; use memmove if they do */ 
memcpy(&struct1, &struct2, sizeof(struct1)); 

Это будет копировать только первые десять элементов, конечно же, так как Struct1 длится всего десять элементов. Вы можете скопировать последнюю десятку (например), изменив &struct2 на struct2+10 или &(struct2[10]).В C, конечно, не сбегает конец массива - ваша ответственность: memcpy не проверяет.

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

+0

Интересный подход. Я никогда не слышал об этом раньше, но это имеет для меня большой смысл. Я предполагаю, что sizeof() не должен быть просто числовой константой? – Justin

+0

@Justin: 'sizeof' фактически является числовой константой, он просто позволяет компилятору сделать математику для вас. Он также знает, как компилятор решил выложить ваши данные (например, любое дополнение, которое оно может добавить). – derobert

+0

Я этого не знал. Это очень хороший способ сделать это. – Justin

3

Что-то еще, что не совсем точно уместно на ваш вопрос, но интересно, тем не менее, является то, что хотя массивы не могут быть назначены, структуры, содержащие массивы может быть назначены:

struct test 
{ 
    float someArray[100]; 
}; 


struct test s1 = { /* initialise with some data*/ }; 
struct test s2 = { /* initialise with some other data */ }; 

s1 = s2; /* s1's array now contains contents of s2's array */ 

Это также дает возможность вернуть фиксированную длину массивы данных из функций (так как возвратные простые массивы не допускаются):

struct test FunctionThatGenerates100Floats(void) 
{ 
    struct test result; 
    for (int i = 0; i < 100; i++) 
     result.someArray[i] = randomfloat(); 

    return result; 
} 
+0

Это еще один превосходный подход к использованию структур. Спасибо, что поделился! – Justin

1

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

Но вы пытаетесь сделать это со структурой? Что бы это значило? Предположим, вы увеличиваете объем памяти, доступный для struct1 в Object. Это по-прежнему структура с одним членом и больше ничего не делает.

Является ли идея сделать объект способным содержать расширенную структуру?

typedef struct _test2 { 
    float value; 
    NSObject *reference; 
} test2; 

Но вы все еще не можете получить доступ к reference нормально, потому что это не известная часть объекта.

Object *object2; 
... 
NSLog(@"%@", object2.struct1.reference); // does not compile 

Если вы знали, что вы были одним из ваших модифицированных объектов, вы могли бы сделать

Object *object2; 
... 
NSLog(@"%@", ((test2)(object2.struct1)).reference); 

А также вы можете по-прежнему предположительно пройдет object2 к чему-либо, что ожидает объекта. У этого есть только шанс работать, если struct1 является последним членом Объекта, и не вмешивается в объект подкласса.

Возможно, после этого может возникнуть множество разнообразных трюков realloc, но я не думаю, что realloc в частности, потому что это предназначено для использования в объектах, которые выделяются с помощью malloc, а также о деталях того, какая функция C используется для выделения объектов в не показано в Objective C, поэтому вы не должны предполагать, что это malloc. Если вы переопределите alloc, вы можете убедиться, что используется malloc.

Также вы должны следить за тем, что в Objective C обычно используется более одного указателя на объект, который существует. realloc может перемещать объект, который не будет семантически корректным, если вы не исправите все указатели.

+0

Еще один интересный подход. Я не уверен, что вы можете поместить объект в структуру, но я проверю его. Я использую struct, поэтому я могу сделать что-то вроде 'struct1.value = 1' вместо' [[Object struct1] setValue: 1] '. Это намного лучше посмотреть и отредактировать. – Justin

+0

Да, вы можете поместить объект _pointer_ в объект в структуре, но это был всего лишь пример. – morningstar

+0

Мне кажется, вам нужно больше познакомиться с синтаксисом Objective C. Вам не нужно делать что-либо подробное, чтобы установить поле объекта. Изнутри объекта вы можете просто сказать «значение = 1». Вне объекта вы не можете делать 'struct1.value = 1'. Но вы можете настроить @property, а затем вы можете получить доступ к значению с помощью точечной нотации. В любом случае я не понимаю, что для этой цели имеет _resizable_ struct. То, что вы пытаетесь сделать, может быть сделано только так хакерски, что это определенно не стоит того, чтобы сделать ваш код красивее. В любом случае Objective C уже поддерживает довольно хороший код. – morningstar

Смежные вопросы