C言語:エッセイ4

1.関数パラメーターと実際のパラメーターの説明:

(1)関数の定義時に指定された正式なパラメーター。関数呼び出しがない場合、それらはメモリー内のストレージユニットを占有しません。関数呼び出しが発生した場合にのみ、関数の正式なパラメーターにメモリー単位が割り当てられます。呼び出しの終了後、正式なパラメータによって占有されていたメモリユニットも解放されます。

(2)実際のパラメーターは、定数、変数、またはmax(3、a + b)などの式にすることができますが、特定の値が必要です。実際のパラメーターの値は、呼び出し中に正式なパラメーターに割り当てられます(割り当てられます)。

(3)定義された関数では、仮パラメーターのタイプを指定する必要があります。

(4)実際のパラメーターのタイプは、同じであるか、割り当てに互換性がある必要があります。

(5)C言語では、実際のパラメーターから正式なパラメーターへのデータ転送は「値の転送」(COPYと同等)の一方向転送であり、正式なパラメーターから実際のパラメーターに戻されるのではなく、実際のパラメーターのみが正式なパラメーターに渡されます。メモリ内では、実際のパラメータ単位と正式なパラメータ単位が異なります。

PS:関数が呼び出されると、ストレージユニットが正式なパラメータに割り当てられ、実際のパラメータに対応する値が正式なパラメータに渡されます。呼び出しが完了すると、正式なパラメータユニットが解放され、実際のパラメータユニットは元の値を保持および維持します。関数が呼び出されたときに、正式なパラメーターの値が変更されても、呼び出し元の関数の実際のパラメーターの値は変更されません。(変更がある場合は、後でアドレスを使用します。これがアドレスのアドレスです。これにより、彼のメモリが変更されます。つまり、アドレスまたは参照が過去に渡されます)(現在は、値によって渡されます。コピーするだけで、値の値が渡されます)

2.関数の戻り値:

(1)関数の戻り値は、関数のreturnステートメントを介して取得されます。

returnステートメントは、呼び出された関数の特定の値を呼び出し元の関数に戻します

1つの関数に複数のreturnステートメントが存在する可能性があります。どのreturnステートメントが実行され、どのステートメントが機能するか。

式が最終的に正確な値を計算できる限り、戻り値は値または式にすることができます。

C言語では、型指定のないすべての関数は、積分型に従って自動的に処理されます。

3.関数宣言の役割。

宣言の機能は、関数名、関数パラメーターの数、関数タイプなどの情報をコンパイルシステムに通知することです。これにより、関数呼び出しが発生したときに、コンパイルシステムは関数を正しく識別し、呼び出しが有効かどうかを確認できます。(たとえば、関数名が正しいかどうか、実際のパラメーターのタイプと数が同じかどうか)

関数の「定義」と「宣言」は同じものではありません。関数の定義とは、関数の関数の確立を指します。関数名、関数値のタイプ、正式なパラメーターとそのタイプ、関数本体などを含み、完全で独立した関数ユニットです。(したがって、関数宣言はメモリを占有しません、彼の定義はメモリです)

呼び出し元の関数の前に関数定義が表示されている場合は、それを宣言する必要はありません。

C言語はネストされた関数を定義できませんが、ネストされた関数を呼び出すことはできます。

4.再帰

関数を呼び出すプロセスでは、関数自体が直接または間接的に呼び出されます。これは、関数の再帰呼び出しと呼ばれます。

5.配列は関数パラメーターとして使用されます。

配列は関数パラメーターとして使用できます。関数パラメーターとして使用されるデータ転送配列には、次の2つの形式があります。

(1)1つは、実際のパラメーターとして配列要素(添え字変数)を使用することです。

配列要素は添え字変数(a [1]は2番目の要素)であり、通常の変数と区別がつかないため、関数パラメーターとしての使用は通常の変数とまったく同じです。関数呼び出しが発生すると、次のように解釈されます。実際のパラメータの配列要素の値が正式なパラメータに転送され、一方向の値の転送が実現されます。

(2)もう1つは、関数の正式なパラメーターおよび実際のパラメーターとして配列名を使用することです。

PS:関数パラメーターとして配列名を使用することと、実際のパラメーターとして配列要素を使用することには、いくつかの違いがあります。

