C言語:エッセイ8-構造

学習するとき、なぜ構造を使用するのかを知る必要がありますか?

参照しやすいように、さまざまなタイプのデータを有機的な全体に結合(カプセル化)する必要がある場合があるためです。(たとえば、クラスはメソッドとプロパティをカプセル化することであり、ここでの構造はさまざまな変数をカプセル化することです)

たとえば、学生には学生ID /名前/年齢/住所などの属性があります

int num;
char name[20];
char sex;
int age;
char addr[30];

1.構造

構造を定義する一般的な形式は次のとおりです。

struct 结构名
{
   成员列表
};
//成员列表由若干个成员组成,每个成员都是该结构的一个组成部分。对每个成员也必须作类型说明,其形式为:
类型说明符 成员名;//int num;


//比如:定义的student学生类他有6个属性
struct stucent
{
   int num;
   char name[20];
   char sex;
   int age;
   float score;
   char addr[30];

};

次の3つの方法を使用して、構造タイプ変数を定義できます。

(1)最初に構造タイプを宣言してから、変数名を定義します。

//比如:定义的student学生类他有6个属性
struct stucent//struct 结构体名
{
   int num;
   char name[20];
   char sex;
   int age;
   float score;
   char addr[30];//成员表列

};
struct student  student1,student2;//类型名 结构体  变量名,变量名
//定义了student1和student2为struct student类型的变量,即他们具有struct student类型的结构。

(2)タイプを宣言しながら変数を定義する

/比如:定义的student学生类他有6个属性
struct stucent
{
   int num;
   char name[20];
   char sex;
   int age;
   float score;
   char addr[30];

}student1,student2//变量名列表
//在定义结构体变量后,系统会为之分配内存单元。例如studet1和student2在内存中各占?子节
(4+20+1+4+4+30=67)

(3)構造タイプ変数を直接定義する

//即不出现结构体名
struct
{
   成员表列
}变量名表列;

構造のネストされた定義(構造内の構造)

1つに 名前 セックス 年齢 お誕生日 addr

まず、月(月)、日(日)、年(年)、および3つのメンバーで構成される構造データを定義する必要があります。

変数boy1とboy2が定義されて説明されると、メンバーの誕生日がデータ構造タイプとして説明されます。メンバー名は、互いに干渉することなく、プログラム内の他の変数と同じ名前にすることができます。(スコープが異なるため)

//结构的嵌套
struct data
{
   int month;
   int day;
   int year;
};

struct
{
int num;
char name[20];
char sex;
struct data birthday;
float score;
}boy1,boy2={102,“jane”,'M',98.5};//结构体变量初始化,对变量赋值(有点像数组)
boy1=boy2;//将boy2的值给了boy1,全体成员copy

//如果对boy1使用点"."运算符进行了赋值操作的话,那么boy2=boy1;(将boy1的全体成员赋值给了boy2)这个语句就可以把boy1里边成员变量的值直接都赋值给boy2的成员变量,因此Boy2不用再一个个的进行赋值。

構造変数を定義した後、もちろんこの変数を参照できます。準拠する必要があります:

(1)構造変数全体の入出力はできません。

ドット「。」メンバー演算子を使用する必要があります。

//比如打印student1中的各个变量的值时,我们不可以直接
printf("%d,%s,%c,%d,%f,%\n",student1);

//正确引用结构体变量中成员的方式为:
//结构体变量名.成员名//点操作符来进行引用
//student1.num//表示student1变量中的num成员。
//student1.num=100;//可以对变量的成员赋值

「。」はメンバー(コンポーネント)演算子であり、すべての演算子の中で最も優先度が高いため、student1.num全体を扱うことができます。上記の割り当てステートメントは、student1変数のメンバーnumに整数100を支払うことです。

次のステートメントを使用して構造変数全体を読み取ることもできません:(下部の各レベルで正確にするため)

scanf("%d,%s,%c,%d,%f,%s",&student1);

