Can strings be used as an array index?

拈花ヽ惹草 提交于 2020-01-20 04:15:05

问题


Can a string be used as array index in C?

Ex: String Corresponding value "ONE" 1 "TWO" 2 "FIVE" 5 "TEN" 10

When a string in the above list is passed to the function, the function must return the corresponding value indicated above. Can this be achieved by declaring a constant array with string as index

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

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

The above logic does not work as expected; is there a workaround to implement the above logic in order to have a string-indexed array?


回答1:


It might compile, but it won't work.

It's not entirely clear what you're trying to achieve. I think you want an associative array, in which case you should find a library implementation of one.

If you're looking for something more like an enumerated type, and you can rely on C89, look at something like:

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

If you can't rely on C89, then you should try some typedef trickery.




回答2:


There are other excellent answers to what you should do, so I thought I'd explain what you are doing and why it's compiling and not working.

In C, array reference is done by having an array or pointer and an integer of some sort. (in x[1], x is the array and 1 is the integer). As long as you're using some integral type, it'll work as you expect.

Suppose you have something that's not an integer. In that case, the C implementation will see if it can convert it to the appropriate type, so you wind up with array and integer. It's cases like this where you get into trouble (and slightly more sophisticated versions of this in C++ have confused more experienced people than you).

In C, a literal string like "one" is of type const char *, meaning pointer to characters you can't change. The actual value is the memory address of where the string actually resides in memory. Normally, you'd pay no attention to this pointer value, and look at the string value, but there's a gotcha here.

In C, any data pointer can be converted to some sort of integer, and will be automatically. Therefore, you've got a string like "one", and its value is whatever number that represents the memory address. Use it where C expects some sort of integer, and it'll get converted to some integral value or other.

Therefore, this is what's happening with x["ONE"]. The C system has to put the string "ONE" somewhere in memory, and it doesn't matter where. It's likely to be somewhere with a fairly large memory address, quite possibly in the billions. When it sees x["ONE"], it tries to convert that value to an integer, and uses it as a subscript. Therefore, you're trying to access the array x far, far beyond its bounds, and that's causing the problem. Either you're trying to use memory you're not allowed to, and the system just stops you, or you're mucking with a chunk of memory you should be leaving alone, and it's likely to fail in some mysterious way later.




回答3:


You will need to write a function that maps strings to integers, or alternatively use enumerations throughout (and then perhaps a function that maps enumerated values to strings).

In general, it's nicer to do the latter: to pass integers, so that the implementation isn't dependent on the details of strings that might be used in the representation. For example, think about how you would manage localization (translation) if ever you need to make those strings palatable to somebody speaking a different language.




回答4:


You can easily build lookup tables with the function bsearch() provided by stdlib.h. A working example is this:

#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;
}



回答5:


What you are looking for is probably the equivalent of an associative array which can't be provided with the same syntactic sugar in C unfortunately without some silly results.

However, what you can provide is a hashmap if your data conforms to key -> value pairs. What you will need is an appropiate hash function.

There's a decent simple example of a hashtable here:

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




回答6:


As already indicated, you need an associative array or hash map or equivalent. One possible source for such code is Hanson's "C Interfaces and Implementations" (code at Google Code - double check licencing terms etc before using it.)




回答7:


This is an old thread, but I thought this might still be useful for anyone out there looking for an implementation. It doesn't take too much code; I did mine in ~100 lines of without any extra library like Hank Gay suggested. I called it a dictionary since it parallels (sort of) the python datatype. Here is the code:

#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)));
}

Unfortunately you can't replicate the list[x] syntax, but this is the best alternative I have come up with.




回答8:


In "plain C" you can mimic using a string as an index, but not QUITE in the way you seem to be wanting. However, doing so is seldom useful and mostly an excellent way of making your code unreadable. What you seem to be wanting is to be able to use string keys into a dictionary (or "hash table", if you prefer) and there are no built-in data structure for that in C. The exact design would depend on what you want (and, indeed, if this is part of homework, you may not even need to use a full-fledged hash-table implementation but could probably get away with less performant static coding).

An example using a string (OK, a char array) in the "index position) of an a[b] construct:

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;
}

The above is, as far as I can tell, legal C, with a well-defined output (the string "Tss ssi"). It relies on the fact that a[b] is defined to be the same as *(a+b).



来源:https://stackoverflow.com/questions/416237/can-strings-be-used-as-an-array-index

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!