1):配列要素を実際のパラメーターとして使用する場合、配列タイプが関数の仮パラメーター変数のタイプと一致している限り、添え字変数としての配列要素のタイプも関数の仮パラメーター変数のタイプと一致します。したがって、そうではありません。必要な関数の正式なパラメーターも添え字変数です。つまり、配列要素の処理は通常の変数として扱われます。

2)配列名を関数パラメーターとして使用する場合、仮パラメーターと対応する実パラメーターは同じタイプの配列である必要があり、明確な配列記述が必要です。仮パラメーターと実パラメーターが矛盾している場合は、エラーが発生しました。

配列名を渡す場合、実際のパラメーターと正式なパラメーターは配列でなければならないのはなぜですか?

配列名は、実際には一連の配列変数の最初の要素のアドレスです。リーダーのアドレスはポインターと同等です。ポインター変数にはアドレスも格納されます。

3)通常の変数または添え字変数が関数パラメーターとして使用される場合、正式なパラメーター変数と実際のパラメーター変数は、コンパイラシステムによって割り当てられる2つの異なるメモリユニットです。関数が呼び出されたときに発生する値の転送は、実際のパラメーター変数の値を正式なパラメーター変数に割り当てることです。

配列名を関数パラメータとして使用する場合、値は転送されません(ただし、アドレス(アドレス)は転送されます)。つまり、実際のパラメータグループの各要素値は、形状パラメータグループの各要素に割り当てられません。

どうして?

理由:実際には、形状パラメーターグループは存在せず、コンパイラシステムは形状パラメータグループにメモリユニットを割り当てません。(それでは、引数変数が彼を要素に変えるとき、彼はそれにユニットを割り当てることができるが、それをアドレスとして扱い、メモリを割り当てないという前になぜですか?)

では、データ送信はどのように実現されますか?

前述のように、アレイ名はアレイの最初のアドレスです。したがって、配列名を関数パラメータ(実際のパラメータ)として使用した場合に実行される転送は、アドレス(配列の最初の要素のアドレス)(最初の要素のアドレスを渡す)、つまり実際のパラメータの転送のみです。グループの最初のアドレスには、パラメーターグループの名前が割り当てられます。(この場合、アドレスを取得した後で彼がそこにいることがわかるので、配列全体を渡す必要はありません。直接使用します)

形状パラメータグループ名が最初のアドレスを取得した後は、実際の配列(同じ配列)を持つことと同じです。実際、形状パラメータグループと実際のパラメータグループは同じ配列であり(同じメモリ空間を指しているため)、メモリ空間を共有します。

#include<stdio.h>

void test(int b[10])
void main()
{
    int a[10]={2,4,6,8};//数组的地址的连续的
    test(a);//传一个地址过去,因为数组名就是数组的首地址
    putchar("\n")
}
void test(int b[10])//int一个 b数组接收到地址,可以说b数组也是指向了首地址
{
   int i=0;
   for(;i<10;i++)
   {
      printf("%d",b[i])//再打印
   }
}

ストレージについては、次の図を参照してください。

5.1字型パラメータグループの長さは定義されていません

つまり、上記のbは長さを定義していません。つまり、配列タイプとして定義し、それが配列タイプであることをコンパイラに通知するだけで済みます。それで問題ありません。コンパイラは決して配列の大きさを通知する必要はありません。配列を定義すると、元の配列を指すだけで、元の配列のサイズはそのままになります。(または、array [2]またはarray [20]と書くこともできます。彼はそれを配列に向けただけで、新しい配列を再定義しませんでした。)

#include<stdio.h>

void test(int array[])//不给他一个大小
void main()
{
    int a[10]={2,4,6,8};//数组的地址的连续的
    test(a);//传一个地址过去,因为数组名就是数组的首地址
    putchar("\n")
}
void test(int array[])//array不定义长度
   int i=0;
   for(;i<10;i++)
   {
      printf("%d",b[i])
   }
}

6.正式なパラメーターもローカル変数です(ローカル変数はスタックに格納されます)。(関数内の変数は呼び出し後に自動的に破棄されますが、グローバル変数はプログラム全体が完全に終了するまで待機してから終了する必要があります(異なる関数は同じローカル変数名を使用できますが、名前は同じですがメモリスペースが異なります)の。)

可変ストレージカテゴリ:

