【问题标题】:Add data dynamically to array of pointers to structures within function将数据动态添加到指向函数内结构的指针数组
【发布时间】:2016-01-15 17:46:20
【问题描述】:

我尝试了很多组合,但实际上没有任何效果。时间已经够长了,所以我决定写这个问题。

我只想要一个指向结构的指针数组,以便以后可以通过交换地址轻松对其进行排序。我有一个从文件中获取数据并写入数组的函数。不幸的是,我无法在函数之外读取这些数据。

我最后一次尝试(我删除了文件操作,因为这些不是问题):

Header.h:

#pragma once

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

typedef struct {
    int32_t year;
    uint16_t month;
    uint16_t day;
} Date;

typedef struct {
    char name[16];
    Date date;
    uint32_t number;
} Player;

size_t readData(Player** players);

来源.c:

#include "Header.h"

size_t readData(Player** players) {
    players = NULL;

    players = realloc(players, sizeof(Player*) * 1);
    players[0] = malloc(sizeof(Player));
    strcpy(players[0]->name, "asd");
    printf("$ %s\n", players[0]->name);//OK

    return 1;
}

int main() {
    Player **players = NULL;
    uint32_t elemCount, i;

    elemCount = readData(players);

    printf("> %s", players[0]->name);//BUM - ERROR!

    return 0;
}

我正在使用 Visual Studio Community 2015。我知道用 C 编码并没有那么多,但我设法配置了解决方案,而且它似乎不是 imo。

我将非常感谢您的帮助。然而,我想把它看作是我的代码的翻版。我尝试了一些其他类似 StackOverFlow 问题的答案,但没有帮助。

【问题讨论】:

  • 在调用realloc() 时,始终将返回值保存到临时变量中,然后在分配给目标变量之前检查(!=NULL)该变量。这样当realloc()失败时,原来分配的内存指针不会丢失然后如果== NULL,则处理错误
  • 这一行:players = NULL; 是一个非常糟糕的主意,因为传递的指针正在被破坏
  • 函数:readData() 返回一个size_t,但size_t 被分配给uint32_t。这会导致隐式转换。这可能导致值被破坏。建议:更改readData()函数的签名以返回uint32_t
  • 这一行:int32_t year; 应该是uint32_t year;,因为没有负数
  • 编译时,始终启用所有警告,然后修复这些警告。 (对于 gcc,至少使用:-Wall -Wextra -pedantic(我也推荐:-Wconversion -std=c99))。除此之外,这将表明变量i 未被使用。

标签: c function pointers struct pointer-to-pointer


【解决方案1】:

如果一个函数的参数不仅被读取而且也应该是一个输出,你必须将一个指向你的数据的指针传递给函数。如果你的数据类型是Player**,你的参数类型必须是Player ***,所以玩家列表本身可以在函数内部改变。

size_t readData(char* fname, Player*** players) {
                                // ^ players is a input and output parameter
    Player **tempPlayers = *players; // local list of players
    tempPlayers = realloc(tempPlayers, sizeof(Player*) * 1);
    tempPlayers[0] = malloc(sizeof(Player));
    strcpy(tempPlayers[0]->name, "asd");
    printf("$ %s\n", tempPlayers[0]->name);//OK

    *players = tempPlayers; // write back list of players to paramter
    return 1;
}

int main() {
    Player **players = NULL;
    uint32_t elemCount, i;

    char *fileName = NULL; 
    elemCount = readData(&players);
                      // ^

    printf("> %s", players[0]->name);//BUM - ERROR!

    return 0;
}

如果你不想使用***,你可以这样做:

Player* *readData(char* fname, Player** players, size_t *size) {
    players = realloc(players, sizeof(Player*) * 1);
    players[0] = malloc(sizeof(Player));
    strcpy(players[0]->name, "asd");
    printf("$ %s\n", players[0]->name);//OK

    *size = 1;
    return players;
}

players = readData( fileName,  players, &elemCount );

【讨论】:

  • 这个答案包含两个潜在的问题。 1) 使用来自realloc() 的返回值,而不首先检查对realloc() 的调用是否成功;。 2) 使用来自malloc() 的返回值,而不首先检查对malloc() 的调用是否成功。
【解决方案2】:

您通过 value 将指针传递给函数; main 中的原始 players 没有改变。这与做没有什么不同:

#include <stdio.h>

void func(int x)
{
    x = 4;
}

int main()
{
    int x = 0;
    func(x);

    printf("%d\n", x);      // zero
}

我假设是指针让你感到困惑,所以你现在应该看到你的错误了。

要使修改在readData 之外可见,您需要使用三个间接级别:

size_t readData(Player*** players) {
    *players = malloc(sizeof(Player*) * 1);
    *(players)[0] = malloc(sizeof(Player));
    strcpy((*players)[0]->name, "asd");
    printf("$ %s\n", (*players)[0]->name);//OK

    return 1;
}

int main() {
    Player **players = NULL;
    uint32_t elemCount, i;

    elemCount = readData(&players);

    printf("> %s", players[0]->name);  // prints 'asd'

    return 0;
}

当您知道传递的指针始终为NULL 时,使用realloc 毫无意义,因此我将其改为malloc

附带说明:Visual Studio 非常适合 C 开发,并实现了 C99 甚至 C11 的部分内容(过去 MSVC 一直停留在 C89)。您可以对源文件使用 .c 扩展名,这将使编译器假定代码为 C,或者您可以在属性页中显式设置。

【讨论】:

  • 也感谢您的贡献 - 我为之鼓掌;)
【解决方案3】:

传递给readData()的参数必须是调用者指针'players'的地址

readData() 中对players 的每个引用都必须考虑到这一点。

否则调用者的指针'player'将不会被更新。

然后 main() 函数,调用 printf() 试图从地址 0 输出,导致未定义的行为,导致 seg 错误事件。

在 main() 函数中,如果你插入:

printf( "player value; %p\n", (void*)players);

在当前调用 printf() 之前,您会看到 players 指针仍然包含 NULL。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-01-29
    • 1970-01-01
    • 2012-07-16
    • 2011-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-18
    相关资源
    最近更新 更多