定义一个 C 函数,该函数采用带有维度大小变量的二维数组

Defining a C function that takes a 2D array with variables for dimension sizes

编辑:原来我使用的编译器不支持可变长度数组,所以我无法使用 MSVC

实现我想要的符号

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

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] == '[=10=]' && *(strArray + (i * strLength) + j) == '[=10=]')
                return i;
        }
    }
    return -1;
}

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

有没有办法让我告诉编译器接受一个变量作为数组轴之一的大小,以便我可以使用符号 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); 这样的函数

  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;
    /* ..... */

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

如果您的编译器支持可变长度数组,则可以按以下方式声明和定义函数,如下面的演示程序所示。请注意,并非所有编译器都支持可变长度数组(尤其是 MSVC),在这种情况下无法获得所需的符号。

#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, 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 = "Everybody";
    
    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 = "Bye";
    
    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 );
    }
    
    return 0;
}

程序输出为

"Hello" is found at position 0.
"World" is found at position 1.
"Everybody" is found at position 2.
"Bye" is not found.

其他答案涵盖了如何使用可变长度数组 (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就定义了类型选择机制调用_Generic. Clang and GCC have supported it since the feature was introduced, and so do recent versions of 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, A, N), \
            const char (*)[TYPE_MAX_CHARS] : findStringIndex_T(Q, A, N), \
            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, A, N) \
        ((sizeof((A)[0]) == INST_MAX_CHARS) ? findStringIndex_I(Q, A, N) : \
        ((sizeof((A)[0]) == TYPE_MAX_CHARS) ? findStringIndex_T(Q, A, N) : \
        -1))

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