Можно ли использовать строки в качестве индекса массива? - PullRequest
5 голосов
/ 06 января 2009

Может ли строка использоваться как индекс массива в C?

Ex: Строка Соответствующее значение "ОДИН" 1 "ДВА" 2 "ПЯТЬ" 5 "ДЕСЯТЬ" 10

Когда строка из приведенного выше списка передается функции, функция должна вернуть соответствующее значение, указанное выше. Может ли это быть достигнуто путем объявления константного массива со строкой в ​​качестве индекса

int *x;
x["ONE"]  = 1;
x["TWO"]  = 2;
x["FIVE"] = 5;
x["TEN"]  = 5;

return x["string received by the function"];

Вышеприведенная логика не работает должным образом; Есть ли обходной путь для реализации вышеуказанной логики для получения массива с индексированной строкой?

Ответы [ 8 ]

17 голосов
/ 06 января 2009

Может скомпилироваться, но работать не будет.

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

Если вы ищете что-то более похожее на перечислимый тип , и вы можете положиться на C89 , посмотрите на что-то вроде:

enum cardsuit {
   CLUBS,
   DIAMONDS,
   HEARTS,
   SPADES
};

Если вы не можете положиться на C89, то вам стоит попробовать typedef обман.

10 голосов
/ 06 января 2009

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

В C ссылка на массив выполняется с помощью массива или указателя и некоторого целого числа. (в x [1] x - массив, а 1 - целое число). Пока вы используете какой-то целочисленный тип, он будет работать так, как вы ожидаете.

Предположим, у вас есть что-то, что не является целым числом. В этом случае реализация C увидит, сможет ли она преобразовать его в соответствующий тип, поэтому вы получите массив и целое число. Именно в таких случаях вы сталкиваетесь с проблемами (и немного более сложные версии этого в C ++ сбивают с толку более опытных людей, чем вы).

В C литеральная строка типа "one" имеет тип const char *, что означает указатель на символы, которые вы не можете изменить. Фактическое значение - это адрес памяти, где строка фактически находится в памяти. Обычно вы не обращали бы внимания на это значение указателя и смотрели бы на строковое значение, но здесь есть ошибка.

В C любой указатель данных может быть преобразован в некоторое целое число, и будет автоматически. Следовательно, у вас есть строка типа «one», и ее значением является любое число, представляющее адрес памяти. Используйте его там, где C ожидает какое-то целое число, и оно будет преобразовано в какое-то целое или другое значение.

Следовательно, это то, что происходит с x ["ONE"]. Система C должна поместить строку «ONE» где-то в памяти, и не имеет значения, где. Вероятно, это будет где-то с довольно большим адресом памяти, вполне возможно, в миллиардах. Когда он видит x ["ONE"], он пытается преобразовать это значение в целое число и использует его как индекс. Следовательно, вы пытаетесь получить доступ к массиву x далеко за его пределами, и это вызывает проблему. Либо вы пытаетесь использовать память, которой вам не разрешено, и система просто останавливает вас, либо вы копаетесь в клочке памяти, который вы должны оставить в покое, и это может произойти каким-то таинственным образом позже.

3 голосов
/ 06 января 2009

Вы можете легко создавать таблицы поиска с помощью функции bsearch(), предоставляемой stdlib.h. Рабочий пример таков:

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

#define count(ARRAY) (sizeof(ARRAY)/sizeof(*ARRAY))

struct item
{
    const char * name;
    int value;
};

static _Bool sorted;

static struct item items[] =
{
    { "one", 1 },
    { "two", 2 },
    { "three", 3 },
    { "ten", 10 }
};

static int compare(const void * p1, const void * p2)
{
    return strcmp(*((const char **)p1), *((const char **)p2));
}

int get(const char * name)
{
    if(!sorted)
    {
        qsort(items, count(items), sizeof(*items), compare);
        sorted = 1;
    }

    struct item * item = bsearch(&name, items, count(items), sizeof(*items),
        compare);

    return item ? item->value : 0;
}

int main(int argc, char ** argv)
{
    int i;
    for(i = 1; i < argc; ++i)
        printf("%i\n", get(argv[i]));

    return 0;
}
3 голосов
/ 06 января 2009

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

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

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

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

Однако вы можете предоставить хеш-карту, если ваши данные соответствуют парам ключ -> значение. Вам понадобится подходящая хеш-функция.

Здесь приведен простой пример хеш-таблицы:

http://www.cl.cam.ac.uk/~cwc22/hashtable/

0 голосов
/ 30 марта 2017

Это старый поток, но я подумал, что он все еще может быть полезен для любого, кто ищет реализацию. Это не займет слишком много кода; Я сделал мой в ~ 100 строк без какой-либо дополнительной библиотеки, как предложил Хэнк Гэй. Я назвал его словарем, так как он параллелен (вроде) типу данных python. Вот код:

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

typedef struct hollow_list hollow_list;

struct hollow_list{
    unsigned int size;
    void *value;
    bool *written;
    hollow_list *children;
};

//Creates a hollow list and allocates all of the needed memory
hollow_list hollow_list_create(unsigned int size){
    hollow_list output;
    output = (hollow_list) {.size = size, .value = (void *) 0, .written = calloc(size, sizeof(bool)), .children = calloc(size, sizeof(hollow_list))};
    return output;
}

