定义一个 C 函数,该函数采用带有维度大小变量的二维数组 解决方法 1:使所有字符串大小相同解决方法 2:使用 _Generic解决方法 3:

问题描述

编辑:结果我使用的编译器不支持可变长度数组,所以我无法使用 MSVC 实现我想要的符号


我有一个函数,它接受一个字符串数组和一个查询字符串,并返回数组中与查询匹配的字符串的索引。

int findStringIndex(char query[],int strLength,char* strArray,int numStrings) {
    for (int i = 0; i < numStrings; i++) {
        for (int j = 0; j < strLength; j++) {

            // Skip to next word if there is a mismatch
            if (query[j] != *(strArray+ (i * strLength) + j))
                break;

            if (query[j] == '\0' && *(strArray + (i * strLength) + j) == '\0')
                return i;
        }
    }
    return -1;
}

值得注意的是,字符串的长度和数组的大小都不同,因为我在几个不同的地方使用了这个函数,并且字符串的大小不同。目前,这种方法有两个问题:

  • 丑陋的数组访问符号 *(strArray+ (i * strLength) + j)) 而不是像 strArray[i][j] 之类的东西
  • 当我调用函数并将字符串数组作为第三个参数传递时,我收到警告,即我传递的参数从 char* 中“在间接级别上有所不同”

有没有办法告诉编译器接受一个变量作为数组轴之一的大小,以便我可以使用符号 strArray[i][j]

另外,我应该如何定义函数,以免收到“间接级别”警告?

编辑:作为澄清,字符串数组没有参差不齐。它们具有恒定大小的维度,但我想在其上使用该函数的不同数组具有不同的大小。代码运行良好并在当前状态下实现了所需的行为,我只是想确保我以正确的方式编写内容

以下是我可能与此函数一起使用的数组的两个示例(不同的字符串大小):

char instructionStrings[NUM_INSTRUCTIONS][INST_MAX_CHARS] = {
    "nop","lit","litn","copy","copyl","asni",/* etc */
};

char typestrings[NUM_TYPES][TYPE_MAX_CHARS] = {
    "null","int8","int16","int32","int","real32","real"
};

其中 INST_MAX_CHARS 和 TYPE_MAX_CHARS 是不同的值。然后我会为第二个例子调用 findStringIndex(userInput,TYPE_MAX_CHARS,typestrings,NUM_TYPES); 之类的函数

解决方法

如果您的编译器支持可变长度数组,则可以按以下方式声明和定义该函数,如下面的演示程序所示。

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

size_t findStringIndex( size_t m,size_t n,char a[m][n],const char *s ) 
{
    size_t i = 0;

    while ( i < m && !( strcmp( a[i],s ) == 0 ) ) ++i;
    
    return i;
}

int main(void) 
{
    enum { M1 = 3,N1 = 10 };
    
    char a1[M1][N1] =
    {
        "Hello","World","Everybody"
    };
    
    const char *s = "Hello";
    
    size_t pos = findStringIndex( M1,N1,a1,s );
    
    if ( pos != M1 )
    {
        printf( "\"%s\" is found at position %zu.\n",s,pos );
    }
    else
    {
        printf( "\"%s\" is not found.\n",s );
    }
    
    s = "World";
    
    pos = findStringIndex( M1,s );
    }
    
    s = "Everybody";
    
    pos = findStringIndex( M1,s );
    }
    
    s = "Bye";
    
    pos = findStringIndex( M1,s );
    }
    
    return 0;
}

程序输出为

"Hello" is found at position 0.
"World" is found at position 1.
"Everybody" is found at position 2.
"Bye" is not found.
,
  1. 使用正确的尺寸类型:size_t

  2. 您可以通过使用指向数组的指针来使用“普通”索引。

int findStringIndex(char query[],size_t strLength,char (*strArray)[strLength],size_t numStrings) {
    for (size_t i = 0; i < numStrings; i++) {
        for (size_t j = 0; j < strLength; j++) {

            // Skip to next word if there is a mismatch
            if (query[j] != strArray[i][j])
                break;
    /* ..... */

我假设您传递的是二维字符数组(不是指针数组)

,

其他答案涵盖了如何使用可变长度数组 (VLA) 获得所需的数组访问语法。

如果您在不支持 VLA 的系统上工作,您可能需要继续使用与您已经展示的接近的实现。

但是,有一些解决方法。

解决方法 1:使所有字符串大小相同

如果您在通常较小的字符串上使用此函数。 ,然后不是允许 INST_MAX_CHARSTYPE_MAX_CHARS 是不同的值,而是规定传递给此函数的所有数组必须具有相同的第二维值。因此,在实践中,它将是指令字符串和类型字符串上所有字符串镜头的最大值。 (您可能需要实现自己的 MAX 宏。)

#define X_MAX_CHARS MAX(INST_MAX_CHARS,TYPE_MAX_CHARS)

char instructionStrings[NUM_INSTRUCTIONS][X_MAX_CHARS] = {
    "nop","lit","litn","copy","copyl","asni",/* etc */
};

char typeStrings[NUM_TYPES][X_MAX_CHARS] = {
    "null","int8","int16","int32","int","real32","real"
};

然后,您的函数可能如下所示:

int findStringIndex(char query[],char (* strArray)[X_MAX_CHARS],int numStrings) {
    for (int i = 0; i < numStrings; i++) {
        if (strcmp(query,strArray[i]) == 0) return i;
    }
    return -1;
}

解决方法 2:使用 _Generic

从 C 2011 开始,C 定义了一个类型选择机制调用 _GenericClangGCC 自引入该功能以来就支持它,最近版本的 MSVC 也是如此。显然,如果您使用的不是至少 Visual Studio 2019 version 16.8 Preview 3,那么您将不会拥有此功能。

使用 _Generic,您可以检测第二个维度的大小,并调用一个不同的函数来使用它。

#define findStringIndex(Q,A,N) \
        _Generic((A),\
            const char (*)[INST_MAX_CHARS] : findStringIndex_I(Q,N),\
            const char (*)[TYPE_MAX_CHARS] : findStringIndex_T(Q,\
            default                        : -1)

其中,findStringIndex_IfindStringIndex_T 分别被定义为处理它们知道如何支持的字符串数组。您可以创建一个宏来自动创建函数,以防您需要添加许多此类函数。

#define DEFINE_FIND_STRING_INDEX(SUFFIX,STRING_SZ) \
        int findStringIndex_ ## SUFFIX ( \
                char query[],\
                char (* strArray)[STRING_SZ],\
                int numStrings) { \
            for (int i = 0; i < numStrings; i++) { \
                if (strcmp(query,strArray[i]) == 0) return i; \
            } \
            return -1; \
        }

DEFINE_FIND_STRING_INDEX(I,INST_MAX_CHARS)
DEFINE_FIND_STRING_INDEX(T,TYPE_MAX_CHARS)

解决方法 3:

虽然不像 _Generic 那样通用,但由于您只处理字符串的大小,因此您可以使用条件表达式完成同样的事情。通过选择所提供数组的第一个元素的大小,这实际上为您提供了第二个维度的大小,您可以确定要调用的适当函数。

#define findStringIndex(Q,N) \
        ((sizeof((A)[0]) == INST_MAX_CHARS) ? findStringIndex_I(Q,N) : \
        ((sizeof((A)[0]) == TYPE_MAX_CHARS) ? findStringIndex_T(Q,N) : \
        -1))

_Generic 一样,要调用的各个函数是单独实现的。