【问题标题】:function for a pointer to structure and copy it content without memcpy指向结构的指针的函数并在没有 memcpy 的情况下复制它的内容
【发布时间】:2016-07-24 17:45:23
【问题描述】:

我正在尝试创建一个指向单个库存结构(一条记录)的函数,并将其内容复制到inventory.c 中定义的gobal 数组中的一个元素。我想逐个成员地复制它们,如果复制失败,则返回整数-1。在我的主要功能中,我想遍历我的示例数据并为 sampleData Array 中的每个元素调用 invSetRecord 函数。我不知道该怎么做,需要一些帮助。我更新了我的 inventory.c 文件。我对我的功能感到困惑。我是自己编译的。我不明白如何让 productName 复制。有什么建议么?我也很难为库存记录设置计数器。

这是我老师的指示,

使用为实验 9(第 2 部分)创建的 petstore 文件来完成此作业。

将以下示例数据复制到petstore_main.c 文件中:

# define SAMPLE_SZ 5

struct inventory_s sampleData[SAMPLE_SZ] = {
    { 1000, 1.49, 3.79, 10, 0, "Fish Food" },
    { 2000, 0.29, 1.59, 100, 1, "Angelfish" },
    { 2001, 0.09, 0.79, 200, 1, "Guppy" },
    { 5000, 2.40, 5.95, 10, 0, "Dog Collar, Large" },
    { 6000, 49.99, 129.99, 3, 1, "Dalmation Puppy" }
};

inventory.c文件中定义一个函数,其原型如下(将原型放在inventory.h文件中)

int invSetRecord(struct inventory_s *ipx);

此函数将获取一个指向单个库存结构(一条记录)的指针,并将其内容复制到inventory.c 中定义的全局数组中的一个元素。目标位置(全局数组元素)将由数组的当前大小确定,如果数组为空,则将记录复制到元素 0,如果当前大小为 1,则将记录复制到元素 1,依此类推。意味着您需要在 inventory.c 中定义一个全局计数器,用于跟踪数组中的元素数量。在 invSetRecord 函数中,您需要使用适合类型的方法逐个成员复制结构成员,例如原始数据类型的简单赋值,字符串数据类型的 strcpy()。该函数返回一个整数值:如果复制操作失败(例如全局库存数组已满),该函数返回-1,否则返回0。

petstore_main.c 的主函数中,您需要创建一个循环,该循环遍历样本数据并为sampleData 数组中的每个元素调用invSetRecord。对于对 invSetRecord 的每次调用,您需要检查函数的返回值,如果操作失败或成功,则打印错误,以及相应的记录编号。例如,如果所有 5 个样本数据记录的操作都成功,则这是从您的 main 函数打印的输出:

record #1 set successfully
record #2 set successfully
record #3 set successfully
record #4 set successfully
record #5 set successfully

如果对 invSetRecord 的调用失败(函数返回 -1),则会打印以下消息:

error: could not set record 1

如果发生错误,您必须在该点跳出循环。

到目前为止,我需要一些帮助。我整个星期都在拔头发,试图弄清楚这一点。提前感谢您的帮助。

//inventory.c    
#include <stdio.h>

#include "inventory.h"
int i;
#define invSetRecord main

struct inventory_s inventory[MAX_INVENTORY];
int i;

int invSetRecord(struct inventory_s *ipx)
{

    int result;
    i = sizeof(MAX_INVENTORY)/sizeof(inventory[0]);

    if (i > MAX_INVENTORY)
    {
    result = -1;
    printf("%i", i);
    }
    if (i < MAX_INVENTORY)
    {
        result = 0;
        printf("%i", i);
        inventory[i].productNumber = ipx->productNumber;
        inventory[i].mfrPrice = ipx->mfrPrice;
        inventory[i].retailPrice = ipx->retailPrice;
        inventory[i].numInStock = ipx->numInStock;
        inventory[i].liveInv = ipx->liveInv;
        //inventory[i].productName= (ipx->productName);


        i++;
    }
    return result;



}

这是我的 inventory.h 文件...

