1

我正在为我的 CS1 课程做一个项目,我遇到了我以前从未想过的事情。我知道 C 中的所有数组本质上都是指向数组第一个元素的指针,而字符串实际上只是一个字符数组。但是,对于我的任务,我们必须读入一个文件,该文件的一部分如下:

Brad Tim Rick(更多人名用空格分隔)
露西·安吉拉·蒂娜(更多女性名字以空格分隔)

这是一个简短的示例,但我要做的是提取名称并将它们存储到两个单独的数组中,一个用于男性,一个用于女性。

我从来没有用过这样的东西,所以我当然很困惑。这就是我想要做的,当然它不工作......哦,是的,我正在尝试将它们存储在动态分配中。唯一的规范说名称永远不会超过 19 个字符(我应该说 20 以允许字符串末尾的 '/0' 无论如何仍然存在吗?)我如何告诉编译器,“嘿,我想要一个字符串数组,每个字符串可以容纳 19 个字符 + 1 用于“字符串预告片'/0'”?然后我如何通过指针访问它们?

char **mens_names, **womens_names;

mens_names = malloc(number_of_couples * sizeof(char[19]));
womens_names = malloc(number_of_couples * sizeof(char[19]));

if(mens_names == NULL){
printf("Malloc failed! Memory could not be allocated to variable mens_names.");
return -1;
}

int i;
for(i = 0; i < number_of_couples; i++){
    fscanf(input_file, "%s", &mens_names[i]);
}


if(womens_names == NULL){
    printf("Malloc failed! Memory could not be allocated to variable womens_names.");
    return -1;
}

for(i = 0; i < number_of_couples; i++){
    fscanf(input_file, "%s", &womens_names[i]);
}

for(i = 0; i < number_of_couples; i++){
    printf("Man: %s ", mens_names[i]);
    printf("Woman: %s\n", womens_names[i]);
}
4

3 回答 3

2

您在谈论二维数组,但仅将其初始化为一维数组。二维数组(矩阵)的正确初始化如下:

static char** allocate_matrix(int nrows, int ncols) 
{
    int i;
    char **matrix;

    /*  allocate array of pointers  */
    matrix = malloc( nrows*sizeof(char*));

    if(matrix==NULL)
        return NULL; /* Allocation failed */

    /*  Allocate column for each name  */
    for(i = 0; i < nrows; i++)
        matrix[i] = malloc( ncols*sizeof(char));

    if(matrix[i-1] == NULL) 
        return NULL; /* Allocation failed */

    return matrix;
}

在你的main()

<...>
mens_names = allocate_matrix(number_of_couples, 19);
womens_names = allocate_matrix(number_of_couples, 19);
<...>

/* Of course, do not forget to free memory once you are done */
于 2013-01-29T13:06:45.267 回答
2

我知道C中的所有数组本质上都是指向数组第一个元素的指针

不完全的。数组和指针完全是两个不同的东西。除非它是sizeof, _Alignof, 或一元运算符的操作数&,或者是用于在声明中初始化数组的字符串文字,“N-element array of ”类型的表达式将被转换为“pointer”类型的表达式to " 并且它的值将是数组中第一个元素的地址。 TT

鉴于声明

int a[10];

指定的对象a永远是一个 10 元素数组int;但是,表达式 a可以被视为指向第一个元素的指针。

如果您知道您的字符串长度永远不会超过 19 个字符(包括终止符在内的 20 个元素),但不提前知道字符串的数量,您可以执行以下操作:

char (*mens_names)[20];
char (*womens_names)[20];
...
mens_names = malloc(number_of_couples * sizeof *mens_names);
womens_names = malloc(number_of_couples * sizeof *womens_names);
...
fscanf(input_file, "%s", mens_names[i]);
...
free(mens_names);
free(womens_names);

在这种情况下,我们将和声明mens_nameswomens_names指向 20 元素数组的指针char(括号很重要)。因此,sizeof *mens_names等价于sizeof (char [20])

您可以像使用常规二维数组一样访问每个单独的字符:

char x = mens_names[i][j];

mens_names[i]隐式取消引用mens_names指针(请记住,表达式a[i]被解释为*(a + i))。

与 KBart 的方法相比,这种方法有几个优点。首先,所有内存都作为单个块连续分配,如果缓存成为问题,这可能很重要。其次,每个数组只需要一个malloc和一个free。当然,这假设每个名称数组的最大大小是 a) 固定的并且 b) 在编译时已知。

如果您直到运行时才知道名称的大小,并且您使用的是支持可变长度数组的 C99 编译器或 C2011 编译器,则可以执行以下操作:

size_t name_len, number_of_couples;
// get name_len from the user or input file
// get number_of_couples
char (*mens_names)[name_len+1] = malloc(number_of_couples * sizeof *mens_names);
...

如果您直到运行时才知道名称的大小,并且您使用的编译器支持 VLA,那么您将需要使用 KBart 的方法。

如果你想变得更花哨,你可以使用一个 3 维数组而不是两个 2 维数组:

#define MENS_NAMES 0
#define WOMENS_NAMES 1
...
char (*all_names)[2][20] = malloc(number_of_couples * sizeof *all_names);
...
fscanf(input_file, "%s", all_names[i][MENS_NAMES]);
...
free(all_names);
于 2013-01-29T14:02:08.780 回答
0
void init(char**** tab,int size1d,int size2d,int size_string)
{
    int iterator;
    int iterator_2;

    char*** temp = (char***) calloc(size1d,sizeof(char**));
    for (iterator = 0 ; iterator < size1d;iterator++)
    {
        *(temp+iterator) = (char**) calloc(size2d,sizeof(char*));

        for (iterator_2 = 0;iterator_2 < size2d;iterator_2++)
        {
            *(*(temp+iterator)+iterator_2) = (char*) calloc(size_string,sizeof(char));
        }
    }

    *tab = temp;
}

int main()
{
    char*** tab;

    init(&tab,100,100,255);
    tab[5][99] = "Hi";

    printf("%s",tab[5][99]);
}
于 2016-06-22T20:20:51.347 回答