6.1変数のストレージカテゴリ

変数のスコープの観点から(つまり、スペースの観点から)、グローバル変数(異なるスコープを持つグローバル変数のスコープはファイル全体)とローカル変数(ローカル変数のスコープは関数全体)に分割できることは誰もが知っています。次に、変数値の存在時間(つまり、存続期間)の観点から、静的ストレージと動的ストレージに分けることができます。

キーワードautoは省略可能です。autoが記述されていない場合は、動的ストレージモードに属する「自動ストレージカテゴリ」として暗黙的に設定されます。

関数内のローカル変数の値は、関数呼び出しの終了後に消えないが、元の値を保持する、つまり、それによって占有されていたメモリユニットが解放されない場合があります。次に関数が呼び出されたとき、変数の既存の値は最後の関数呼び出しの終了時の値です。値。このとき、ローカル変数は「静的ローカル変数」として指定し、キーワードstaticで宣言する必要があります。

次の例は、次のことを示しています。

#include <stdio.h>
int f(int a)//a传给它的是2//相当于a=2赋值过去(两个a在不同的函数,位置不同,此处的a你定义为d也可以将main函数中的a传过来的)
{
   auto int b=0;//局部变量存放的是在栈这个位置,
   static int c=3;//而静态变量存放在数据区这个位置
   b=b+1;//b==1,1,1
   c=c+1;//c==4,5,6
   return(a+b+c);//返回3次7,8,9(第二次调用f函数的时候,b重新赋值为0(因为b不是static型),而c因为是static是静态的,上一次函数调用完之后没有销毁保留了第一次的4,所以第二次加1就是5)
}
void main()
{
    int a=2,i;
    for(i=0;i<3;i++)
    {
        printf("%d\n",f(a));
    } 
    
}

(1)静的ローカル変数は静的ストレージカテゴリに属します。ストレージユニットは静的ストレージ領域に割り当てられ、プログラムの実行期間全体にわたって解放されません。そして、動的ストレージはスタックに格納されます。スタックの機能は、関数を呼び出すたびに、OK、スタックを生成することです。この関数を呼び出した後、OK、スタックはシステムによって自動的にリサイクルされます(これは穏やかに来て穏やかに歩くことはメモリの一部を奪うことはないので、スペースを占有しませんが、静的なものはプログラム全体が破壊されるまで永遠にそこにとどまるので、静的な定義がもっとあると、より多くを消費します羊)。

(2)動的変数(つまり動的ローカル変数)は動的ストレージカテゴリに属し、動的ストレージ領域のスペースを占有しますが、静的ストレージ領域のスペースは占有せず、関数呼び出しの終了後に解放されます。

(3)静的ローカル変数の場合、初期値はコンパイル時に割り当てられます。つまり、初期値は1回だけ支払われます。プログラムの実行時には、すでに初期値があります(上記のcなど)。シングルステップデバッグの場合は、ブレークポイントをに設定します。 forループでは、f関数を初めて入力しない場合、cの値はすでに3になっています。実行中は、cの割り当てはスキップされますが、この時点ではbの値はありません。f関数を入力すると、次のように実行されます。 b、bに値0)が割り当てられている場合、将来関数が呼び出されるたびに初期値が再割り当てされることはありませんが、最後の関数呼び出しの終了時の値は保持されます。

自動変数に初期値を割り当てる場合、コンパイル時ではなく、関数が呼び出されたときに行われます。関数が呼び出されるたびに、割り当てステートメントの実行と同等の初期値が再度与えられます。

(4)ローカル変数の定義時に初期値が割り当てられていない場合、静的変数の場合、コンパイル時に初期値0(数値変数の場合)または空文字(文字変数の場合)が自動的に割り当てられます。自動変数の場合、初期値が割り当てられていない場合、その値は不確定な値になります。一般的に、それは非常に大きな負の数です。これは、各関数呼び出しが終了した後にストレージユニットが解放され、次の呼び出しが行われたときにストレージユニットが再度割り当てられ、割り当てられたユニットの値が不確実であるためです。

(5)静的ローカル変数は呼び出し後も存在しますが、他の関数から参照することはできません。(彼は静的であるため、彼は彼にのみ属します)

