C言語 - 関数(1)

こんにちは、また会いましょう。今日は関数の話です。最初のC言語入門では、関数の基本的なポイントをお話ししました。今度は関数を再理解して、より深いレベルで関数をマスターしましょう。学習を始めましょう今日。

数学で遭遇する関数は 1 次元と 1 次元であり、そのほとんどは f(x)=ax+b ですが、C 言語の関数はどうでしょうか? Bar を見てみましょう。
1. 関数とは何ですか?

Wikipedia からの関数の定義: サブルーチン

  • コンピューター サイエンスでは、サブルーチン (英語: Subroutine、プロシージャ、関数、ルーチン、メソッド、
    サブプログラム、呼び出し可能ユニット) は、1 つまたは複数のステートメント ブロックで構成される、大きなプログラム内のコードの特定の部分です
    これは特定のタスクを完了する責任を負い、他のコードからは比較的独立しています。
  • 一般に、入力パラメータと戻り値があり、プロセスのカプセル化と詳細の非表示を提供します。これらのコードは多くの場合、ソフトウェア ライブラリとして統合されます。

2. C言語の関数の分類

  • ライブラリ関数

C 言語に配置された関数を指します。これらの関数は、誰もが使用できるようにコードを統一するために他の人によってよく使用されます。これを使用するときは、相対ヘッダー ファイルをインクルードするだけで済みます。

1. IO 関数
2. 文字列操作関数
3. 文字操作関数
4. メモリ操作関数
5. 時刻/日付関数
6. 数学関数
7. その他のライブラリ関数

上記は私たちがよく使うライブラリ関数ですので、ライブラリ関数を確認できるウェブサイトをいくつか紹介
ます。 com (中国語)


疑問がある場合は、ここを参照してください。そのため、ライブラリ関数があります。

簡単な例を挙げると、printf を入力して scanf を出力するように、使用するたびにコードを使用して置き換える必要がある場合、C 言語で直接使用できるようになり、効率が大幅に向上します。

ここに画像の説明を挿入

これはヘッダー ファイルの一部です。ライブラリ関数を使用したい場合は、ヘッダー ファイルを追加する必要があります。たとえば、最も単純な #include では、入力関数と出力関数を使用する必要があります。

#include<stdio.h>
int main()
{
    
    
    printf("你们真帅啊!");
	return 0;
}

以上のことを理解した上で、機能の習得とツールの使いこなし方を教えていきます。
ここに画像の説明を挿入

上記は MSDN からの小さなツールです。ダウンロードして使用することもできます。
ここに画像の説明を挿入
ここに画像の説明を挿入
これは含める必要があるヘッダー ファイルです。

これを見ると、この関数に何を入力するかだけはわかりますが、それが何に使用されるかはわかりません。現時点では、次のようなメモが表示されます
ここに画像の説明を挿入
ここに画像の説明を挿入

ここに画像の説明を挿入

上記がすべて英語であることを見て、私たちは皆頭が痛むかもしれませんが、編集者は、英語が苦手だからといってコードをうまく入力できないわけではないことをみんなに思い出させます。私たちは皆、辞書を引くことができます。ここで、編集者は次のことを行います。みんなで頑張ってね

備考に終端の null 文字を含めることは、文字を渡すときに '\0' を含めることも渡されることを示していることがわかります。

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

void main( void )
{
    
    
   char string[80];
   strcpy( string, "Hello world from " );//将Hello world from放入string,并且from后面有\0
   strcat( string, "strcpy " );
   strcat( string, "and " );
   strcat( string, "strcat!" );
   printf( "String = %s\n", string );
}
//String = Hello world from strcpy and strcat!这是输出



そういえば、みなさんも私たちが使っているツールについては少しは理解しているはずなので、今後、他の人の記事を読んで知らない機能が出てきたら、こうやって調べて活用することもできます。ツールも研究に役立ちます。

  • 2つのカスタム関数

上で非常に多くのヘッダー ファイルを見ましたが、これは多くのライブラリ関数があることを意味しますが、なぜこれほど多くの関数があるのか​​というと、ライブラリ関数がニーズを満たすことができるのであれば、私たちは将来プログラマーになるため、自分で関数を作成する必要があります。そうすればプログラマーは必要ありません。

ret_type fun_name(para1, * )
{
    
    
 statement;//语句项
}
//ret_type 返回类型
//fun_name 函数名
//para1    函数参数

