C 中的指针数组,易于迭代

如何解决C 中的指针数组,易于迭代

最近我在思考这个问题:如何在 C 中更简单地迭代指针数组。

如果我在 C 中创建一个字符串数组,它应该看起来像这样吗?

int size = 5;
char ** strArr = (char **) malloc(sizeof(char *) * size);
if (strArr == NULL) return;

但问题是,当你出于某种原因想要遍历这个数组时(比如打印其中的所有值),你必须跟踪它的当前大小,存储在另一个变量中。

这不是问题,但是如果您创建了大量数组,则必须在代码中跟踪每个数组的大小。如果你把这个数组传递给另一个函数,你也必须传递它的大小。

void PrintValues (char ** arr,int size) {
    for (int i = 0; i < size; i++)
        printf("%s\n",arr[i]);
}

但是当迭代一个字符串时,情况就不同了。您有 '\0' 字符,它指定字符串的结尾。因此,您可以像这样遍历字符串,而无需保留其大小值:

char * str = (char *) malloc(sizeof(char) * 4);
str[0] = 'a';
str[1] = 'b';
str[2] = 'c';
str[3] = '\0';

for (int i = 0; str[i] != '\0'; i++)
    printf("%c",str[i]);
printf("\n");

现在我的问题: 在指针数组中分配 +1 个单元以保持其尾部为 NULL 是否可以或在道德上是正确的?

char ** strArr = (char **) malloc(sizeof(char *) * (5   +1);
if (strArr == NULL) return;
strArr[0] = PseudoFunc_NewString("Car");
strArr[1] = PseudoFunc_NewString("Car#1");
strArr[2] = PseudoFunc_NewString("Car#2");
strArr[3] = PseudoFunc_NewString("Tree");
strArr[4] = PseudoFunc_NewString("Tree#1");
strArr[5] = NULL; // Stop iteration here as next element is not allocated

然后我可以使用 NULL 指针来控制迭代器:

void PrintValues (char ** arr) {
    for (int i = 0; arr[i] != NULL; i++)
        printf("%s\n",arr[i]);
}

这将帮助我保持代码更简洁,尽管它会消耗更多内存,因为指针大小大于整数大小。

此外,当使用基于事件的库(如 Gtk)进行编程时,大小值会在某个时候从堆栈中释放出来,因此我必须创建一个指针来动态存储大小值。

在这种情况下,可以这样做吗?还是被认为是坏事?

这种技术是否仅用于 char 指针,因为 char 类型的大小只有 1 个字节?

我怀念在 C 中使用 foreach 迭代器...

解决方法

现在我的问题是:在指针数组中分配 +1 个单元以保持其尾部为 NULL 是否可以或在道德上是正确的?

这没问题,最后的 NULL 称为 哨兵值,使用它是有点普遍的做法。当您出于某种原因甚至不知道数据的大小时,最常使用此方法。

然而,这不是最好的解决方案,因为您必须遍历所有数据才能找到大小。单独存储大小的解决方案要快得多。例如,在同一位置包含大小和数据的结构数组。

,

现在我的问题是:在指针数组中分配 +1 个单元以保持其尾部为 NULL 是否可以或在道德上是正确的?

在 C 中,这是一个很常见的模式,它有一个名字。您只是在使用 sentinel value

只要您的列表通常不能包含空指针,就可以了。总的来说,它有点容易出错,但话说回来,这对你来说是 C。

,

没关系,是常用的模式。

作为替代,您可以使用 struct,在那里您可以创建一个 size 变量,您可以在其中存储数组的当前大小,并将结构作为参数传递。优点是你不需要遍历整个数组来知道它的大小。

示例:

Live demo

#include <stdlib.h>
#include <stdio.h>

typedef struct
{
    char **strArr;
    int size;
} MyStruct;

void PrintValues(MyStruct arr) //pass the struct as an argument
{
    for (int i = 0; i < arr.size; i++) //use the size passed in the struct
        printf("%s\n",arr.strArr[i]);
}

int main()
{
    // using the variable to extract the size,to avoid silent errors 
    // also removed the cast for the same reason
    char **strArr = malloc(sizeof *strArr * 5); 

    if (strArr == NULL) return EXIT_FAILURE;

    strArr[0] = "Car";
    strArr[1] = "Car#1";
    strArr[2] = "Car#2";
    strArr[3] = "Tree";
    strArr[4] = "Tree#1";
    
    MyStruct strt = { strArr,5 }; // initialize the struct
    PrintValues(strt); //voila
    free(strArr); // don't forget to free the allacated memory
    return EXIT_SUCCESS;
}  

这允许通过错误检查直接访问索引:

// here if the array index exists,it will be printed
// otherwise no,allows for O(1) access error free
if(arr.size > 6){
    printf("%s\n",arr.strArr[6]);
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?