エレメンタリーC言語-2。関数

        前回のブログでは、C言語の分岐とループについて触れましたが、この記事では、C言語の関数に関する知識を共有しますので、目を離さずによく見てください(笑)。

プレビュー

        関数に関して最初に頭に浮かぶのは数学関数です。数学の分野でのこの非常に興味深く実用的な知識は、ツールとも言えます。プログラミングの世界でも同じ効果がありますか?答えは「はい」でした。数学だけでなく、プログラミングでも非常に便利なツールなので、これ以上面倒なことはせずに、次のコンテンツを始めましょう。


コンテンツ

1. C言語の関数とは何ですか?

第二に、C言語関数の分類

        2.1ライブラリ関数

        2.2カスタム関数

第三に、関数のパラメータ

        3.1実際のパラメータ(実際のパラメータ):

        2.2正式なパラメータ(正式なパラメータ):

第四に、関数呼び出し

        4.1値による呼び出し

        4.2住所による電話

5.ネストされた呼び出しと関数の連鎖アクセス

        5.1ネストされた関数の呼び出し

        5.2チェーンアクセス

6、関数の宣言と定義

        6.1関数の宣言

        6.2関数の定義

 7、関数の再帰

                  7.1再帰のための2つの必須事項

         7.2再帰と反復

結論:


1. C言語の関数とは何ですか?

        次のテキストはBaiduからのものです。

       「C言語で定義された関数は、再利用して関数を独立して完成させることができるコードです。ユーザーから渡されたデータを受け取るかどうかはわかりません。ユーザーデータを受け取る関数は、定義時にパラメーターを指定する必要があります。ユーザーデータを受信しません。データを指定する必要はありません。これによると、関数はパラメーター化された関数とパラメーター化されていない関数に分けることができます。関数はカプセル化された再利用可能なコードの一部であり、プログラムをよりモジュール化して実行します。繰り返しコードをたくさん書く必要はありません。関数を事前に保存して一意の名前を付けることができ、名前がわかっている限りコードを使用できます。」

        次のコードは、関数を使用してメニューを印刷するだけです。簡単な紹介です。関数の使用方法とその形式については、後で説明します。(上記の段落は比較的長く、理解しにくいと感じています。私の理解に基づいて、以下で詳しく紹介します)        


 第二に、C言語関数の分類

        2.1ライブラリ関数

        まず、私たちが使用したいくつかのライブラリ関数を引用しましょう

そのような:

1)画面に「HelloWorld!」を印刷します。(printf)を使用します。

2)「HelloWorld!」に入りたいので、(scanf)を使用します

3)nのk乗を計算する場合は、(pow)を使用する必要があります。

        これらは私たちの一般的なライブラリ関数です。C言語は私たちが使用するための一連のライブラリ関数を提供します。

        最終的にライブラリ関数はいくつありますか?ここにWebサイトがあり、誰もがそれを知っています。

http://www.cplusplus.com/reference/ http://www.cplusplus.com/reference/

ここで使用するライブラリ関数はたくさんあるので、とても便利なウェブサイトです。

C言語で一般的に使用されるライブラリ関数は次のとおりです。
        IO機能、入力および出力機能。
        文字列操作関数;
        文字操作機能;
        メモリ操作機能;
        タイムスタンプなどの時刻/日付関数。
        累乗、平方根、絶対値などの数学関数。
        他のライブラリ関数など。

知らせ:

        ライブラリ関数を使用するには、 #include に対応するヘッダーファイルをインクルードする必要があります。


        2.2カスタム関数

 カスタム関数とライブラリ関数の違いは、達成したい効果に応じて独自の関数を設計できることです。

        カスタム関数の構成:
戻り型  関数名  (関数パラメーター);  (関数パラメーターも入力されます)(1つ以上の場合があります)

関数は最初に定義されてから呼び出されます。

例としてstrlen関数(文字列の長さを計算する)を取り上げましょう

size_tは符号なし整数を表し、文字列の長さを計算します。これは負の数ではありません。

const、文字列の長さを計算したいので、文字列を変更させることはできないので、constを使用して変更します

char*文字ポインタ

 次に、2つの数値の最大値を計算する関数を自分で作成してみましょう。

