您的问题有点不清楚您的最终目标是什么,但您似乎希望从 stdin(或文件)读取用户 ID,并将用户 ID 添加到动态扩展的指针数组中。执行此操作时,您似乎希望确保仅添加新 ID,并跳过所有重复的 ID。
虽然使用指向 char 的指针数组(技术上是 pointer-to-pointer-to-char)没有任何问题,但如果您的所有 ID 都是固定数量的字符,您通过使用指向数组的指针(例如char (*users)[NCHR])作为存储的基础,可以大大简化您的内存分配。 (可以在一次调用中管理和释放存储,而不是分别为 ID 分配指针和存储)但是,因为您看起来想要一个 char ** 作为您的存储方案的基础(无论如何它都是您常用的东西) ),这将得到解决。
当希望以动态方式存储任何内容时,使用指针到指针是这样做的正常基础。方案很简单。最初分配一些合理预期的指针数量,然后分配存储空间以保存每个指针指向的任何内容,当您用完初始指针数量时,只需 realloc 指针数量并继续。 (您可以在每次重新分配时添加任意数量的指针,通过将数量加倍是一种相当常见的方案)
为了把所有这些放在一起,让我们从两个简单的函数开始。在用户集合中查找用户索引(如果存在)并返回索引(或者说-1,如果 ID 不存在)。你可以这样做:
/* 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,如果用户已经存在,则完成。如果不存在,则需要先检查是否已用完所有分配的指针,如果是,则reallocmore(总是将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 becoming 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') 如何作为 指针 传递当它们的值在函数中更新时——新值在调用函数中可用)
这就是您的存储方案的基本要素。现在你需要在调用者中做的所有事情(main() 这里)是读取每个用户 ID,获取长度,修剪尾随 '\n'(所以 strcmp 工作 - 所以你没有虚假的 @987654342 @s 悬挂在字符串的末尾),然后调用 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 becoming 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)
始终确认您已释放已分配的所有内存并且没有内存错误。
如果您还有其他问题或我错过了您的问题意图,请仔细查看并告诉我。如果您能说明您还需要什么,我很乐意提供进一步帮助。