#ifndef _INVENTORY_H_ //ensures that inventory.h does not run more than once
#define _INVENTORY_H_

#define  PRODUCTNAME_SZ 20
#define MAX_INVENTORY 50


struct inventory_s
{
    int productNumber;
    float mfrPrice;
    float retailPrice;
    int numInStock;
    char liveInv;
    char productName[PRODUCTNAME_SZ];
};

int invSetRecord(struct inventory_s *ipx);

#endif //_INVENTORY_H_

这是我的 main() 文件...

//main.c
#include <stdio.h>
#include <stdlib.h>

#include "inventory.h"

#define SAMPLE_SZ 5

extern struct inventory_s inventory[MAX_INVENTORY]; 

struct inventory_s sampleData[SAMPLE_SZ]={

    { 1000, 1.49, 3.79, 10, 0, "Fish Food" },
    { 2000, 0.29, 1.59, 100, 1, "Angelfish" },
    { 2001, 0.09, 0.79, 200, 1, "Guppy" },
    { 5000, 2.40, 5.95, 10, 0, "Dog Collar, Large" },
    { 6000, 49.99, 129.99, 3, 1, "Dalmation Puppy"}
};

int main()
{
    int i;

    for(i = 0; i < SAMPLE_SZ; i++);
    invSetRecord(sampleData);

    printf("The product number is %i for sampleData element[]", sampleData[0].productNumber);
    return 0;
}

【问题讨论】:

  • 对winmain的未定义引用是由于链接器找不到主函数的定义。你自己编译inventory.c吗?您需要同时编译main.cinventory.c,以便链接器可以找到int main() 的定义(在main.c 中)以及在inventory.h 中声明的函数的定义(在inventory.c 中)
  • 我自己编译它,然后在顶部添加了#define invSetRecord main,它现在运行良好。我仍然对将 productName 复制到指针感到困惑。有什么建议吗?
  • 我更新了一些 inventory.c 文件。我的结构分配有问题。我的格式看起来正确吗?我试图让结构的指针复制它在函数中指向的内容,所以我可以在我的主函数中为每个 sampleData 调用它。

标签: c function pointers struct structure


【解决方案1】:

让我们从编译器抛出错误和警告的简单问题开始:

#include "inventory.h"
int i;
#define invSetRecord main

struct inventory_s inventory[MAX_INVENTORY];
int i;

i需要声明多少次? #define invSetRecord main 没有理由。请注意,在声明全局时,建议使用比非常常见的 i 更独特的名称。

invSetRecord,(使用idx(索引)作为你的i),为什么不干脆做:

/* inventory.c */
#include <stdio.h>
#include <string.h>

#include "inventory.h"

int idx;

struct inventory_s inventory[MAX_INVENTORY];

int invSetRecord (struct inventory_s *ipx)
{
    if (idx == MAX_INVENTORY) return -1;

    inventory[idx].productNumber = ipx->productNumber;
    inventory[idx].mfrPrice      = ipx->mfrPrice;
    inventory[idx].retailPrice   = ipx->retailPrice;
    inventory[idx].numInStock    = ipx->numInStock;
    inventory[idx].liveInv       = ipx->liveInv;
    strcpy (inventory[idx].productName, ipx->productName);
    idx++;

    return 0;
}

注意,您的编译器会告诉您不能简单地将ipx-&gt;productName 分配给inventory[idx].productName,因为它是一个字符数组(转换后是char *)。您必须使用strcpy(或strncpy)或简单地迭代(ipx-&gt;productName)[],直到到达nul-terminating字符,将逐个字符复制到inventory[idx].productName[]

以下内容毫无意义:

    i = sizeof(MAX_INVENTORY)/sizeof(inventory[0]);

(您只能在数组声明的范围内获取数组中的元素个数(不是指针)使用sizeof array/sizeof array[0]。你有什么以上与正确使用没有相似之处。)

inventory.h 的其余部分似乎还不错。然而,第一行不是//ensures that inventory.h does not run more than once,它确保头文件inventory.h 不被包含超过一次。