#include <stdio.h>
int Find_Max(int x, int y)
{
	if (x > y)
	{
		return x;
	}
	else
	{
		return y;
	}
    //还有一个更简单的写法,用三目操作符
    //return x > y ? x : y;
}
int main()
{
	int num1 = 0;
	int num2 = 0;
	scanf("%d %d", &num1, &num2);
	int max = Find_Max(num1, num2);
	printf("%d\n", max);
	return 0;
}


第三に、関数のパラメータ

        3.1実際のパラメータ(実際のパラメータ):

関数に渡される実際のパラメーターは、実際のパラメーターと呼ばれます。
引数には、定数、変数、式、関数などがあります。
数量のタイプに関係なく、実際のパラメーターは、関数が呼び出されたときにそれらの値を正式なパラメーターに渡すために、明確な値を持っている必要があります。

        2.2正式なパラメータ(正式なパラメータ):

正式なパラメーターは、関数が呼び出されたときにのみインスタンス化されるため、関数名の後の括弧内の変数を参照します
(メモリユニットを割り当てる)ので、正式なパラメータと呼ばれます。関数呼び出しが完了すると、正式なパラメーターは自動的に破棄されます。したがって、フォーム
数式パラメータは、関数内でのみ有効です。

2つの関数の最大値を見つけるための上記のコード、num1、num2は実際のパラメーター、x、yは正式なパラメーターです。

        モニターを見て、アドレス記号を取り、仮パラメーターと実パラメーターのアドレスを取り出して見てみましょう。明らかに、仮パラメーターと実パラメーターは2つの異なるアドレスを開くので、仮パラメーターは実際のパラメータの一時的なコピーに相当します


第四に、関数呼び出し

        4.1値による呼び出し

関数の仮パラメータと実際のパラメータはそれぞれ異なるメモリ空間を占有し、仮パラメータの変更は実際のパラメータに影響を与えません。 


        4.2住所による電話

参照による呼び出しは、関数の外部で作成された変数のメモリアドレスを関数パラメーターに渡すことによって関数を呼び出す方法です。
パラメータを渡すこの方法により、関数は関数の外部の変数との実際の接続を確立できます。つまり、関数は関数の内部で直接操作できます。
関数外の変数として。

次に、2つの整数変数の内容を交換する関数を記述します

void Swap1(int x, int y)
{
	int tmp = 0;
	tmp = x;
	x = y;
	y = tmp;
}

void Swap2(int* px, int* py) {
	int tmp = 0;
	tmp = *px;
	*px = *py;
	*py = tmp;
}
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	printf("交换前:a=%d,b=%d\n", a, b);
	//Swap1(a, b);
	Swap2(&a, &b);
	printf("交换后:a=%d,b=%d\n", a, b);
	return 0;
}

        ここでは2つの関数を記述しています。Swap1とSwap2を比較できます。

        また、正式なパラメータと実際のパラメータは2つの異なるメモリ空間を開くことにも言及しました。Swap1を値渡し呼び出しとして解釈するために、実際のパラメータを変更せずに正式なパラメータのみを交換し、正式なパラメータは一時的なものにすぎません。実際のパラメーターのパラメーターをコピーします。関数が呼び出されると、正式なパラメーターが自動的に破棄されるため、Swap1は目的の関数を実行できません。

        Swap2は実パラメータのアドレスを渡し、関数パラメータ部分はポインタ変数(ポインタ変数はストレージアドレス)によって受信されます。これは参照による呼び出しです。仮パラメータと実パラメータは同じメモリ空間を使用します。これにより、関数と関数を作成できます。外部変数は実際の接続を確立します。つまり、関数の外部の変数は、関数の内部で直接操作できます。

上の図から、aとpxのアドレスが同じであり、bとpyのアドレスが同じであることがわかります。


関数を使用してさまざまな問題を実装するコードをいくつか書いてみましょう

(1)