(2)メンバー自体が構造タイプに属している場合、複数のメンバー演算子を使用して、レベルごとに最下位のメンバーレベルを検索します。最下位レベルのメンバーのみを割り当てたり、アクセスしたり、操作したりできます。

上で定義した構造変数student1の場合、各メンバーには次のようにアクセスできます。

student1.num
student1.birthday.month//因为birthday也是一个结构的变量名,他不能代表全体成员,我要再点进去,访问到最下级,最下级才有操作的权力。

(3)構造変数のメンバーは、通常の変数と同様にさまざまな操作を実行できます(実行できる操作はタイプによって決まります)。

(4)構造変数メンバーのアドレス(&boy1.num)または構造変数のアドレス(&boy1)を参照できます。(&Boy1と&boy1.numの2つのアドレスは同じで、配列に似ています)

構造変数のアドレスは、主に構造変数のアドレスを渡すための関数パラメーターとして使用されます。(実際には、最初の要素のアドレスは問題ありませんが、構造変数のアドレスが一般的に使用されます。名前はより代表的なものです)

2、構造配列

構造変数には、一連のデータ(学生の学生ID、名前、成績など)を格納できます。データを計算する必要のある学生が10人いる場合は、明らかに配列を使用する必要があります。これは構造配列です。

構造配列は、前に紹介した数値配列とは異なります。各配列要素は構造型データであり、すべてに各メンバー(コンポーネント)項目が含まれていることがわかります。

以下は、説明するためのアドレス帳の例です。

#include<stdio.h>
#include<stdlib.h>
#define NUM 3

struct person//定义person结构
{
   char name[20];
   char phone[10];
};
void main()
{
   struct person man[NUM];//定义man数组,结构体数组,有三个元素
   int I;
   for(I=0;i<NUM;i++)//一个元素包含了两个项分别是name和phone。
   {
      printf("input name:\n");
      gets(man[i].name);
      printf("input phone:\n");
      gets(man[i].phone);
   }
   printf("\tname\t\t\t\t\tphone\n\n");
   for(i=0;i<NUM;i++)
   {
      printf("%20s\t\t\t%20s\n",man[i],name,man[i].phone);
   }
   system("pause")
}

構造配列の定義:

//第一种定义方法
struct stucent
{
   int num;
   char name[20];
   char sex;
   int age;
   float score;
   char addr[30];

};
struct student student[3];//定义了一个数组有三个元素,这个数组名称叫student,有三个元素,每一个元素都有上述6个项

//第2种定义方法
struct stucent
{
   int num;
   char name[20];
   char sex;
   int age;
   float score;
   char addr[30];

}studet[3];

構造配列の初期化:

