迭代动态分配的char数组

时间:2018-02-03 20:10:39

标签: c arrays string pointers

我是C的新手,请耐心等待。我正在尝试编写一个函数,如果找到该字符串则返回该数组的索引,否则将存储该字符串,然后返回索引。我想创建一个像{"1234", "3241", "2234", "2222"}这样的结构,我已经动态分配了这样的char数组(我需要从命令行获取大小)

char** userToRdrMap = {NULL};
userToRdrMap = malloc(numOfReducers * sizeof(char*));

现在,我有一个userId&#39>的数组

char *userId[numOfReducers];
    userId[0] = "2234";
    userId[1] = "2222";
    userId[2] = "3223";
    userId[3] = "2222";

对于每个userId,我将调用一个函数findRdrNum(userId[i]),它将检查先前动态分配的char数组userToRdrMap,如果userId存在则应返回索引,否则存储它然后返回索引。 我无法解决这个问题,因为我感到困惑并迷失了指针。有人可以帮忙吗? 这是我的程序,它尝试返回索引然后存储它。

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

int numOfReducers;
char** userToRdrMap = {NULL};
char* temp1;

int findRdrNum(char *userId){
    if(!userToRdrMap[0]){
        return 0;
    } else{
        int i;
        printf("%s", userToRdrMap[1]);
        for(i=0; i<numOfReducers; i++){
            if(strcmp(userToRdrMap[i], userId))
                return i;
        }
    }
    return NULL;
}

int main (int argc, char *argv[])
{
    numOfReducers = atoi(argv[1]);
    int i;
    char *userId[numOfReducers];
    userId[0] = "2234";
    userId[1] = "2222";
    userId[2] = "3223";
    userId[3] = "2222";
    int rdrNum;

    userToRdrMap = malloc(numOfReducers * sizeof(char*));
/*  temp1 = malloc(numOfReducers * 4 * sizeof(char));
    for (i = 0; i < numOfReducers; i++) {
      userToRdrMap[i] = temp1 + (i * 4);
    }
*/
    printf("%s\n", userId[0]);
    for(i=0; i<numOfReducers; i++){
        rdrNum = findRdrNum(userId[i]);
        strcpy(userToRdrMap[rdrNum], userId[i]);
        printf("%d", rdrNum);
    }
    return 0;
}

谢谢, Harrish

4 个答案:

答案 0 :(得分:0)

所以基本上你想要的是这样的东西?

编辑:修复了示例代码中的小错误

int findRdrNum(char *userId) {
    for (int pos = 0; pos < numOfReducers; pos++) 
    // check whether position hasn't been allocated yet
    if (userToRdrMap[pos] != NULL) 
        //compare string and return pos if equal
        if (strcmp(userId, userToRdrMap[pos]) == 0)
            return pos;
}

此外,您可以在以下函数中分配矢量:

int allocateUserToRdrMap(){
    userToRdrMap = malloc(numOfReducers * sizeof(char));
    if (userToRdrMap == NULL)
        return 0;
    return 1;
}

答案 1 :(得分:0)