//Frees all memory of associated with a hollow list and its children
void hollow_list_free(hollow_list *l, bool free_values){
    int i;
    for(i = 0; i < l->size; i++){
        hollow_list_free(l->children + i, free_values);
    }
    if(free_values){
        free(l->value);
    }
    free(l);
}

//Reads from the hollow list and returns a pointer to the item's data
void *hollow_list_read(hollow_list *l, unsigned int index){
    if(index == 0){
        return l->value;
    }
    unsigned int bit_checker;
    bit_checker = 1<<(l->size - 1);
    int i;
    for(i = 0; i < l->size; i++){
        if(bit_checker & index){
            if(l->written[i] == true){
                return hollow_list_read(l->children + i, bit_checker ^ index);
            } else {
                return (void *) 0;
            }
        }
        bit_checker >>= 1;
    }
}

//Writes to the hollow list, allocating memory only as it needs
void hollow_list_write(hollow_list *l, unsigned int index, void *value){
    if(index == 0){
        l->value = value;
    } else {
        unsigned int bit_checker;
        bit_checker = 1<<(l->size - 1);
        int i;
        for(i = 0; i < l->size; i++){
            if(bit_checker & index){
                if(!l->written[i]){
                    l->children[i] = hollow_list_create(l->size - i - 1);
                    l->written[i] = true;
                }
                hollow_list_write(l->children + i, bit_checker ^ index, value);
                break;
            }
            bit_checker >>= 1;
        }
    }
}

typedef struct dictionary dictionary;

struct dictionary{
    void *value;
    hollow_list *child;
};

dictionary dictionary_create(){
    dictionary output;
    output.child = malloc(sizeof(hollow_list));
    *output.child = hollow_list_create(8);
    output.value = (void *) 0;
    return output;
}

void dictionary_write(dictionary *dict, char *index, unsigned int strlen, void *value){
    void *hollow_list_value;
    dictionary *new_dict;
    int i;
    for(i = 0; i < strlen; i++){
        hollow_list_value = hollow_list_read(dict->child, (int) index[i]);
        if(hollow_list_value == (void *) 0){
            new_dict = malloc(sizeof(dictionary));
            *new_dict = dictionary_create();
            hollow_list_write(dict->child, (int) index[i], new_dict);
            dict = new_dict;
        } else {
            dict = (dictionary *) hollow_list_value;
        }
    }
    dict->value = value;
}

void *dictionary_read(dictionary *dict, char *index, unsigned int strlen){
    void *hollow_list_value;
    dictionary *new_dict;
    int i;
    for(i = 0; i < strlen; i++){
        hollow_list_value = hollow_list_read(dict->child, (int) index[i]);
        if(hollow_list_value == (void *) 0){
            return hollow_list_value;
        } else {
            dict = (dictionary *) hollow_list_value;
        }
    }
    return dict->value;
}

int main(){
    char index0[] = "hello, this is a test";
    char index1[] = "hello, this is also a test";
    char index2[] = "hello world";
    char index3[] = "hi there!";
    char index4[] = "this is something";
    char index5[] = "hi there";

    int item0 = 0;
    int item1 = 1;
    int item2 = 2;
    int item3 = 3;
    int item4 = 4;

    dictionary d;
    d = dictionary_create();
    dictionary_write(&d, index0, 21, &item0);
    dictionary_write(&d, index1, 26, &item1);
    dictionary_write(&d, index2, 11, &item2);
    dictionary_write(&d, index3, 13, &item3);
    dictionary_write(&d, index4, 17, &item4);

    printf("%d\n", *((int *) dictionary_read(&d, index0, 21)));
    printf("%d\n", *((int *) dictionary_read(&d, index1, 26)));
    printf("%d\n", *((int *) dictionary_read(&d, index2, 11)));
    printf("%d\n", *((int *) dictionary_read(&d, index3, 13)));
    printf("%d\n", *((int *) dictionary_read(&d, index4, 17)));
    printf("%d\n", ((int) dictionary_read(&d, index5, 8)));
}

К сожалению, вы не можете повторить синтаксис list [x], но это лучшая альтернатива, которую я придумала.

0 голосов
/ 06 января 2009

Как уже указывалось, вам нужен ассоциативный массив или хэш-карта или эквивалент. Одним из возможных источников такого кода является Hanson " C Интерфейсы и реализации " (код на Google Code - дважды проверьте условия лицензирования и т. Д. Перед его использованием.)

0 голосов
/ 06 января 2009

В "обычном C" вы можете имитировать, используя строку в качестве индекса, но не QUITE, как вам кажется. Тем не менее, это редко бывает полезно и в основном является отличным способом сделать ваш код нечитаемым. Похоже, что вам нужно иметь возможность использовать строковые ключи в словаре (или «хэш-таблице», если вы предпочитаете), и в C. нет встроенной структуры данных для этого. Точный дизайн будет зависеть от того, что вы хотеть (и, действительно, если это часть домашней работы, вам, возможно, даже не понадобится использовать полноценную реализацию хеш-таблицы, но, возможно, вам не удастся избежать менее производительного статического кодирования).

Пример использования строки (ОК, массив символов) в «позиции индекса» конструкции a [b]:

int main (void)
{
  char *str = "This is a test string";
  int x;

  for (x=0; x < 12; x += 3)
    putchar(x[str]);

  printf("\n");

  return 0;
}

Вышесказанное, насколько я могу судить, является допустимым C, с четко определенным выводом (строка "Tss ssi"). Он основан на том факте, что a [b] определено так же, как * (a + b).

...