字符串可以用作数组索引吗?

8

在C语言中,可以使用字符串作为数组的索引吗?

例如: 字符串 对应的值 "ONE" 1 "TWO" 2 "FIVE" 5 "TEN" 10

当传递上述列表中的字符串到函数中时,该函数必须返回上面指定的相应值。是否可以通过声明一个带有字符串索引的常量数组来实现这一点?

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

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

上述逻辑不像预期的那样起作用;是否有解决方法来实现具有字符串索引的数组?

1
我怀疑这是一项家庭作业:编写一个函数,该函数接受一个包含1到10范围内任何数字的文字表示的字符串,并返回相应的整数,使用数组,对吧? - Vlad Gudim
8个回答

19

可能会编译通过,但不会起作用。

不太清楚您想要实现什么。我认为您想要一个关联数组,这种情况下,您应该找到一个库来实现它。

如果您正在寻找类似于枚举类型的东西,并且可以依赖于C89,那么可以看一下以下代码:

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

如果您不能依赖C89,则应尝试一些typedef技巧。


10

有其他很好的答案可以告诉你该做什么,所以我想解释一下你正在做什么以及为什么它会编译而不起作用。

在C语言中,数组引用是通过拥有一个数组或指针和某种整数来完成的(例如以x[1]为例,x是数组,1是整数)。只要使用某种整数类型,它就会按照你的预期工作。

假设你有一些不是整数的东西。在这种情况下,C实现将查看是否可以将其转换为适当的类型,因此您最终将得到数组和整数。正是这些情况导致了问题(在C ++的稍微复杂的版本中,比您更有经验的人也可能被困扰)。

在C语言中,像"one"这样的文本字符串类型是const char*,意思是指向无法更改的字符的指针。实际值是字符串实际驻留在内存中的位置的内存地址。通常,您不会关注此指针值,并查看字符串值,但是这里存在一个陷阱。

在C语言中,任何数据指针都可以转换为某种整数,并且将自动进行转换。因此,您有一个类似于"one"的字符串,它的值是代表内存地址的数字。将其用于C期望某种整数的地方,它会被转换为某种整数值。

因此,这就是x["ONE"]正在发生的事情。C系统必须在内存中的某处放置字符串"ONE",位置并不重要。它很可能在一个具有相当大的内存地址的地方,很可能在十亿级别。当它看到x ["ONE"]时,它会尝试将该值转换为整数,并将其用作下标。因此,您正在尝试访问远远超出其范围的数组x,导致了问题。要么你正在尝试使用你不允许使用的内存,系统会阻止你,要么你正在操作应该不该碰的一块内存,它可能会在以后以某种神秘的方式失败。


1
如果Kanakagiri真的尝试编译这段代码,打开编译器警告功能至少会提示编译器对代码的理解与程序员不同。编译器是如何理解代码的呢? - hillu

5

您可以使用 stdlib.h 提供的函数 bsearch() 轻松构建查找表。以下是一个可行的示例:

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

如果您输入的值不在列表中,将会出现核心转储错误 :( - Jonathan Leffler
在winXP上,使用mingw-gcc 3.4.5和当前版本的tinyCC都可以正常工作 - 您使用的是哪个系统/编译器? - Christoph
有其他人尝试过这个并得到了核心转储吗?如果我不知道它是否损坏,就无法修复它... - Christoph

3
您需要编写一个将字符串映射到整数的函数,或者在整个过程中使用枚举(然后可能需要编写一个将枚举值映射到字符串的函数)。
一般来说,最好采用后者:传递整数,这样实现就不依赖于可能在表示中使用的字符串的细节。例如,考虑如果您需要使那些字符串对讲其他语言的人更易懂时,如何管理本地化(翻译)问题。

1
你要找的可能是关联数组的等价物,不幸的是在C语言中不能以同样的语法糖提供它,否则会得到一些愚蠢的结果。
然而,如果你的数据符合键值对的格式,你可以提供一个哈希表。你需要做的是选择一个适当的哈希函数。
这里有一个不错的简单哈希表示例:

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


0
这是一个旧的主题,但我认为对于任何正在寻找实现的人仍然可能有用。它不需要太多的代码;我在没有像Hank Gay建议的任何额外库的情况下完成了大约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
正如先前所述,您需要一个关联数组、哈希映射或等价物。这样的代码可能来自 Hanson 的 "C 接口与实现"(在Google Code上可以找到代码 - 在使用之前一定要仔细检查许可证条款等)。

-2
在“普通C”中,您可以模仿使用字符串作为索引,但不是您想要的方式。然而,这样做很少有用,大多数情况下会使您的代码难以阅读。您似乎想要能够在字典(或者如果您喜欢,哈希表)中使用字符串键,但在C语言中没有内置的数据结构来实现这一点。具体的设计取决于您的需求(如果这是作业的一部分,您可能甚至不需要使用完整的哈希表实现,而是可以使用性能较差的静态编码)。
以下是在a[b]结构的“索引位置”中使用字符串(好吧,是char数组)的示例:
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)。

这怎么帮助Kanakagiri解决问题呢?如果一个是指针,另一个是整数,那么x[str]str[x]是相同的可能很有趣,但与问题无关... - Christoph
实际上,这与问题有很大关系(具体来说,是的,您可以使用字符串作为索引,但不是原始查询者想要的明显方式)。 - Vatine

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接