如果您尝试使函数findRdrNum返回与userToRdrMap匹配的userId元素的索引,请考虑strcmp如果参数为0则返回0等于;因此,您的函数将返回与userId不相等的第一个元素的索引。检查strcmp(userToRdrMap[i], userId) == 0是否有用。此外,此函数的类型为int,因此没有return NULL的位置。另一件事是建议将malloc返回的结果推荐给你的类型(malloc返回*void

答案 2 :(得分:0)

问题:

你的问题有点令人困惑,但我可以看到你得到了什么,很明显你确实花了一些力气写出一篇不错的帖子。因此,我试图编写一个程序来完成你想要的,并且很容易理解。

<强>程序:

以下程序会自动安装您在示例中提供的相同的示例字符串。然后它读取main的参数向量(char *argc[])中提供的每个参数,以检查它是否在那里。如果不是,则安装。正如您将在示例输出中看到的那样,所有内容都通过print语句进行演示。

输出示例:

./a.out 2222 3223 kjd 090 kjd
The User ID "2222" exists at index 1!
The User ID "3223" exists at index 2!
Installed "kjd"!
Installed "090"!
The User ID "kjd" exists at index 4!

这是程序代码。我试着评论一下你会明白我做了什么。在底部,我将包含一些关于您原始程序的评论,以及您可以采取哪些措施来尝试改进它。

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

// An array of strings: (A pointer to an array of pointers).
// Declaring a variable out in global scope is always initialized to NULL!
char **strings;

// 1: The index of the next free pointer. 2: The total number of pointers available.
int str_idx, str_size;

// Returns index of given string pointer `sp`. If it's not there, -1 is returned.
int indexOf (char *sp) {

    for (int i = 0; i < str_idx; i++) {
        // If string at index i == string we're looking for.
        if (strcmp(sp, strings[i]) == 0) {
            return i;
        }
    }

    return -1;
}

// Copies given string into strings. If necessary, strings is resized.
void installString (char *sp) {
    char *copy = NULL;

    // 1. Ensure we have space. Otherwise resize strings array.
    if (str_idx >= str_size) {
        str_size = str_size * 2;
        strings = realloc(strings, str_size * sizeof(char *));
    }

    // 2. Allocate space for copy of string. Print an error if it failed.
    copy = malloc(strlen(sp) * sizeof(char));
    if (copy == NULL) {
        printf("Error: Allocation failure in \"installString\"!\n");
        exit(EXIT_FAILURE);
    }

    // 3. Copy the contents over.
    copy = strcpy(copy, sp);

    // 4. Place the pointer (copy) at str_idx (next available spot), and post-increment str_idx.
    strings[str_idx++] = copy;
}

int main (int argc, char *argv[]) {

    // 1. Initialize our global strings array. Ensure it succeeded.
    str_size = 10;
    strings = malloc(str_size * sizeof(char *));
    if (strings == NULL) {
        printf("Error: Allocation failure in \"main\"!\n");
        exit(EXIT_FAILURE);
    }

    // 2. Install some example User IDs.
    installString("2234");
    installString("2222");
    installString("3223");
    installString("2222");

    // 3. Check the User IDs provided as program arguments.
    // (I subtract from argc first because argument zero is the name of the program)
    while (--argc) {
        char *id = *(++argv); // Increment argv pointer (to skip program name), dereference at next argument.

        // 4. If the id is installed, print the index. Otherwise install it.
        int index;
        index = indexOf(id);
        if (index > -1) {
            printf("The User ID \"%s\" exists at index %d!\n", id, index);
        } else {
            installString(id);
            printf("Installed \"%s\"!\n", id);
        }
    }

    // 5. Clean up allocated memory.
    for (int i = 0; i < str_idx; i++) {
        free(strings[i]);
    }
    free(strings);

    return EXIT_SUCCESS;
}

<强>提示:

  1. 声明:如果您将char** userToRdrMap = {NULL};保留在全局范围内,则char **userToRdrMap;更好地写为findRdrNum。原因是它会在那里自动初始化为NULL。如果它是本地或自动变量,则不是这样!

  2. 不要将零作为坏索引返回。零是一个有效的索引,-1更有意义,因为没有任何东西可以真正在索引-1。

  3. 您的函数int表示它返回NULL。但最后你会返回REGEXP_REPLACE,它通常用于指针。这是不一致的。不要这样做。

  4. 你没有释放你在程序中分配的任何内存。你真的应该这样做。

  5. 您在程序中分配了一个字符串指针的数组。但是,实际上并没有为要复制到指针的字符串分配任何空间。这是不明确的行为,是不可接受的。如果要将任何内容复制到您分配的指针数组,则需要为要复制的字符串实际分配内存,然后将strings数组中指针的值设置为已分配副本的值。我在我的例子中这样做。

答案 3 :(得分:0)

您的问题有点不清楚您的最终目标是什么,但看起来您希望从stdin(或文件)中读取用户ID,并将用户ID添加到动态扩展的指针数组中。执行此操作时,您似乎希望确保仅添加新ID并跳过所有重复ID。

虽然使用char和char指针数组没有任何问题(技术上是指向指针指向char 的指针),如果你的所有ID都是固定数量的字符,那么你通过使用指向数组的指针(例如char (*users)[NCHR])作为存储的基础,可以大大简化内存分配。 (可以在一次调用中管理和释放存储,而不是分别为ID分配指针和存储)但是,因为您希望char **作为存储方案的基础(并且您通常会这样做)无论如何使用),这将被解决。

当想要以动态方式存储任何东西时,使用指针指针是这样做的正常基础。该计划很简单。最初分配一些合理预期的指针数,然后分配存储来保存每个指针所指向的内容,当你用完初始指针数时,只需要realloc指针的数量并继续。 (你可以在每次重新分配时添加你喜欢的任意数量的指针,通过加倍数字是一个相当常见的方案)

将所有这些放在一起,让我们从两个简单的功能开始。一个是在用户集合中找到用户索引(如果存在)并返回索引(或者如果ID不存在则说-1)。你可以这样做:

/* simple function to check if user exists & return index */
int finduserndx (char **users, char *u, int n)
{
    int i;

    for (i = 0; i < n; i++)             /* loop over each index */
        if (strcmp (users[i], u) == 0)  /* compare IDs as strings */
            return i;                   /* return index on match */

    return -1;  /* return -1 indicating no-match */
}

