【问题标题】:Struct elements as parameter in a function C programming结构元素作为函数 C 编程中的参数
【发布时间】:2020-09-06 04:20:41
【问题描述】:

我是编程新手,我很难学习如何在堆上分配内存并尝试同时使用结构和指针。 这就是我想要实现的目标:

  • 创建一个名为 student 的结构,其中包含 2 个指向要在堆上分配的字符串的指针元素
  • 创建一个从用户获取输入并将输入保存在堆上结构的元素中的函数
  • 创建一个函数,打印堆上结构中的所有值
#include <stdio.h>

   struct student
 { 
    char * name;
    char * course;
 };

//function prototypes
void getStudentInfo(struct student *pStu, pStu->name, pStu->course);
void printInfo(struct student springClass[]);


int main(){
//create an array of 5 students
struct student springClass[5];

for(int i=0; i < 5; i++)
{
    getStudentInfo(struct student springClass[i], springClass[i].name, springClass[i].course);
}

printInfo(struct student springClass)

//free memory
free(p1);

return 0;
}


// function that gets input from the user and saves the input in the elements of the struct on the heap

void getStudentInfo(struct student *pStu, pStu->name, pStu->course)
{
    struct student *p1 {NULL}; 

       //create memory on the heap
     p1 = (struct student *) malloc( 2 * (31 * sizeof(char)));  // for 1 names strings and 1 course string

     //get input from user

         printf("Enter name: ");
         fgets(p1->name, 31, stdin);
         printf("Enter course: ");
         fgets(p1->course, 31, stdin);

}

void printInfo(struct student springClass[])
{
     for(int i=; i < 5; i++)
     {
        printf("Student #%d Name: %s\n", i+1, springClass[i].name);
        printf("Student #%d course: %s\n", i+1, springClass[i].course);
        printf("==========================================\n");
     }
}


无论如何,我真的很感激你的帮助,在此先感谢