//打印100~200之间的素数
#include <stdio.h>
#include <math.h>
int is_prime(int n)
{
	//2~n-1的数试除
	//2~sqrt(n)的数试除
	int j = 0;
	for (j = 2; j <= sqrt(n); j++)
	{
		if (n % j == 0)
		{
			return 0;
		}
	}
	return 1;
}
int main()
{
	int i = 0;
	for (i = 100; i <= 200; i++)
	{
		//判断是否为素数
		//返回值为1,表示是素数
		//返回值为0,表示不是素数
		if (is_prime(i) == 1)
		{
			printf("%d ", i);
		}
	}
	return 0;
}


 (2)

//写一个函数判断是不是闰年
#include <stdio.h>
int Is_leap_year(int n)
{
	//判断闰年的规则:
	//(1)能被4整除,但不能被100整除
	//(2)能被400整除
	if (n % 4 == 0 && n % 100 != 0 || n % 400 == 0)
	{
		return 1;
	}
	return 0;
    //简洁方法
    //return (((n % 4 == 0) && (n % 100 != 0)) || (n % 400 == 0));
}
int main()
{
	int y = 0;
	scanf("%d", &y);
	int ret = Is_leap_year(y);
	//如果是闰年,就返回1
	//如果不是闰年,就返回0
	if (ret == 1)
	{
		printf("%d年是闰年\n", y);
	}
	else
	{
		printf("%d年不是闰年\n", y);
	}
	return 0;
}


 (4)

//写一个函数,实现一个整形有序数组的二分查找
//找到了就返回下标
//找不到就返回-1
#include <stdio.h>
int binary_search(int arr[], int k, int sz)
{
	int left = 0;
	int right = sz - 1;
	while (left <= right)
	{
		int mid = left + (right - left) / 2;
		if (arr[mid] < k)
		{
			left = mid + 1;
		}
		else if (arr[mid] > k)
		{
			right = mid - 1;
		}
		else
		{
			return mid;
		}
	}
	return -1;
}
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int k = 0;
	scanf("%d", &k);//要查找的数
	int sz = sizeof(arr) / sizeof(arr[0]);
	int ret = binary_search(arr, k, sz);
	if (-1 == ret)
	{
		printf("找不到了\n");
	}
	else
	{
		printf("找到了,下标为:%d\n", ret
		);
	}

	return 0;
}

 


5.ネストされた呼び出しと関数の連鎖アクセス

        機能と機能は、実際のニーズに応じて組み合わせることができます。つまり、相互に呼び出すことができます。

        5.1ネストされた関数の呼び出し

#include <stdio.h>
void new_line()
{
	printf("Hello\n");
}
void three_line()
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		new_line();
	}
}
int main()
{
	three_line();
	return 0;
}
注:  関数はネストされた状態で呼び出すことができますが、ネストされた状態で定義することはできません。

ネストされた定義:

 ですから、これは間違っていることを忘れないでください。


        5.2チェーンアクセス

ある関数の戻り値を別の関数の引数として使用します。
これは通常のコードです

 これは連鎖アクセスのコードです

典型的な連鎖アクセスコードがあります

 画面に4321が表示されますが、これはなぜですか?

        これは、printf関数の戻り値です。つまり、これらの各関数は、印刷された文字数を返すか、エラーが発生した場合は負の値を返します。つまり、内側から外側に向かって、最初の印刷43、43は2文字、次の印刷2、2は1文字、最後の印刷1、最後に4321を画面に印刷します。


6、関数の宣言と定義

        6.1関数の宣言

1.コンパイラーに、関数の名前パラメーター、および戻り値のタイプを伝えます。しかし、それが存在するかどうかは、関数宣言によって決定されません。
2.関数の宣言は、通常、関数を使用する前に表示されます。使用前に宣言を満たすため
3.関数宣言は通常、ヘッダーファイルに配置されます。

        6.2関数の定義

関数の定義は、関数の特定の実装を参照し、関数の関数の実装について説明します。

        次のコードは上記とは異なります。上記のコードはカスタム関数をメイン関数の上に配置しますが、このように記述されている場合は警告が報告されます。

 したがって、上記の宣言が最初に使用されることに注意する必要があります。(ただし、正しくても、以下のように書かれることはあまりありません)

 


 7、関数の再帰

