コンテンツ
演習1:2つの整数変数の内容を交換する関数を作成する演習2:数値が素数であるかどうかを判断する関数を作成する
演習3:整数でソートされた配列の二分探索を実装する関数を記述します。
演習4.1:整数値(符号なし)を取得し、そのビットを順番に出力します
演習4.2:一時変数の作成を許可しない関数を記述し、文字列の長さを見つけます
4.3:nの階乗を見つけます(数式を知っている限り、それは再帰コードです)
4.4:n番目のフィボナッチ数を見つけます(最初の2つの数の加算は3番目の数に等しい)
1.1:ライブラリ関数
すべてのプログラマーは、移植性をサポートし、プログラムの効率を向上させるために、開発中にこれを使用できます。したがって、C言語の基本ライブラリは、一連の同様のライブラリ関数を提供します。これは、プログラマーがソフトウェアを開発し、開発効率を向上させ、コードを標準化するのに便利です。
1.2:カスタム関数
ライブラリ関数と同様に、カスタム関数には関数名、戻り値の型、および関数パラメーターがあります。しかし、違いは、これらはすべて私たちによって設計されているということです。これにより、プログラマーはプレイする余地がたくさんあります。
演習1:2つの整数変数の内容を交換する関数を作成する演習2:数値が素数であるかどうかを判断する関数を作成する
当实参传给形参的时候,形参是实参的一份临时拷贝,对形参的修改不会影响实参
void swap2(int* pa, int* pb)
{
int tmp = *pa;
*pa = *pb;
*pb = tmp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:a=%d,b=%d\n", a, b);
int* p1 = &a;
int* p2 = &b;
swap2(&a, &b);
printf("交换后:a=%d,b=%d\n", a, b);
return 0;
}
参照による呼び出しは、関数の外部で作成された変数のメモリアドレスを関数パラメーターに渡すことによって関数を呼び出す方法です。
パラメータを渡すこの方法により、関数は関数の外部の変数との実際の接続を確立できます。つまり、関数は関数の外部の変数を直接操作できます。
演習2:数値が素数であるかどうかを判断する関数を作成する
int is_prime(int n)
{
int j = 0;
for (j = 2; j <= n-1; j++)
{
if (n % j == 0)
{
return 0;
}
}
return 1;
}
int main()
{
//打印100~200之间的素数
int i = 0;
for (i = 100; i <= 200; i++)
{
//判断i是否为素数
if (is_prime(i) == 1)
{
printf("%d ", i);
}
}
return 0;
}
演習3:整数でソートされた配列の二分探索を実装する関数を記述します。
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;
}
2.1:ネストされた呼び出しと関数の連鎖アクセス
関数はネストされた状態で呼び出すことができ(たとえば、メイン関数は他の関数を呼び出す)、ネストされた状態で定義することはできません。
void test()
{
}
int main()
{
return 0; 可以
}
int main()
{
void test()
{
}
return 0; 不可以
}
連鎖アクセス(ある関数の戻り値を引数として別の関数に渡す)
int main()
{
//int len = strlen("abcdef");
//printf("len = %d\n", len);
上面两句代码可以写成:
printf("len = %d\n", strlen("abcdef"));
return 0;
}
2.2演習:次のコードの出力は何ですか
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
return 0;
}
回答:4321
説明:最初に43を出力します(printfの戻り値はint型であり、printfは印刷された文字数を返します)。次に、2,1を順番に返します。
3.1:関数の宣言+静的ライブラリのインポート
//int Add(int x,int y); //让编译器不再报警告,相当于告诉编译器我这里有一个Add函数
//返回类型int,函数名Add,函数参数int x,int y
int main()
{
int a = 10;
int b = 20;
int sum = Add(a, b);//函数的使用
printf("%d\n", sum);
return 0;
}
int Add(int x, int y)
{
return x + y;
}
Add関数はmain関数の後にあり、コンパイラーはmain関数からAdd関数を検出せず、警告を報告します。
コンパイラがレポートしないようにするには、メイン関数の前に関数宣言を記述する必要があります
新しいヘッダーファイルAdd.hを作成し、関数宣言をヘッダーファイルに入れることもできます
自分で作成したコードを公開したくない場合は、ソースファイルを静的ライブラリにコンパイルできます(ソースファイルを右クリック->プロパティ)
静的ライブラリはバイナリ情報を保存し、Add.libファイルとAdd.hファイルを見つけます
追加機能を使用して、自分で作成したソースファイルを公開しないでください。また、他のユーザーが使用できるようにします。
#include "add.h" //包含头文件
#pragma comment(lib, "add.lib")//导入静态库
同じことがライブラリ関数にも当てはまります
(あなたはそれを使うことを学びました!次に、コンパイラを書きます)
4.1:再帰と反復
再帰とは何ですか?
自分自身を呼び出すプログラムのプログラミングトリックは再帰と呼ばれます
通常、大規模で複雑な問題は、同様の小規模な問題に変換されて解決されます。
少量のコードは、複数の繰り返される操作を解決し、コードの量を減らします
再帰的な主な考え方:大きなものを小さくする
再帰に必要な2つの条件
1:制限があります。この制限が満たされると、再帰は続行されません。
2:再帰呼び出しを行うたびに、この制限にどんどん近づいていきます。
演習4.1:整数値(符号なし)を取得し、そのビットを順番に出力します
たとえば、1234、出力1 2 3 4
void print(int n)
{
if (n > 9) //1:存在限制条件,当满足这个限制条件的时候,递归便不再继续。
{
print(n/10); //2:每次递归调用之后越来越接近这个限制条件。
}
printf("%d ", n % 10);
}
int main()
{
unsigned int num = 0;
scanf("%d", &num);//1234
//解题步骤:
//1234%10=4 得到4
//1234/10=123
//123%10=3 得到3
//...
//[4 3 2 1] 得到4 3 2 1
//
print(num);//print函数可以把num的每一位按照顺序打印出来
return 0;
}
再帰
再帰の最後に戻る
演習4.2:一時変数の作成を許可しない関数を記述し、文字列の長さを見つけます
再帰、一般的な書き込みを使用しないでください
int my_strlen(char* str) { int count = 0;//统计字符的个数 while (*str != '\0') { count++; str++; } return count; } int main() { char arr[] = "abcd"; //char* str = arr; int len = my_strlen(arr); printf("%d\n", len); return 0; }
再帰を使用する
//my_strlen("abcdef") //1+my_strlen("bcdef") //1+1+my_strlen("cdef") //1+1+1+ my_strlen("def") //1+1+1+1+ my_strlen("ef") //1 + 1 + 1 + 1 +1+my_strlen("f") //1 + 1 + 1 + 1 + 1 + 1+ my_strlen("") //1 + 1 + 1 + 1 + 1 + 1 + 0 = 6 int my_strlen(char* str) { if (*str != '\0') return 1 + my_strlen(str+1); else return 0; } int main() { char arr[] = "abcd"; //char* str = arr; int len = my_strlen(arr); printf("%d\n", len); return 0; }
4.3:nの階乗を見つけます(数式を知っている限り、それは再帰コードです)
n <= 1の場合、n = 1;
n> 1の場合、n!= nx(n-1)
int fac1(int n)
{
if (n <= 1)
return 1;
else
return n * fac(n - 1);
}
int fac(int n)
{
int i = 0;
int ret = 1;
for (i = 1; i <= n; i++)
{
ret = ret * i;
}
return ret; //非递归,迭代
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = fac(n);
printf("%d\n", ret);
return 0;
}
4.4:n番目のフィボナッチ数を見つけます(最初の2つの数の加算は3番目の数に等しい)
递归
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;
}
必要なフィボナッチ数が非常に大きい場合、繰り返し計算のプロセスも非常に大きくなります。この場合、再帰アルゴリズムはあまり良くありません。50番目のフィボナッチ数を計算するには、コンパイラが長時間実行する必要があります。
F(n)を解くには、最初にF(n-1)とF(n-2)を計算し、F(n-1)とF(n-2)を計算し、F(n-3)とFを最初に計算する必要があります(n-4)。。。。。。以下同様に、F(1)とF(0)を最初に計算し、次に逆にしてF(n-1)とF(n-2)の結果を取得する必要があるため、F(n)は何度も繰り返す必要があります。計算される値は、時間の浪費を引き起こします。アルゴリズムの時間計算量は、Nの増加とともに指数関数的に増加し、時間計算量はO(2 ^ n)、つまり2のn乗です。
非递归
int fib(int n)
{
int a = 1;
int b = 1;
int c = 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(> 2)から計算を開始し、2つの数値F(n-1)とF(n-2)を使用して結果を計算します。これにより、多くの繰り返し計算が回避され、その効率は再帰よりもはるかに高速になります。アルゴリズムの場合、アルゴリズムの時間計算量はnに比例します。つまり、アルゴリズムの時間計算量はO(n)です。