您的main() 具有创意……至少可以这么说。您要完成的是将sampleData[i](例如&amp;sampleData[i])的地址传递给invSetRecord(例如指向结构的指针,而不是结构本身)。

此外,这段代码会发生什么?

    for(i = 0; i < SAMPLE_SZ; i++);

回答:没有。 i 只是从 0 - SAMPLE_SZ 更改值,而不影响任何其他代码。为什么?循环后面的; 等同于:

    for(i = 0; i < SAMPLE_SZ; i++) {}

例如——一个空块。删除; 以便循环在以下行上运行。

不清楚您最后要打印什么。假设您希望每个元素都使用 productNumber,您可以执行以下操作:

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

#include "inventory.h"

#define SAMPLE_SZ 5

extern struct inventory_s inventory[MAX_INVENTORY]; 

struct inventory_s sampleData[] = {
    { 1000, 1.49, 3.79, 10, 0, "Fish Food" },
    { 2000, 0.29, 1.59, 100, 1, "Angelfish" },
    { 2001, 0.09, 0.79, 200, 1, "Guppy" },
    { 5000, 2.40, 5.95, 10, 0, "Dog Collar, Large" },
    { 6000, 49.99, 129.99, 3, 1, "Dalmation Puppy" }
};

int main (void)
{
    int i;

    for (i = 0; i < SAMPLE_SZ; i++)
        if (invSetRecord (&sampleData[i]) == 0)
            printf ("record #%d set successfully.\n", i);
        else {
            fprintf (stderr, "error: could not set record %d.\n", i);
            break;
        }
    putchar ('\n');     /* tidy up output formatting */

    for (i = 0; i < SAMPLE_SZ; i++)
        printf ("sampleData[%2d] : %d\n", i, sampleData[i].productNumber);

    return 0;
}

总而言之,编译和运行代码将产生以下结果:

$ ./bin/main
record #0 set successfully.
record #1 set successfully.
record #2 set successfully.
record #3 set successfully.
record #4 set successfully.

sampleData[ 0] : 1000
sampleData[ 1] : 2000
sampleData[ 2] : 2001
sampleData[ 3] : 5000
sampleData[ 4] : 6000

全部查看,如果您有任何问题,请告诉我。只需放慢速度,仔细考虑代码的每一行。此外,如果您在将代码拆分为三个不同的源文件时遇到困难,您可能希望简单地使用单个源文件进行初始开发,然后在一切都有意义时将其拆分为单独的文件。这样一来,您一开始就消除了除代码问题之外的所有问题。


使用 IDE 的注意事项

为了完整起见,您的inventory.h 可以保留原样。我做了一些更改(除了个人喜好,例如#define _INVENTORY_H_ 1 来实际定义_INVENTORY_H_ 的值,而不是让它默认)。我使用了以下内容:

#ifndef _INVENTORY_H_
#define _INVENTORY_H_ 1

#define PRODUCTNAME_SZ 20
#define MAX_INVENTORY 50

struct inventory_s {
    int productNumber;
    float mfrPrice;
    float retailPrice;
    int numInStock;
    char liveInv;
    char productName[PRODUCTNAME_SZ];
};

int invSetRecord (struct inventory_s *ipx);

#endif

如 cmets 中所述,我只是通过从命令行调用 gcc 来编译和构建项目。具体我用过:

$ gcc -Wall -Wextra -pedantic -std=gnu11 -Ofast inventory.c -o main main.c

所需的基本、最小编译字符串是:

$ gcc inventory.c -o main main.c

这只是说编译main.c-o main 将生成的可执行文件输出到一个名为main 的文件中(它可以是任何你想要的,bananas 就此而言),最后是inventory.c,这就是告诉编译器,除了main.c,它还必须编译和链接inventory.c 中的代码,才能生成一个可以工作的可执行文件。您从命令行运行代码:

$ ./main     (or ./bananas if you went that route)

其余选项仅启用所有编译器警告(因此您可以在考虑代码可靠之前修复它们),-Ofast 启用所有编译器优化(此处不是那么重要),并设置语言标准到带有 gnu 扩展的 C11。我将在下面进一步解释。

-Wall -Wextra -pedantic 只需启用所有编译器警告。 (至少在每个项目中使用-Wall -Wextra修复所有警告,然后才能认为您的代码可靠)对于代码块,请查看Project-&gt;Build options...-&gt;Compiler Flags(然后选中@987654373 的框@,以及下一个框为-Wextra)您也可以选中-pedantic 选项,但这会增加另一层微妙的警告,这些警告会在初步学习中有所帮助。

-std=gnu11 只是告诉编译器使用C11(和gnu extensions)构建。您可以省略此标志并默认构建为C89。 (没关系)

-Ofast 仅启用所有编译器优化。 -Ofast 优化适用于gcc 4.6 及以上,否则它们只是-O0(即Oh zero)意味着没有优化。 -O1-O2-O3 级别仅允许编译器进行更积极的优化以加快代码速度——例如消除死代码、优化循环等)