struct stucent
{
   int num;
   char name[20];
   char sex;
   int age;
   float score;
   char addr[30];

}stu[2]={
            {101,“Li”,'M',18,87.5,"Beijing"},
            {102,“zhang",'F',19,99,"Shanghai"}
        
        };

//或者也可以用以下形式初始化:
struct student
{
   int num;
   ...
};
student student str[]{
   
   {...},{...},{...}};
//即先声明结构体类型,然后定义数组为该结构体类型,再定一数组是初始化。

質問の例:候補者の投票の統計手順。候補者は3名です。投票した候補者の名前を入力するたびに、各投票の結果を出力する必要があります。そして、最高の有権者を出力します。

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

#define NUM 5
struct person
{
      char name[20];
      int count;
}candidate[NUM] = {
                  {"A", 0},
                  {"B", 0}, 
                  {"C", 0},
                  {"D", 0}, 
                };//定义结构体数组

char *winner();

int main()
{
      int i, j;
      char candidate_name[20];

      printf("欢迎进入2020年火爆网站评选系统:\n\n");
      printf("选择项目:A,B,C,D \n\n");
      
      for( i=1; i <= 6; i++ )
      {
            printf("第 %2d 位投票, 请选择支持的候选人: ", i);
            scanf("%s", candidate_name);
            for( j=0; j < NUM; j++ )//j小于5,所以j只能到4,因为有4个A,B,C,D
            {
                  if( 0 == strcmp(candidate_name, candidate[j].name) )
                  {
                        candidate[j].count++;
                  }
            }
      }
      printf("\n");

      for( i=0; i < 5; i++ )//打印ABCD分别的得票数
      {
            printf("%s 得票数为: %d\n", candidate[i].name, candidate[i].count );
      }
      printf("\n");
      printf("本次评选活动的胜利者的: %s\n", winner() );//调用winner()函数,比较得票最高的是谁

      printf("\n");
      system("pause");
}

char *winner()
{
      int i =0 , winner = i;//相当于winner=0
      
      for( i=1; i < NUM; i++ )
      {
            if( candidate[winner].count < candidate[i].count )//结构体数组的第一个元素的count项依次与后边的元素的count项比较比较
            {
                  winner = i;
            }
      }
      
      return candidate[winner].name;
}

----構造タイプデータへのポインタ---

構造変数のポインタは、構造変数が占めるメモリセグメントの実際のアドレスです。(ポインタはアドレスを格納する変数です)

構造変数を指すようにポインタ変数を設定できます。このとき、ポインタ変数の値(つまりアドレス)が構造変数の開始アドレスになります。(あなたが何を意味するにせよ、ポインターは本当に素晴らしいです)

ポインタ変数を使用して、構造配列内の要素を指すこともできます。

構造ポインタ変数の一般的な形式:

struct  结构名  *结构指针变量名//*表示他是指针
//例如前面定义的stu这个结构,说明一个指向stu的指针变量名pstu,可以写为:
struct  stu  *pstu;//*表示它是一个指针,stu表示他是哪一个结构的指针。
//当然也可以在定义stu结构时同时说明pstu。与前面讨论的各类指针变量相同,结构指针变量也必须要先赋值后才能使用。(不然就是个无头案)

//割り当ては、構造変数の最初のアドレスをポインター変数に割り当てることであり、構造名をポインター変数に割り当てることはできません。

構造名と構造変数名は同じものではありません(1つはstudentで、もう1つはboy1です)。例えば

//如果boy是被说明为stu类型的结构变量,则:
struct stu//结构为stu
{
   ...
}boy={...,...,...,};//结构变量为boy,并同时赋初值
//(还可以结构变量名再有一个girl,不止一个boy了这次,所以再声明一个指针指向girl就好了,访问方式跟下边雷同)
struct  stu  *pstu;//*表示它是一个指针,stu表示他是哪一个结构的指针。//定义结构变量指针
pstu=&boy;//是正确的//将boy的初始值地址给了它
(*pstu).name//正确;
(*pstu)->name//正确;
boy.name//正确//通过三种形式可以输出
pstu=&stu;//是错误的
//为什么呢???
//因为结构名stu和结构变量boy是两个不同的概念,不能混淆,结构名只能表示一个结构形式,编译系统并不对它分配内存空间。
//只有当某变量被说明定义为这种类型的结构时,才对该变量分配存储空间。在定义时操作系统才会为它分配空间。只是说明没定义不分配空间。
//因此上面&stu这种写法是错误的,不可能去取一个结构名的首地址。(因为结构名本来是有地址的,他只是让编译器知道,编译器在编译时会申请空间)有了结构指针变量,就能更方便的访问结构变量的各个成员。

//其访问的一般形式为:
(*结构指针变量).成员名
//或者:
结构指针变量->成员名
//例如:
(*pstu).num
//或者:
pstu->num

例:構造変数を指すポインター変数の適用(上記の3つの形式での出力)。

3つ、関数パラメーターとしての構造ポインター変数

構造変数の値を別の関数に渡すには、次の3つの方法があります。

(1)構造変数のメンバーをパラメーターとして使用します。

(2)構造変数を実際のパラメーターとして使用します。(実際のパラメーターとして構造変数の名前を使用します)

(3)構造変数(または配列)へのポインターを実際のパラメーターとして使用し、構造変数(または配列)のアドレスを仮パラメーターに渡します。

例:3つのコースの学生ID、名前、成績を含む構造変数stuがあります。これらは、関数printを呼び出すことによって出力されます。

(要件は、構造変数を関数パラメーターとして使用することです(実際のパラメーターとして渡す)(2))

この例では、構造全体をコピーします。これは、void print(struct student)であるためです。ここでのパラメーターは、構造変数全体です。(値渡し)

#include<stdio.h>
#include<string.h>
struct student//定义结构
{
   int num;
   char name[20];//字符串,(name[20]出现在栈里面,实实在在的空间,而下面的“zhangsan字符串出现在数据常量区”//改变为char *name;的话下边的语句stu.name="zhangsan";是将数据区常量的地址传递给name这个指针而已。
   float score[3];//浮点型数组
};
void print(struct student);//函数的参数是这个结构体,一个结构体作为他的形参
void main()
{
   struct student stu;//定义这个结构体变量的名称为stu
   stu.num=8;
   strcpy(syu.name,"zhangsan");//如果用stu.name="zhangsan";这个语句会错误(复制号的左边必须是一个左值l-value(可以简单的理解l-value就是一个地址,必选是一个指针所以上边就必须改一下定义为char *name;这样的话这句话就没有错误了)),
   stu.score[0]=98.2;
   stu.score[1]=93.2;
   stu.score[2]=98.7;//分别赋值
   print(stu);//把结构体变量作为实参传递给他
}
void print(struct student stu)//print这个函数知道了结构体变量的位置就能够索引到他的名称学号成绩并分别打印
{
    printf("\tnum      :%d\n",stu.num);
    printf("\tname      :%s\n",stu.name);
    printf("\tscore_1      :%5.2f\n",stu.score[0]);
    printf("\tscore_2      :%5.2f\n",stu.score[1]);
    printf("\tscore_3      :%5.2f\n",stu.score[2]);
    printf("\n")
}

(実際のパラメーターとして構造変数へのポインターが必要です(3))

2番目の方法では、アドレスのみがコピーされるため、効率の観点から、2番目の方法は送信が少ない方法です。(通路)

#include<stdio.h>
#include<string.h>
struct student//定义结构
{
   int num;
   char name[20];//字符串,(name[20]出现在栈里面,实实在在的空间,而下面的“zhangsan字符串出现在数据常量区”//改变为char *name;的话下边的语句stu.name="zhangsan";是将数据区常量的地址传递给name这个指针而已。
   float score[3];//浮点型数组
};
void print(struct student *);//函数的参数是指针
void main()
{
   struct student stu;//定义这个结构体变量的名称为stu
   stu.num=8;
   strcpy(syu.name,"zhangsan");
   stu.score[0]=98.2;
   stu.score[1]=93.2;
   stu.score[2]=98.7;//分别赋值
   print(&stu);//因为上边是指针,指针必须接收一个地址嘛,所以这里传递给他的那就是这个结构体的地址了
}
void print(struct student *p)//用指针来接收,p指向的还是这个结构,用p来打印
{
    printf("\tnum      :%d\n",p->num);
    printf("\tname      :%s\n",p->name);
    printf("\tscore_1      :%5.2f\n",p->score[0]);
    printf("\tscore_2      :%5.2f\n",p->score[1]);
    printf("\tscore_3      :%5.2f\n",p->score[2]);
    printf("\n")
}

---動的ストレージ割り当て----

配列の章では、配列の長さが事前に定義されており(直接説明されていませんが、間接的に配列の長さをコンパイラに通知します)、プログラム全体で固定されていることが紹介されました。動的配列タイプはC言語では許可されていません。

int a [10] = {0,1,2,3,4,5,6,7,8,9}; //長さを教えてください

char a []; //コンパイラは文字列の長さを自動的に計算し、[]を自動的に入力します。(したがって、配列は事前に定義され、その長さを指定する必要があります)

int a [n]; //これは不可能です。動的配列タイプはC言語では許可されていないためです。(変数を使用して長さを示し、配列のサイズを動的に説明したいのは誤りです。これは誤りです。コンパイラーは配列の大きさを知らないため、定義時にスペースを割り当てる方法を知りませんが、実際のプログラミングでは、必要なメモリスペースは実際の入力データに依存し、事前に決定できないことがよくあります。したがって、この種の問題の場合、アレイでこの問題を解決することは困難です(アレイはいくつを指定する必要があるため)長いデータ))

上記の問題を解決するために、C言語はいくつかのメモリ管理機能を提供します。これらのメモリ管理機能は、必要に応じてメモリスペースを動的に割り当てることができ、使用されなくなったスペースを再利用してメモリリソースを効果的に使用する手段を提供することもできます。

一般的に使用されるメモリ管理機能は3つあります。

(1)メモリスペースを割り当てる関数mallocおよびcalloc

(2)空きメモリスペース機能

----マロック機能

//函数原型为
void *malloc(unsigned int size)//返回一个空指针,参数是要申请的一个尺寸,尺寸是无符号整型
//其作用是在内存的动态存储区中分配一个长度为size的连续空间(size是一个无符号数)。
//此函数的返回值是一个指向分配域的(要分配的这个空间的)起始地址的指针(类型为void)。
//如果此函数未能成功的执行(例如存储空间不足),则返回空指针(NULL)表示0。

---- calloc関数

//函数原型为:
void *calloc(unsigned n,unsigned size);
//其作用是在内存的动态存储区中分配n个长度为size的连续空间。
//函数返回一个指向分配域起始地址的指针。
//如果分配不成功,返回NULL。
//用calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为size。
(,这种用法用的少,上个用法用的多,因为这种用法是结合数组来使用的)

----無料機能

//函数原型为void free(void *p);
//无返回值,将一个指针(void *p),这个指针就是你将要释放的,刚才申请的那个内存的地址
//起作用是释放由p指向的内存区,使这部分内存区能被其他变量使用
//p是最近一次调用calloc或malloc函数时返回的值
//free函数无返回值

----リンクリスト(これはデータ構造です)

リンクリストとは何ですか?

回答:リンクリストは、動的なストレージ割り当ての構造である、一般的で重要なデータ構造です。(だから、今、3つの機能について話します)

リンクリストの構成:

ヘッドポインタ:最初の要素を指すアドレスを格納します。

ノード:ユーザー(要素)が必要とする実際のデータとリンクノードのポインター。

凸面

上記のABCDは4つのノードで、ヘッドポインターの最初のポインターです。次に、機能が見つかります。最初のポインターはアドレスを格納し、アドレスは最初のデータを参照し、最初のデータは2つの部分、最初の部分を持ちます。これはデータの内容であり、他の部分は次のデータを指すアドレスです。次のデータには2つの部分があり、1つは内容で、もう1つは次のデータを指すアドレスです。次に、最後にない場合はNullを指します。これは、これを意味します。リンクリストは終了します。すると、このリンクリストは配列によく似ていることがわかります。配列のデータも1つずつ構成されており、リンクリストも隣り合っていますが、リンクリストと配列には違いがあります。リンクリストの1つの要素には2つのものが含まれています。 、1つは要素のコンテンツであり、もう1つは次の要素のアドレスです。この場合の最も単純な利点の1つは、リンクされたリストを動的に割り当てることができることです。動的に割り当てる方法は?1.一時的にEを追加し、このNullをEのアドレスに変更してから、Eを次のアドレスにポイントします。そうでない場合は、Nullになります。ある場合は、次のアドレスをポイントします。2。Bを一時的に削除して、Aを保存することもできます。アドレスはCを指しています(世界からBを直接蒸発させます)。3。AとCの間にBを追加できますか?はい。BのアドレスをCにポイントし、AのアドレスをBにポイントし、要素をBに割り当てました。これにより、AとCの間にBを挿入できるようになりました。したがって、リンクリストのデータ構造により、割り当てスペース全体がより柔軟になります。 。アレイに対する利点は、これらの側面に反映されています。

次の図に従ってリンクリストを作成してみてください

リンクリストには、3つのデータと3つのノードがあります。ソースコードは次のとおりです。

//由上图可以看到他有三个结点,每个结点都有元素(一个是number学号、还有score分数、还有地址(存放下一个的地址))
#include<stdio.h>
struct student
{
    long num;//长型存放学号
    float score;//分数
    struct student *next;//(相当于结构体里边有个结构体指针)//因为我们下一个指向的是跟他相同结构的也是student这个结构的一个数据,那我们这里就指向了一个指向结构的指针,指向student这个结构//这是一个指针,存放的地址就是下一个这样的结构,大家结构都是一样的只是位置不同//next这个指针变量,是一个结构体类型的所以它需要指向的是一个结构体(类比于 int *p;相当于int型的这个指针变量,需要指向的是int型的数据)
};
void main()
{
   struct student a,b,c,*head;//定义abc三个结点和一个head是一个结构体指针。
   a.num=10101;
   a.score=89.5;
   b.num=10103;
   b.score=90;
   c.num=10107;
   c.score=85;//赋值
 
   head=&a;//head是链表的头指针它必指向链表的第一元素,我们只需要知道这个链表的第一个元素在哪里,我们接着的第几个元素都可以索引到,因为我们接着的元素都会存放下一个元素的地址。//head里边存放的地址是指向第一个元素
   a.next=&b;//a指向b//因为是指针指向下一个地址
   b.next=&c;//b指向c
   c.next=null//c最后的地址就存放Null,表示链表结束
   do//使用do while依次打印出来,只要head不等于0
   {
       printf("%ld %5.1f\n",head->num,head->score);
       head=head->next;//第一次循环相当于head=a.next;而a.next=&b;//为什么每次执行的都是head,不是就一个head吗?head->a->b->c->Null。(因为我们这里并没有规定head必须指向头结点,所以head的指向是可以变化的)(它这里是把 head 当普通指针使用了,每次改变 head 的值,这样 head 就不指向头结点了,而是每次指向下一个节点)
   }while(head);//这个的意思就是while(head!=0);//while(head!=Null);//NUll就是0//他的地址的下一个值是有指向一个结构的,那么他就可以继续把他打印出来,直到最后一个,因为最后一个他是指向Null,所以这里head=head->next;这个head 是空的,那么整个程序结束。

}//通过链表索引分别将a元素,b元素,c元素打印出来。

---動的リンクリストを確立する

いわゆる動的リンクリストの確立とは、プログラムの実行中に最初からリンクリストを確立すること、つまり、ノードを1つずつ開き、各ノードのデータを入力すること(つまり、必要に応じてノードを動的に開いて削除すること、これを動的確立と呼びます)を指します。リンクリストは、アレイに対するリンクリストの優位性と適時性を具体化し、フロントリンクとバックリンクの関係を確立します(リンクリストは1つずつ接続されます)。

次の分析に基づいてプログラムを作成し、学生(学生番号、学年)データを含む一方向の動的リンクリストを作成します。

(慣例:学生IDが0でない場合は、それを入力します。学生IDが0の場合(リンクリストの確立が完了したことを意味します)、リンクリストの確立プロセスが完了したことを意味し、ノードはリンクリストに接続されるべきではありません)

以下のように分析します。

彼を最も単純なボックスフローチャートとして記述します。この図を分析するにはどうすればよいですか。描画するには、分析する円と三角形を描画します。順番に実行します(三角形はノードを示します)

head = Null、n = 0は、0に初期化することを意味し、コピーするだけです。

アルゴリズムの実装:

(1)入力p1-> numが0に等しくない場合、入力は最初のノード(n = 1)です。つまり、head = p1とします。つまり、p1の値をheadに割り当てます。つまり、headも新しいノードを指すようにします。開いたノードの場合、p1が指す新しく開いたノードが、リンクリストの最初のノードになります。(次に、この1つのデータのみがポイントできるため、3つのノードすべてを最初のデータにポイントする必要があります。)

(2)別のノードを開き、P1がそのノードを指すようにして、そのノードのデータを入力します。

(3)入力p1-> numが0に等しくない場合は、2番目のノード(n = 2)に接続し、新しいノードのアドレスを最初のノードの次のメンバーに割り当てます。

(4)次に、p2 = p1にします。つまり、p2が作成したノードを指すようにします。

図(図の説明)

最初のノードが作成されたばかりのとき、head、p1、およびp2はすべて最初のノードを指しています。(写真a)2番目のノードが表示されると、p1はブラントに突入し、p2を引っ張ります(写真b)。このとき、最初のノードのヘッドのみがそれを指し、最初のノードをガイドします。ノードの次のポインターは2番目のノードを指し、このステップが完了すると、3つのポインターすべてが2番目のノードを指します(図c)。この場合、3番目と4番目は同じです。プロセスがあります。

(5)別のノードを開き、p1がそのノードを指すようにして、ノードのデータを入力します。

(6)3番目のサイクルでは、n = 3(nは1に等しくない)であるため、p1の値はp2-> nextに割り当てられます。つまり、3番目のノードを2番目のノードに接続した後、そして、p2 = p1にし、p2が最後のノード(つまり、3番目のノード)を指すようにします。

次の図に示すように、3番目のサイクルで終了します。(終了方法、最後の次のポイントをNullにする)(3番目と2番目の手順を繰り返し、最初にp1をポイントし、次にp2を作成します。それを指し示す、それへの彼の次のポイント)

(7)新しいノードを開き、p1がそれを指すようにして、ノードのデータを入力します。p1-> numの値が0であるため、ループは実行されなくなり、この新しいノードはリンクリストに接続されるべきではありません。

(8)p2-> nextにNullを割り当てます。

(9)リンクリストを確立するプロセスはここで終了します。p1が指す最後のノードはリンクリストにリンクされていません。3番目のノードの次のメンバーの値はNullであり、どのノードも指していません。

次のプログラムもリンクリストの出力を実現しますが、どのように実現しますか?

まず、リンクリストの最初のノードのアドレスを知る必要があります。つまり、ヘッド値を知る必要があります。(住所がわかっている限り、リンクされたリスト全体を把握するのと同じです)

ポインタ変数pを設定し、最初のポイントを最初のノード(つまり、ヘッドノード)に設定し、pが指すノードを出力してから、pを1ノード戻します(この時点で、pはヘッド->次のポインタに移動します。次のノードに移動します)、リンクリストの接続されていないポイントまで出力します。したがって、次のフローチャートを取得できます

次のプログラムは動的入力と動的停止です

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

#define LENsizeof(struct student)//结构的大小
struct student *creat();//创建链表
void print(struct student *head)//打印链表(的函数)
struct student
{
    int num;
    float score;//分数
    struct student *next;
};
int n;//全局变量,用来记录存放了多少数据

void main()
{
   struct student *stu;
   stu=create();//12这个stu就是他的头结点,指针指向他的头结点
   print(stu);//然后我把这个头当成参数来print(是自定义的函数,实现的是打印这个链表)接收他的头
   printf("\n\n");
   system("pause");
}

struct student *creat()//creat创建函数
{
   struct student *head;
   struct student *p1,*p2;//在这里只是开辟了四个字节,说明这些指针是指向这个结构的,在内存并没有真正的创建整个结构那么大小的空出来。只是创建了一个指针,说它指向这么一个结构所以在2处就必须动态的给他初始化,给他获取相应的空间。LEN在上边通过宏定义,定义为这个student这个结构的大小
   p1=p2=(struct student *)malloc(LEN);//先定义要给他赋值,我们是动态创建链表,我们在上边声明的都是指针结构,都是指针,指针结构的话他只是开辟了一个四个字节,放一个指针地址//LEN是student结构的大小。3//让p1,p2同时指向我们刚申请的这个大小

   printf("Please enter the num:");
   scanf("%d",&p1->num);
   printf("Please enter the score:");
   scanf("%f",&p1->score);//4接着我们输入,输入都是指向的p1,p1首当其冲
   head=NULL;//head指向NULL
   n=0;//0个节点,还没有任何结点//5开始初始化
   while(p1->num)//6实现循环,当p1的num不等于0的时候,就是没有退出(while(0!=p1->num))
   {
      n++;//7不等于0就n++,表示出现了第一个结点(因为当p1的num成员为0时,就标志着我们整个程序结束,链表创建完成)
      if(1==n)//
      {
         head=p1;//8当n=1时head必须指向它p1,我们整个数据链的头结点指向它
      }
      else
     {
         p2->next=p1;
     }
     p2=p1//p2是跟在p1后边的
     p1=(struct student *)malloc(LEN);//9然后接着p1再次创建一个新的结点
   
     printf("\nPlease enter the num:");
     scanf("%d",&p1->num);
     printf("\nPlease enter the score:");
     scanf("%f",&p1->score);  //10这里要求输入第二个结点//只要输入的值不为0他就在while循环里边慢慢的循环,依次循环,当他在while处判断输入的值为0,到11
   }
   p2->next=NULL;//p2->next就指向NULL,我们刚申请的9到11之间的这个地方就废掉了,他就停留在内存的某个地方
   return head;//然后我们返回的值是head,head是我们整个链表的一个头结点的一个指针(也就是整个链表的领头羊,相当于我们数组的首地址,我们知道了这个数组的首地址,我们知道了数组依次索引,链表是同样的。)(我们之所以发明链表这个数据结构主要就是弥补数组的不足)//return返回的是指向我们创建完的链表的头
}
void print(struct student *head)//11定义接收头
{
   struct student *p;//12我们又定义了p指针,
   printf("\nThere are %d records!\n\n",n);
   p=head;//将头先指向了p
   if(head)//13如果head不等于0的话(if(NULL!=head))(什么都没有也就是一个空指针因为head是一个指针嘛)
   {
       do//14然后用while的形式把p输出出来
       { 
           printf("学号为%d的成绩是:%f\n",p->num,p->score);
           p=p->next;
       }while(p);//这里也是P不等于NULL的时候(while(NULL!=p))他就实现循环
   }

}

結果表示:

-----リンクリストの削除操作:

たとえば、次のリンクリストでBを削除したいとします。

(A-> B-> C-> D-> E-> H)

実際、それを直接削除して、AをCにポイントするのは非常に簡単です。

Bを削除した後にBを追加し直す場合は、AとCの間のリンクを解除し、Bを追加します(AをBに、BをCに再度ポイントします)。

(配列ABCDEの場合、Bを削除したい場合はどうすればよいですか?Bを直接削除すると、CはBの位置に移動し、DはCの位置に移動し、EはDの位置に移動します。次にBを再度挿入する場合入った場合は、Eを元の位置に戻し、Dを戻し、Cを戻し、次にBを入れる必要があります。したがって、これは、配列がリンクされたリストと同等である不便な場所です)

動的リンクリストからノードを削除しても、実際にはメモリからノードが消去されるわけではありません(リンクリストの次のポインタはノードのポインタを指しているため、アドレスにリンクするためだけにリンクリストの次のノードをリンクします。ポインタを変更するだけで、彼が消えたように感じます(消えたノードは、宇宙に捨てられた宇宙飛行士のようにどこに行きましたか、遅かれ早かれ隕石にぶつかるでしょう、そして彼は同じです、メモリ内のどこかが遅かれ早かれデータによって上書きされます。心配しないでください。実際にはメモリから消去されませんが))リンクリストから分離するには、元のリンク関係を元に戻します。 OK。

 

 

 

 

 

 

おすすめ

転載: blog.csdn.net/m0_37957160/article/details/108685364