これが関数の構成です。コードを入力して関数を作成しましょう。関数の目的は、2 つの数値のうち大きい方の値を見つけることです。

#include<stdio.h>
int max(int x, int y)
{
    
    
	if (x > y)
		return x;
	else
		return y;
}
int main()
{
    
    
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);//输入两个数
	int c = max(a, b);
	printf("%d\n", c);
	return 0;
}

これは、最初の C 言語入門でも触れた、より大きな値を比較するための簡単なコードですが、今度はより難しい例に移ります。

2つの数字を交換する

void swap(int x, int y)//不需要返回值用void
{
    
    
	int tmp = 0;
	tmp = x;
	x = y;
	y = tmp;

}
int main()
{
    
    
	int a=10;
	int b = 20;
	printf("交换前:a=%d b=%d\n",a,b);
	swap(a, b);
	printf("交换后:a = % d b = % d\n", a, b);
	return 0;
}

ここに画像の説明を挿入

どういうことだ、機能交換に交換を明記したのに、まだ交換してくれない、なぜだ!
その理由は、プログラムにバグがあるためです。このコードを詳しく説明しましょう。

ここに画像の説明を挿入

デバッグ プロセス中に、a、b の値、および a と b のアドレスを確認できます。次に関数を入力すると、
ここに画像の説明を挿入
関数の値が同じであり、デバッグ中に xy も同じであることがわかります。重要な点は、アドレスが異なることがわかるため、関数内での交換は a と b の値にまったく影響を与えないことです。

ここで結論を導き出します。実パラメータが仮パラメータに渡される場合、仮パラメータは実パラメータの一時的なコピーにすぎず、仮パラメータの変更は実パラメータには影響しません。上記のコードでは、実パラメータは a
とb はメイン関数の関数であり、仮パラメータは呼び出す関数です。

上記の考え方とデバッグの結果を踏まえて、学習したポインタについて考えてみませんか? 以下は正しいコードです。

#include<stdio.h>
void swap(int* x, int* y)//不需要返回值用void
{
    
    //接收地址用*
	int tmp = 0;
	tmp = *x;
	*x = *y;
	*y = tmp;

}
int main()
{
    
    
	int a=10;
	int b = 20;
	printf("交换前:a=%d b=%d\n",a,b);
	swap(&a, &b);
	printf("交换后:a = % d b = % d\n", a, b);
	return 0;
}

コードを実行するとき、関数へのポインタを渡すので、アドレスの内容 *x *y は a と b の値を交換し、コードの効果を達成できます。

3つの関数パラメータ

  • 実パラメータ (実パラメータ)

実際に関数に渡されるパラメータを実パラメータと呼びます。
実際のパラメータは、定数、変数、式、関数などです。
実際のパラメータがどのような種類の量であるかに関係なく、関数が呼び出されるとき、それらの値は仮パラメータに転送できるように、明確な値を持っている必要があります。

  • 仮パラメータ(パラメータ)

仮パラメータは関数名の後の括弧内の変数を指します。これは、仮パラメータは関数が呼び出されたときにのみインスタンス化される (メモリ単位が割り当てられる) ため、仮パラメータと呼ばれます。仮パラメータは、関数呼び出しが完了すると自動的に破棄されます。したがって、仮パラメータは関数内でのみ有効です

上記のコードでは、ab は実際のパラメータ、xyは仮パラメータ
4 の関数呼び出しです。

  1. 値による呼び出し

関数の仮パラメータと実パラメータは異なるメモリ ブロックを占有し、仮パラメータを変更しても実パラメータには影響しません。

  1. 住所で電話をかける

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

知識を定着させるためにいくつかの演習を行ってみましょう

  1. 数値が素数かどうかをチェックする関数を作成します。
#include<stdio.h>
#include<math.h>//sqrt要包含的头文件
int su_shu(int n)
{
    
    
	int x = 0;
	for (x = 2; x <= sqrt(n); x++)
	{
    
    
		if (n % x == 0)
		{
    
    
			return 0;
		}
	}
	return 1;
}
int main()
{
    
    
	int n = 0;
	scanf("%d", &n);
	if (su_shu(n)==1)
	{
    
    
		printf("是素数:%d\n", n);
	}
	return 0;
}