通常の状況では、変数の値(静的ストレージと動的ストレージを含む)はメモリに保存されます。実行する場合は、次のようなプロセスを実行する必要があります。

 

プログラムで変数の値を使用すると、コントローラーはメモリー内の変数の値を算術ユニットに送信するように命令を出し、算術ユニットを介して操作を実行します。数値を格納する必要がある場合は、算術ユニットからデータを送信します。メモリストレージ。

一部の変数が頻繁に使用される場合(たとえば、関数がループで10,000回実行され、各ループでローカル変数を参照する必要がある場合)、変数の値にアクセスするのに長い時間がかかります。実行効率を向上させるために、C言語ではローカル変数の値をCPUのレジスターに配置し、レジスターから直接取り出して直接使用すると、メモリー内でアクセスしなくても操作に参加できます。レジスタのアクセス速度はメモリのアクセス速度よりもはるかに速いため、実行効率を向上させることができます。この種の変数はレジスタ変数と呼ばれ、キーワードregisterで宣言されます。

#incluede<stdio.h>
int fac(int n)
{
   register int i;f=1;//把f和i都定义为寄存器,让他存储在寄存器,而不存储在内存
   for(i=1;i<=n,i++)
    {
       f*=i;
    }
   return (f)
}
void main()
{
   int i;
   for(i=1;i<=5;i++)
      {
          printf("%d!=%d\n",i,fac(i))
      }
}
//但是不建议把所有的变量都变成寄存器,因为CPU的容量空间是有限的,所以他里边的寄存器也是有限的

(6)externは外部変数を宣言します

外部変数はグローバル変数であり、その範囲は変数の定義からプログラムファイルの終わりまでです。このスコープでは、グローバル変数はプログラム内のさまざまな関数によって参照でき、外部変数はコンパイル中に静的ストレージに割り当てられます。外部変数の範囲を拡張するために、externを使用して外部変数を宣言する必要がある場合があります。

#include<stdio.h>
int max(int x,int y)
{
    int z;
    z=x>y?x:y;
    return(z);
}
void main()//程序首先从main开始他extern了A,B(意思是告诉我们这个A和B的两个变量是全局变量),我们如果去掉他就看了,因为他是在下边定义的。如果是在上边定义他就看到了,因为编译器是从上往下读的。而读到此出还没有读到,所染A和B是全局变量因为他定义在各个函数之外。但是我们仍需要用extern声明一下,告诉他他是全局变量,只是你现在还没有看到他而已。这时候就能够顺利的调用他。(否则使用是在定义之前就不能编译)
{
    extern a,b;//可以尝试一下去掉extern关键字是什么后果
    print("%d\n",max(A,B))
}
int A=13,B=-6;

さらに進むには、マルチファイルプログラムで外部変数を宣言します

前述のすべてのプログラムは、.cサフィックスが付いたファイルで実行される1つのプログラムで実行されます。しかし、実際、私たちが遭遇したプログラムや既製のプログラムはすべて、非常に多くのファイルで構成されていますプログラム内では自由にプログラムを開くことができます内部ソースファイルは多くのファイルで構成されています各ファイルは基本的にプログラムの機能の一部を実現し、次にさまざまな機能を実現しますそれをまとめると、ファイルを複数のファイルに分割して、すべてこのプログラムに接続する方法を学ぶ必要があります。

プログラムを複数のファイルに分割します。

ファイルにAを定義します。次に、それを別のファイルで使用する場合は、A //を外部に移動して、Aが定義済みの外部変数であることを示す必要があります。

staticを使用して外部変数を宣言します(ローカル変数を宣言すると、このローカル変数は動的ストレージではなくなり、各関数が返されるためにキャンセルされなくなり、常にメモリに保存されます。プログラム全体が終了するまで、静的を使用してローカル変数を宣言する場合、つまり、内部変数またはローカル変数を宣言すると、ローカル変数は静的変数になります。)静的を使用して外部変数を宣言するとどうなりますか?

プログラムの設計では、特定の外部変数がこのファイルでのみ参照でき、他のファイルでは参照できないことが望まれる場合があります。このとき、外部変数を定義するときに静的ステートメントを追加できます(つまり、静的を追加して、他のファイルが参照できないようにします)。 )。

変数の宣言と定義について:

変数の場合、宣言と定義の関係は少し複雑です。宣言部分に表示される変数には、2つの状況があります。1つは内部ストレージスペースを確立する必要がある場合(int a;など)、もう1つはストレージスペースを確立する必要がない場合(extern a;など)(extern a;など)です。メモリスペースを確立する必要があります。これは、aなどの定義がすでに存在することをコンパイラに通知するだけです。)次に、前者は確定宣言または定義と呼ばれ、後者は参照宣言と呼ばれます(宣言だけが定義ではありません)。ストレージスペースを確立します)。

一般に、説明の便宜上、ストレージスペースを確立するステートメントを定義と呼び、ストレージスペースを確立する必要のないステートメントをステートメントと呼びます。明らかに、ここで言及されているステートメントは狭い、つまり、定義されていないステートメントです。

PS:要約すると:

(1)スコープ(つまりスコープ)の観点から、ローカル変数とグローバル変数があります。彼らが使用するストレージカテゴリは次のとおりです。

ローカル変数(つまり、この関数のみが表示されます) 自動変数auto、つまり動的ローカル変数(関数を終了すると、値は消えます)
静的ローカル変数static(関数を終了し、値は残ります)
レジスター変数(関数を離れると、値が消えます)(それは、それが格納されている場所、レジスターです)
(正式なパラメーターは自動変数またはレジスター変数として定義できますが、静的変数として定義することはできず、エラーが報告されます)
グローバル変数(最外層に表示されます) 静的外部変数(このドキュメントでのみ参照)
外部変数
(つまり、非静的外部変数であり、他のファイル参照を許可します)

(2)変数が存在する時間(存続期間)から、動的ストレージと静的ストレージの2つのタイプがあります。静的ストレージはプログラムの実行時間全体(プ​​ログラム全体が停止すると消えます)であり、動的ストレージは関数が呼び出されたときの一時的な割り当て単位です(動的ストレージは関数全体が終了すると消えます)。 )。(ストレージ範囲が異なるため、つまり、メモリ内の異なる場所にとどまるため、ストレージ時間が異なります。静的ストレージと同様に、データセグメントに配置され、動的ストレージはスタックに配置されます。セグメントなので、一時的に消えます)

動的ストレージ 自動変数(この関数で有効)
レジスタ変数(この関数で有効)
正式なパラメータ(この関数で有効)
静的ストレージ 静的ローカル変数(関数で有効)
静的外部変数(このドキュメントで有効)
外部変数(他のファイルは引用できます)

(3)変数値の保存場所とは区別され、次のように分類できます。

メモリ内の静的ストレージ領域 静的ローカル変数
静的外部変数(関数外の静的変数)
外部変数(他のファイルから参照できます)
メモリ内の動的ストレージ領域(つまり、スタック領域) 自動変数と正式なパラメータ
CPUに登録する レジスタ変数

範囲と寿命に関しては、前者は空間的観点から、後者は時間的観点からです。

(4)静的には、ローカル変数とグローバル変数のスコープが異なります。

ローカル変数の場合、彼は変数を動的ストレージから静的ストレージに変更します。グローバル変数の場合、変数をローカル(このファイルに対してのみローカル)にしますが、それでも静的ストレージです。スコープの観点からは、静的宣言のスコープは制限されており、この関数(静的ローカル変数)またはこのファイル(静的外部変数)に制限されています。

------内部機能と外部機能----

関数は別の関数から呼び出す必要があるため、本質的にグローバルですが、他のファイルから関数を呼び出せないように指定することもできます。

関数は、他のソースファイルから呼び出すことができるかどうかに応じて、内部関数と外部関数に分けられます。

このファイル内の他の関数からのみ関数を呼び出すことができる場合、それは内部関数と呼ばれます。

内部関数を定義するときは、関数名と関数タイプの前にstaticを追加します。これは;

即static类型标识符   函数名(形参表)
如static int fun(int a,int b)

(1)関数を定義するときに、キーワードexternが関数ヘッダーの左端に追加されている場合、それはこの関数が外部関数であり、他のファイルから呼び出すことができることを意味します。たとえば、関数の最初の部分はextern int fun(int a、int b)と書くことができます。

このようにして、関数funを他のファイルに対して呼び出すことができます。C言語では、関数を定義するときにexternを省略した場合、それは暗黙的に外部関数であると規定されています。

おすすめ

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