プログラムがそれ自体を呼び出すプログラミング手法は、再帰と呼ばれます。
        アルゴリズムとしての再帰は、プログラミング言語で広く使用されています。定義または仕様のプロシージャまたは関数には、それ自体を直接または間接的に呼び出すメソッドがあります。これは通常、大きくて複雑な問題を、解決する元の問題と同様の小さな問題に変換します。

再帰的戦略:問題解決プロセスに必要な繰り返し計算を記述するために使用できるプログラムは少数であるため、プログラムのコード量が大幅に削減されます。
再帰についての主な考え方は次のとおりです。大きなものを小さくする

簡単に言えば、関数の再帰はそれ自体を呼び出す関数です

        7.1再帰のための2つの必須事項

(1)制限があり、この制限が満たされると、再帰は続行されません。
(2)各再帰呼び出しの後にこの制限にどんどん近づいていく

例として:

//接收一个整型值(无符号),按照顺序打印它的每一位
void print(int n)
{
	if (n > 9)
	{
		print(n / 10);
	}
	printf("%d ", n % 10);
}
int main()
{
	unsigned int num = 0;
	scanf("%d", &num);//1234
	print(num);//print函数可以把num的每一位按照顺序打印出来

	return 0;
}

この再帰的なプロセスに慣れるために、簡単な図を描いてください。

 

        スタック領域については、ここで説明します。プログラムが関数を呼び出すたびに、メモリのスタック領域にメモリスペースが開かれます。たとえば、今のコードでは、n=1234のときにスペースが開かれます。制限を設定しない場合、および再帰が進むにつれて、この制限に近づくことなく、再帰が繰り返され、デッド再帰の状況が形成されますが、スタック領域のスペースが形成されます。は制限されており、無制限の開発はスタックオーバーフローを引き起こします。次の図では、上記のコードを使用して簡単に説明しています。

 


         7.2再帰と反復

再帰についても上記で紹介しましたが、反復については単純にループとして理解できます。

次にいくつかの例を示します。

(1)

//用递归计算n的阶乘
int fac(int n)
{
	if (n <= 1)
	{
		return 1;
	}
	else
	{
		return n * fac(n - 1);
	}
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = fac(n);
	printf("%d\n", ret);

	return 0;
}


(2)

//用递归求第n个斐波那契数。(不考虑溢出)
int fib(int n)
{
	if (n <= 2)
	{
		return 1;
	}
	else
	{
		return fib(n - 1) + fib(n - 2);
	}
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = fib(n);
	printf("%d\n", ret);

	return 0;
}

         これは再帰法で書かれていますが、再帰法には小さな問題があります.40番目のフィボナッチ数を計算したい場合、40番目のフィボナッチ数が計算されるため、すぐには画面に表示されません。比較的大量の計算が必要であり、コンピュータでさえしばらくの間それをしなければなりません。

上図に示すように、40番目のフィボナッチ数を計算するには、40から始めて、最初の数まで数える必要があります。50番目のフィボナッチ数の場合は、さらに時間がかかります。

しかし、再帰的な方法がない場合、効率を改善するためのより良い方法はありますか?

int fib(int n)
{
	int a = 1;
	int b = 1;
	int c = 0;
	int i = 0;
	if (n <= 2)
	{
		return 1;
	}
	while (n > 2)
	{
		c = a + b;
		a = b;
		b = c;
		n--;
	}
	return c;
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = fib(n);
	printf("%d\n", ret);

	return 0;
}

        上記のコードは再帰的方法を使用していませんが、n番目のフィボナッチ数を計算することもでき、コードの効率が大幅に向上しています。n番目から最初までを計算する再帰的方法と比較して、この非再帰的方法は計算します。 1番目からn番目の方法まで非常に速いので、知識は鮮明です。今日再帰を学ぶと、他の方法を放棄するとは言えません。それでも、最善の解決策を見つける必要があります。解決策。


結論:

        C言語機能に関する知識を共有し、このブログをここに書いています。ブログの気持ちを振り返ると、それでもとてもいいです。毎日がとても充実していると感じています。次のブログを楽しみにしています。 。(はは、花の終わり)

 

 

おすすめ

転載: blog.csdn.net/m0_64607843/article/details/122540152