上記の関数は素数かどうかを判定する関数です 数値を入力したら素数かどうかを判定する関数を入力します sqrtを使うのは数学の平方根に相当します インクルードするヘッダファイルは<math.h>.最大の素数を見つけてループし、その結果と根号が割り切れるかどうかを確認します。割り切れない場合は、その数値が素数であることを意味します。1 を返します。その後、メイン関数の if ステートメントに戻り、素数を判定して出力します。

  1. 年がうるう年かどうかを確認する関数を作成します。

ここに画像の説明を挿入
以上が閏年かどうかの判断基準です。では、このコードを書いてみましょう

#include<stdio.h>
int run_nian(int y)
{
    
    
	if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0)
	{
    
    
		return 1;
	}
	else
		return 0;
}
int main()
{
    
    
	int year;
	scanf("%d", &year);
	if (run_nian(year) == 1)
	{
    
    
		printf("%d\n", year);
	}
	return 0;
}

このプログラムでは閏年であれば出力し、閏年でなければ出力しないようにしています 閏年の関数を入れています 閏年の基準に達した場合は 1を返します満たされない場合は 0 を返しますが、これは大きいため、戻り値が 1 の場合、数値が出力されると、この数値はうるう年であることを意味します。

  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)
		{
    
    
			right = mid - 1;
		}
		else if (arr[mid] < k)
		{
    
    
			left = 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 (ret == -1)
	{
    
    
		printf("找不到\n");
	}
	else
	{
    
    
		printf("找到了,下标是%d", ret);
	}
	
	return 0;
}

このプログラムでは、関数に配列、検索する番号、配列の番号を渡すので、関数の添字もわかります。関数が処理された後、中間結果が得られます。正の数または 0 を返す場合、それはこの数値の添字である可能性があるため、-1 は返されません。このような機能により二分探索機能が実現される。

二分探索を書くとき、int sz = sizeof(arr) / sizeof(arr[0]);それを main 関数に入れなければなりません。なぜ、関数に入れると、渡されるアドレスはポインタ変数である最初の文字のアドレスになり、コンパイルではデバイスがオンになります。 32 ビット プラットフォーム、つまりバイト サイズが 4 であるため、sz は 1 と計算されます。 したがって、配列がパラメータとして渡される場合、渡されるのは配列全体ではなく、そのアドレスです。最初の要素。この場合、結果はあなたがそれを見つけることができ、自分で試してデバッグして結果を確認することができます。

  1. 関数が呼び出されるたびに num の値を 1 ずつ増やす関数を作成します。
//写一个函数,没调用一次,num加1
#include<stdio.h>
void test(int* p)
{
    
    
	*p = *p + 1;

}
int main()
{
    
    
	int num = 0;
	test(&num);
	printf("%d\n", num);
	test(&num);
	printf("%d\n", num);
	test(&num);
	printf("%d\n", num);
	return 0;
}

5. 関数のネストされた呼び出しとチェーンアクセス

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

  1. ネストされた呼び出し
#include <stdio.h>
void new_line()
{
    
    
 printf("hehe\n");
}
void three_line()
{
    
    
    int i = 0;
 for(i=0; i<3; i++)
   {
    
    
        new_line();
   }
}
int main()
{
    
    
 three_line();
 return 0;
}

main関数に入ると、何度もthree_line();遭遇して、入力して、最後に結果を実行しますが、次のコードのような定義をネストすることはできませんvoid three_line() new_line();void new_line() printf("hehe\n");

int main()
{
    
    
 void test()
{
    
    

}
 return 0;
}

関数内で関数を定義することはできません

  1. チェーンアクセス
#include <stdio.h>
int main()
{
    
    
    printf("%d", printf("%d", printf("%d", 43)));
    
    //注:printf函数的返回值是打印在屏幕上字符的个数
    return 0;
}

これはチェーンアクセスの例です。このコードを分析してみましょう

まず、printf 関数について知る必要があります。この図では、 printf 関数の戻り値は画面に表示される文字数であるという
ここに画像の説明を挿入
Remark の文がわかります。
ここに画像の説明を挿入
最初の出力は 43 で、その後 43 は 2 文字なので 2 が出力され、次の 2 は文字で 1 が出力され、結果は 4321 になります。
ここに画像の説明を挿入

ということで、関数の基本と使い方は大体理解できたので、その他の関数の内容については後ほど理解していきますので、今日はここまでです、ありがとうございました!

おすすめ

転載: blog.csdn.net/2301_76895050/article/details/131436685