【问题讨论】:

    标签: c pointers struct structure dma


    【解决方案1】:

    我修复了您代码中的一些主要错误。首先,你可以使用'typedef'来声明一个具有指定名称的指定对象,它允许你一遍又一遍地注意重复结构名称。其次,合理组织你的代码。

    在我的代码中,您可以看到我使用 stdlib 库将函数初始化为 calloc,它是 malloc,但它用 0 初始化所有分配的字节(对字符串特别有用,因为您应该始终用 0 初始化所有内容!)。

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct student
    {
        char* name;
        char* course;
    } student;
    
    //function prototypes
    void getStudentInfo(student* pStu);
    void printInfo(student* springClass);
    
    
    int main() {
        //create an array of 5 students
        student* springClass = (student *)calloc(5, sizeof(student));
    
        for (int i = 0; i < 5; i++)
        {
            getStudentInfo(springClass + i);
        }
    
        printInfo(springClass);
    
        for (int i = 0; i < 5; i++)
        {
            free(springClass[i].name);
            free(springClass[i].course);
        }
    
        free(springClass);
    
        return 0;
    }
    
    
    // function that gets input from the user and saves the input in the elements of the struct on the heap
    
    void getStudentInfo(student* pStu)
    {
        //create memory on the heap
        //get input from user
    
        printf("Enter name: ");
        pStu->name = (char*)calloc(31, sizeof(char));
        fgets(pStu->name, 31, stdin);
        printf("Enter course: ");
        pStu->course = (char*)calloc(31, sizeof(char));
        fgets(pStu->course, 31, stdin);
        printf("\n");
    }
    
    void printInfo(student* springClass)
    {
        for (int i = 0; i < 5; i++)
        {
            printf("Student #%d Name: %s\n", i + 1, springClass[i].name);
            printf("Student #%d course: %s\n", i + 1, springClass[i].course);
            printf("==========================================\n");
        }
    }
    

    【讨论】:

      【解决方案2】:

      首先,为了使用“免费”功能,我们还需要包含 stdlib.h:

      #include <stdlib.h>
      

      当您将结构作为参数传递给函数时,您不需要指定该结构中的元素。当您在顶部将结构定义为:

      struct student
          { 
          char * name;
          char * course;
          };
      

      这是你告诉编译器它需要知道的关于该结构的一切。所以在你的 getStudentInfo 原型中,你只需要这样做:

      void getStudentInfo(struct student *pStu);
      

      现在,让我们开始你的主要功能。

      目前,您没有在堆上分配学生对象。

      struct student springClass[5];
      

      此代码在堆栈上为数组中的 5 个学生结构腾出空间。如果您希望这些对象位于堆中,则必须使用 malloc 动态分配它们,就像您对用户输入所做的那样:

      struct student *springClass = malloc ( 5 * sizeof(struct student) );
      

      如果您事先知道会有多少学生,最好还是坚持目前的方法。但是,动态分配通常适用于您不知道具体数量的情况。

      注意:“动态分配”是指在堆中“动态”分配空间,或者在程序运行时分配空间。它是动态的,因为它可以随时更改。

      无论如何,在将数据放入所有 5 个结构的循环中,我们将 getStudentInfo 函数重新格式化为仅接受“结构学生指针”作为参数。这是我们当前要添加的学生所在的内存位置。在 C 语言中,您可以通过在其前面加上“&”或 & 符号来检索某物的地址。我喜欢通过认为“地址”以 A 开头来记住这一点,就像“与号”一样。这不是一个很强的比较,但它是我所拥有的最好的:)

      因为我们想要一个指向当前学生地址的指针,所以我们传入“&springClass[i]”。另外,因为我们已经告诉编译器 getStudentInfo 接收什么,我们不需要再告诉它,我们可以简单地给它一个值。总而言之,在循环内部,我们这样调用:

      getStudentInfo( &springClass[i] );
      

      如果您想了解有关指针以及 * 和 & 符号的更多信息,this site 提供了很好的解释。

      现在,在循环之后我们还调用了 printInfo。与前面的 getStudentInfo 调用类似,这里我们不需要告诉函数我们要给它什么样的东西,所以不需要指定“struct student”:

      printInfo(springClass);
      

      最后,释放我们之前分配的内存。请注意,如果您不使用“malloc”函数分配内存,而是使用“int array[10];”,例如,此内存将在堆栈上。当你退出一个函数时,堆栈上的内存会被自动释放,所以你不必担心显式释放它。

      注意:如果您尝试在该函数之外使用它,自动释放的内存也会导致问题。例如,如果您在堆栈上分配一些内存,然后返回指向该内存的指针,您将得到分段错误。这样做的原因是当您返回时您的内存被自动释放(这称为“超出范围”)。因此,该指针现在将指向不再有效的内存。

      然而,如果你决定在堆上分配你的 springClass 列表,你将需要使用“free”函数。但是,我们不能告诉它释放“p1”,因为这是我们在 getStudentInfo 函数中定义的。内存以及变量名都受它们在内部定义的“范围”的约束。这意味着我们可以在 GetStudentInfo 中使用变量“p1”,但是如果我们尝试在其他任何地方使用它,编译器将不知道我们在说什么。相反,我们需要释放整个数组,例如:

      free( springClass );
      

      但是等等,还有更多!

      由于我们 malloc 了这个数组中每个结构的所有元素,我们还需要进入其中并单独释放所有这些元素。

      重要提示:free() 不是递归的!

      所以让我们再次遍历列表,这次释放东西,直到最终释放整个列表:

      for ( int i = 0; i < 5; i++ )
          {
          free( springClass[i].name );
          free( springClass[i].course );
          }
      
      free( springClass );
      

      唷,好吧,进入下一个功能。但是,我想快速指出,在编程中,孤立地查看函数通常是一种好习惯。

      “这个函数的参数是什么?”

      “这个函数需要返回什么?”

      “我们如何使用给定的参数做到这一点?”

      如果您单独查看每个功能,它可以帮助您避免不知所措。这也是为什么人们喜欢提倡让你的函数保持较小的部分原因:长函数不仅难以阅读,而且会让人感到困惑,并且在以后调试时变得很痛苦。这也是为什么我一次看完了你的整个 main 函数,而不是在你调用它们时转到其他函数。

      无论如何,我们差不多完成了。最后两个功能只需要最少的修复。首先,这个开始类似于我们如何调整原型:

      void getStudentInfo(struct student *pStu) {
      ...
      }
      

      而且,由于我们已经分配了这个学生对象,无论是在堆栈中还是在堆中都无关紧要,我们只需通过给定的指针访问它的数据成员。但是,我们仍然需要为用户将要输入的字符串分配空间。所以,我们这样做:

      pStu->name = malloc( 31 * sizeof( char ) );
      pStu->class = malloc( 31 * sizeof( char ) );
      

      您可以同时分配两者,但我不建议这样做。一旦你学会了如何实际使用指针和动态内存,你就可以学习如何优化它的使用。

      最终函数变为:

      void getStudentInfo(struct student *pStu)
          {
          // Allocate memory on the stack
          pStu->name = malloc( 31 * sizeof(char) );
          pStu->class = malloc( 31 * sizeof(char) );
      
          // Get input from user
          printf("Enter name: ");
          fgets( pStu->name, 31, stdin);
          printf("Enter course: ");
          fgets( pStu->course, 31, stdin);
      
          }
      

      除了一个小错字(使用“int i=;”而不是“int i = 0;”),我认为您的最终功能没有任何问题。虽然有更好的写法,但我觉得入门并不重要,因为现在你应该专注于理解你写的所有内容。


      这是我的完整代码。请仅将其用作参考,重要的是自己动手并了解发生了什么。

      #include <stdio.h>
      #include <stdlib.h>
      
         struct student
       {
          char * name;
          char * course;
       };
      
      
      //function prototypes
      void getStudentInfo(struct student *pStu);
      void printInfo(struct student springClass[]);
      
      
      int main(){
          //create an array of 5 students
          struct student* springClass = malloc(5 * sizeof(struct student));
      
          for(int i=0; i < 5; i++)
          {
              getStudentInfo(&springClass[i]);
          }
      
          printInfo(springClass);
      
          //free memory
          for ( int i = 0; i < 5; i++ )
              {
              free( springClass[i].name );
              free( springClass[i].course );
          }
          free( springClass );
      
          return 0;
      }
      
      
      // function that gets input from the user and saves the input in the elements of the struct on the heap
      
      void getStudentInfo(struct student *pStu)
      {
          // Allocate memory on the stack
          pStu->name = malloc( 31 * sizeof(char) );
          pStu->course = malloc( 31 * sizeof(char) );
      
          // Get input from user
          printf("Enter name: ");
          fgets( pStu->name, 31, stdin);
          printf("Enter course: ");
          fgets( pStu->course, 31, stdin);
      
      }
      
      void printInfo(struct student springClass[])
          {
          for(int i=0 ; i < 5; i++)
              {
              printf("Student #%d Name: %s\n", i+1, springClass[i].name);
              printf("Student #%d course: %s\n", i+1, springClass[i].course);
              printf("==========================================\n");
              }
      }   
      

      肖恩和我的答案之间存在一些风格差异,但两者都应该有效。对于非常冗长的答案,我深表歉意,但由于您是编程新手,我觉得比通常需要的更多细节可能很有用。希望对您有所帮助!

      编辑:根据 Sean 的建议,在 main 末尾添加了免费的 student->name 和 student->class。

      【讨论】:

      • 非常感谢您的回复,我真的很感激!感激不尽
      • 只是说,您没有在 main 末尾释放 'name' 和 'course' 变量...
      • 哎呀!把它放在我的主体中,但不要放在最后的代码中,谢谢!
      猜你喜欢
      • 1970-01-01
      • 2010-10-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多