您只需传递您的用户集合的副本,您要添加的新用户以及存在的数字,然后针对新用户循环遍历每个调用strcmp并返回已经存在的索引存在或-1表示它不存在于列表中。 (注意:如果您的用户ID都是数字,则存储十进制值的效率要高得多)该函数将在您的adduser函数中使用。

要添加新用户,请致电finduserndx,如果该用户已存在,则表示您已完成。如果它不存在,则需要首先检查是否已用完所有已分配的指针,如果是,则realloc更多(始终将realloc的返回值分配给临时指针),以及然后为新用户分配存储空间并将新用户复制到分配给列表中下一个可用指针的新内存块。你可以这样做:

/* simple function to add user 'u' to 'users' with '*n' existing users,
 * and '*nptrs' allocated pointers in 'users'. reallocate if
 * '*n + 1 == *nptrs' and allocate for new `users[n]`, increment
 * '*n' and '*nptrs' accordingly. returns beginning address to `users`
 * which must be assigned in caller to prevent becomming a 3-star
 * programmer (not a compliment)
 */
char **adduser (char **users, char *u, int *n, int *nptrs)
{
    if (finduserndx (users, u, *n) != -1) { /* if users exists, return */
        printf ("user: %s ==> duplicate\n", u); /* informational only */
        return users;
    }
    printf ("user: %s --- adding\n", u);    /* informational only */

    if (*n + 1 == *nptrs) { /* check if num pointers exhausted */
        /* realloc using temporary pointer */
        void *tmp = realloc (users, *nptrs * 2 * sizeof *users);
        if (!tmp) { /* validate realloc success/handle failure */
            perror ("realloc - memory exhausted");
            exit (EXIT_FAILURE);
        }
        users = tmp;    /* assign new block to users */
        *nptrs *= 2;    /* update number of allocated pointers */
    }

    users[*n] = malloc (strlen (u) + 1);    /* allocate for new user */
    if (!users[*n]) {   /* vallidate malloc/handle failure */
        perror ("malloc users[n] - memory exhausted");
        exit (EXIT_FAILURE);
    }
    strcpy (users[(*n)++], u);  /* copy new user to users[n] & increment */

    return users;   /* return pointer to start of users */
}

注意:如果realloc失败,您的现有数据不会丢失并且仍然存在,并且可通过原始指针使用 - 因此您处理realloc失败不需要立即exit - 但这会增加一些复杂性,最好留待以后使用)

另请注意:当前用户ID的数量('n')和当前分配的指针数('nptrs')如何作为指针传递< / strong>这样当它们的值在函数中更新时 - 新值可以在调用函数中获得)

这是您存储方案的基本要点。现在你需要在调用者(main()这里)做的就是读取每个用户ID,获取长度,修剪尾随'\n'(所以strcmp工作 - 所以你不要没有假的'\n'悬挂在你的字符串的末尾),然后调用adduser传递字符串作为参数以及指向当前数字的指针,如果用户ID和指向当前数字的指针已分配的指针。

完全放弃,您可以执行以下操作:

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

#define NREDUCR 4   /* if you need constants, define them */
#define MAXC  128   /* (don't use magic numbers in code) */

/* simple function to check if user exists & return index */
int finduserndx (char **users, char *u, int n)
{
    int i;

    for (i = 0; i < n; i++)             /* loop over each index */
        if (strcmp (users[i], u) == 0)  /* compare IDs as strings */
            return i;                   /* return index on match */

    return -1;  /* return -1 indicating no-match */
}

/* simple function to add user 'u' to 'users' with '*n' existing users,
 * and '*nptrs' allocated pointers in 'users'. reallocate if
 * '*n + 1 == *nptrs' and allocate for new `users[n]`, increment
 * '*n' and '*nptrs' accordingly. returns beginning address to `users`
 * which must be assigned in caller to prevent becomming a 3-star
 * programmer (not a compliment)
 */
char **adduser (char **users, char *u, int *n, int *nptrs)
{
    if (finduserndx (users, u, *n) != -1) { /* if users exists, return */
        printf ("user: %s ==> duplicate\n", u); /* informational only */
        return users;
    }
    printf ("user: %s --- adding\n", u);    /* informational only */

    if (*n + 1 == *nptrs) { /* check if num pointers exhausted */
        /* realloc using temporary pointer */
        void *tmp = realloc (users, *nptrs * 2 * sizeof *users);
        if (!tmp) { /* validate realloc success/handle failure */
            perror ("realloc - memory exhausted");
            exit (EXIT_FAILURE);
        }
        users = tmp;    /* assign new block to users */
        *nptrs *= 2;    /* update number of allocated pointers */
    }

    users[*n] = malloc (strlen (u) + 1);    /* allocate for new user */
    if (!users[*n]) {   /* vallidate malloc/handle failure */
        perror ("malloc users[n] - memory exhausted");
        exit (EXIT_FAILURE);
    }
    strcpy (users[(*n)++], u);  /* copy new user to users[n] & increment */

    return users;   /* return pointer to start of users */
}