对于您的Debug 构建,您通常不会启用-Ofast 优化,但在Linux 上,包含-g 以生成与gdb 调试器一起使用的调试符号。 (我认为 Codeblocks 会自动为您执行此操作)

如果您想先将inventory.c 编译为目标文件,然后将目标文件与main.c 链接以使invSetRecord 函数可用于main,您可以这样做:

$ gcc -Wall -Wextra -c -o inventory.obj inventory.c

执行此操作,将inventory.c 编译为目标文件inventory.obj。最后,您只需编译 main.c 链接 inventory.obj 以创建最终的可执行文件:

$ gcc -Wall -Wextra -o main inventory.obj main.c

(选项的顺序并不重要,只要记住您必须在依赖库中代码的源文件之前包含所有需要的。)

这应该允许您编译和链接您的代码(从命令行或在代码块中)。如果你再次卡住,请告诉我。

【讨论】:

  • 谢谢兰金先生!我回顾了你所说的一切,我开始更好地理解它。我现在编译它时遇到问题。我的构建消息说“未定义对 invSetRecord 的引用”,但我在我的 inventory.h 文件中对其进行了原型化,并将该文件包含在主文件的顶部。你觉得我把它们联系在一起有什么不对吗?顺便说一句,我正在使用 CodeBlocks 来编译它。
  • 您必须同时编译inventory.cmain.c。我从命令行一次性完成所有操作。 gcc -Wall -Wextra -pedantic -std=gnu11 -Ofast inventory.c -o main main.c。我讨厌 IDE(尤其是对于学习 C 的人),因为它们隐藏了构建代码的非常简单(但重要)的概念。您的错误是由于 main.c 正在编译而无法找到函数 invSetRecord(位于 inventory.c 文件中)。所以你要么编译inventory.cobject(例如gcc -c inventory.c -o inventory.obj),然后gcc main.c inventory.obj -o main
  • (对不起,上面的空间用完了——每次编译时不要忘记-Wall -Wextra——这几乎可以启用所有警告,如果你真的想查看所有警告,请添加-pedantic)。您可以告诉 Codeblocks 编译两者(或先将inventory.c 编译为目标文件,然后将其作为main.c 编译的一部分包含在内。试一试,如果您遇到,请告诉我更多问题——关键是真正(我的意思是真正)理解每一行代码在做什么。如果你不明白,那就问吧。C 需要一段时间来学习——但每一分钟花费的努力是值得的。(注意,如果需要,请删除-std=gnu11
  • 为了了解您所面临的情况,我安装了代码块。哇!需要吸收的内容很多。不要误会我的意思,它是很好的 IDE,但对于简单的代码(您将在明年的学习中看到的内容),它只是增加了另一层学习。我怀疑您已经创建了'project'(并且所有文件都位于您的项目目录中)如果文件位于不同的目录中,则必须设置适当的Project-&gt;Build Options.. 来设置搜索路径和链接器路径。如果代码块位于单个目录中,代码块将自动按顺序构建所需的文件。
  • 我发现了我的问题。我没有将它设置为代码块中的项目。我的老师希望我们在一个项目中使用多个文件。我试图在没有将它们包含在内部并在项目文件夹中链接在一起的情况下编译它们。\
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多