定义一个带有变量维度大小的2D数组作为参数的C函数。

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


你是要比较存储在数组中的字符串还是固定大小的字符数组?请至少展示一下你将要处理的数组。 - Vlad from Moscow
为什么不创建一个宏,返回你想要的结果,并将其命名为 STR_ARRAY(size, i, j) 等类似名称调用呢? - Jardel Lucca
1
请在调用现场展示代码。提到“间接级别不同”意味着您正在传递与char *不兼容的内容,因此代码已经出错。 - M.M
我传递的字符串数组示例是 char instructionStrings[NUM_INSTRUCTIONS][INST_MAX_CHARS] = { "nop", "lit", "litn", "copy", "copyl", 等。 - eejakobowski
@M.M 是正确的。当您将 typeStrings 作为第三个参数传入时,参数的类型是 char (*)[TYPE_MAX_CHARS],与您在实现中指定的 char * 不兼容。大多数人会使用 void * 作为函数参数类型,并在函数内部转换为 char * - jxh
是的,最终为了消除警告,我选择了 void*。我之前使用 char* 是为了尽量暗示所需的输入类型,但由于我使用的编译器无法准确显示参数应该是什么,因此只能使用 void* - eejakobowski
3个回答

4
如果编译器支持可变长度数组,则该函数可以按照下面示例程序所示的方式声明和定义。请注意,并非所有编译器都支持可变长度数组(尤其是 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.

findStringIndex 是用来查找行的。它返回字符串所在的行。每一行都是一个字符串。我假设这里没问题。 - Chef Gladiator

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

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

在函数头中放置char(*strArray)[strLength]的部分,我遇到了编译器“预期常量表达式”的错误。特别是,Visual Studio在strLength部分下划了红线。 - eejakobowski
2
微软不符合标准的编译器。你很遗憾。他们仍然没有实现25年前引入的功能 :) - 0___________
@eejakobowski 下载 Eclipse CDT 并享受现代的 GCC 编译器。 - 0___________
1
VLA自C.2011以来一直是可选功能。 - jxh
@eejakobowski,clang 11将与Visual Studio 2019一起推出。它被称为clang-cl。在通过vcvars64.bat或其他VS快捷方式启动后,它将位于路径上。cl.exe不支持VLA或VMT,而且可能永远不会支持。使用VS IDE使用clang甚至更容易。如果您需要进一步帮助,请提出要求。 - Chef Gladiator
显示剩余4条评论

1

其他答案已经涵盖了如何使用变长数组(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。自该功能被引入以来,ClangGCC已经支持它,并且最近的MSVC版本也支持它。显然,如果您没有使用至少Visual Studio 2019版本16.8预览版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一样,要调用的各个函数是分别实现的。

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