int main (int argc, char **argv) {

    /* allocate number of pointer given as argument (NREDUCR by default) */
    int nptrs = argc > 1 ? (int)strtol(argv[1], NULL, 0) : NREDUCR,
        ndx = 0, i;
    char user[MAXC] = "";   /* fixed buffer for reading input */
    char **users = NULL;    /* pointer to pointer to char */

    /* allocate/validate initial number of pointers */
    users = malloc (nptrs * sizeof *users);
    if (!users) {
        perror ("malloc - users");
        exit (EXIT_FAILURE);
    }

    while (fgets (user, MAXC, stdin)) {     /* read each ID from stdin */
        size_t len = strlen (user);         /* get length of ID */
        if (len && user[len - 1] == '\n')   /* check last char is '\n' */
        user[--len] = 0;                    /* overwrite w/nul-character */
        else if (len - 1 == MAXC) {         /* test if ID too long */
            fprintf (stderr, "error: ID too long.\n");
            exit (EXIT_FAILURE);
        }
        /* call adduser assigning return to users */
        users = adduser (users, user, &ndx, &nptrs);
    }

    putchar ('\n');         /* output stored user IDs */
    for (i = 0; i < ndx; i++) {
        printf ("user[%2d] : %s\n", i, users[i]);
        free (users[i]);    /* freeing storage as you go */
    }
    free (users);           /* free pointers */

    return 0;
}

示例输入ID(带有重复项)

$ cat dat/userids.txt
1993
2947
2234
2222
3223
1016
1444
1125
1194
2234
2732
2679
2222
2681
1444
1629

示例使用/输出

$ ./bin/addusers_dyn <dat/userids.txt
user: 1993 --- adding
user: 2947 --- adding
user: 2234 --- adding
user: 2222 --- adding
user: 3223 --- adding
user: 1016 --- adding
user: 1444 --- adding
user: 1125 --- adding
user: 1194 --- adding
user: 2234 ==> duplicate
user: 2732 --- adding
user: 2679 --- adding
user: 2222 ==> duplicate
user: 2681 --- adding
user: 1444 ==> duplicate
user: 1629 --- adding

user[ 0] : 1993
user[ 1] : 2947
user[ 2] : 2234
user[ 3] : 2222
user[ 4] : 3223
user[ 5] : 1016
user[ 6] : 1444
user[ 7] : 1125
user[ 8] : 1194
user[ 9] : 2732
user[10] : 2679
user[11] : 2681
user[12] : 1629

内存使用/错误检查

在你编写的动态分配内存的任何代码中,你有2个职责关于任何分配的内存块:(1)总是保留一个指向起始地址的指针内存块,(2)当不再需要时,它可以释放

必须使用内存错误检查程序,以确保您不会尝试访问内存或写入超出/超出已分配块的范围,尝试读取或基于未初始化值的条件跳转,最后,确认您释放了所有已分配的内存。

对于Linux valgrind是正常的选择。每个平台都有类似的记忆检查器。它们都很简单易用,只需通过它运行程序即可。

$ valgrind ./bin/addusers_dyn <dat/userids.txt
==26867== Memcheck, a memory error detector
==26867== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==26867== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==26867== Command: ./bin/addusers_dyn
==26867==
user: 1993 --- adding
user: 2947 --- adding
user: 2234 --- adding
user: 2222 --- adding
user: 3223 --- adding
user: 1016 --- adding
user: 1444 --- adding
user: 1125 --- adding
user: 1194 --- adding
user: 2234 ==> duplicate
user: 2732 --- adding
user: 2679 --- adding
user: 2222 ==> duplicate
user: 2681 --- adding
user: 1444 ==> duplicate
user: 1629 --- adding

user[ 0] : 1993
user[ 1] : 2947
user[ 2] : 2234
user[ 3] : 2222
user[ 4] : 3223
user[ 5] : 1016
user[ 6] : 1444
user[ 7] : 1125
user[ 8] : 1194
user[ 9] : 2732
user[10] : 2679
user[11] : 2681
user[12] : 1629
==26867==
==26867== HEAP SUMMARY:
==26867==     in use at exit: 0 bytes in 0 blocks
==26867==   total heap usage: 16 allocs, 16 frees, 289 bytes allocated
==26867==
==26867== All heap blocks were freed -- no leaks are possible
==26867==
==26867== For counts of detected and suppressed errors, rerun with: -v
==26867== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认已释放已分配的所有内存并且没有内存错误。

仔细看看,如果您有其他问题或错过了问题的意图,请告诉我。如果你能说清楚你需要什么,我很乐意进一步提供帮助。