C言語の救世主サイドストーリー(Windowsの実用的なデバッグスキル)

注:Linux開発環境のデバッグツールはgdbです。これは、ブログの後半で紹介されます。

コンテンツ

プログラマーが習得するための重要なテクニックは、デバッグを学ぶことです

1.1デバッグとは何ですか?それはどれほど重要ですか?

1.2試運転の基本的な手順

2.1デバッグとリリース

2.2以下のプログラムのデバッグバージョンとリリースバージョンの違い

2.3ショートカットキーの使用方法

2.4質問の例:153が指数であるかどうかを知りたいのですが、愚かでF10を押し続けていますか?i = 100000の場合、どのくらい時間がかかりますか?

 3.1デバッグによるコードの問題の特定

実装コード:1を要求してください!+2!+3!... + n!;オーバーフローは考慮されません。(以下のコードにエラーがあります)

3.2良い(デバッグしやすい)コードの書き方 

3.3ライブラリ関数のシミュレーション:strcpy(文字列コピー)

my_strcpy関数は、関数への連鎖アクセスを実現するために値型char*を返すように設計されています

4.1Const演習の説明

5.1プログラミングでよくある間違い

5.2コンパイルエラーは構文エラーです

5.3チェーンエラー 

5.4ランタイムエラー-デバッグで解決されたエラー(階乗の例)


プログラマーが習得するための重要なテクニックは、デバッグを学ぶことです

一部の女性はWindowsに似ていますが、非常に優れていますが、セキュリティリスクが大きすぎます。 
UNIXのような女性もいます。彼女はとても上手ですが、誰もがプレイできるわけではありません。 
一部の女性はC#と同じくらい美しいですが、家事はできません。 
一部の女性はC++のようです、彼女はあなたのために静かにたくさんのことをします。 
何人かの女性はJAVAのようです、彼女はほんの少しの給料でどこでもあなたに仕えます。 
一部の女性はJAVAスクリプトに似ていますが、彼女には注意を払っていますが、最終的には結果が得られません。 
面倒なこともありますが、編集のような女性もいますが、頼まなければならないこともあります。 
一部の女性はSQLに似ており、彼女はあなたの成長に大いに役立ちます。 
愛は終わりのないサイクルであり、一度実行されると、それに陥ります。 
誰かと恋に落ちることはメモリリークであり、あなたはそれを解放することはできません。 
あなたが本当に誰かと恋に落ちるとき、それは一定であり、決して変わることはありません。 
ガールフレンドは、私のクラスだけが呼び出すことができるプライベート変数です。 
恋人はポインターです、それを使うときあなたは注意を払わなければなりません、さもなければそれは巨大な災害をもたらすでしょう。


1.1デバッグとは何ですか?それはどれほど重要ですか?

起こったことすべての痕跡がなければなりません。明確な良心があれば、覆い隠す必要はなく、兆候はありません。良心があれば、覆い隠し、兆候がなければなりません。しるし、つるをたどるのが簡単ですそして上記のように、これは推論の方法です。この道を下るのは犯罪であり、上流に行くのは真実です。

 デバッグ(英語:デバッグ/デバッグ)は、デバッグとも呼ばれ、コンピュータープログラムまたは電子機器のプログラムエラーを見つけて減らすプロセスです。


1.2試運転の基本的な手順

プログラムエラーの存在を見つける

分離、排除などによってエラーを特定します。

エラーの原因を特定する

間違いを訂正するための解決策を提案する

プログラムエラーを修正して再テストする


2.1デバッグとリリース

デバッグは、デバッグバージョンと呼ばれることが多く、デバッグ情報が含まれ、最適化が行われないため、プログラマーはプログラムを簡単にデバッグできます。

リリースはリリースバージョンと呼ばれ、多くの場合、プログラムをコードサイズと実行速度で最適化するためにさまざまな方法で最適化されているため、ユーザーはそれをうまく使用できます。(デバッグできません)

2.2以下のプログラムのデバッグバージョンとリリースバージョンの違い

#include <stdio.h>
int main()
{
    int i = 0;
    int arr[10] = {0};
    for(i=0; i<=12; i++)
   {
        arr[i] = 0;
        printf("hehe\n");
   }
    return 0;
}

デバッグバージョンでは、プログラムは無限にループします。リリースバージョンでは、プログラムは実行できます。

その理由は、変数がメモリに作成される順序が変更され、プログラムの実行結果に影響を与えるためです。

実行中のコードの結果:高い確率は無限ループです

まず、arrには10個の要素しかありませんが、iは12回ループし、arr [10]は範囲外でアクセスされています。まず、iとarrはローカル変数であり、最初にiを作成し、次にarrを作成します。変数はスタックに配置されますエリアでは、スタックエリアの使用習慣は、最初に上位アドレスを使用し、次に下位アドレスを使用することです。

iが最初に初期化され、それを一番上に置き、arrが配列である場合、配列の添え字の拡張アドレスは低から高に増加します。配列が範囲外の場合、アドレスへの境界を越える可能性があります。のiを1回入力し、iとarrのアドレスを入力します。配列のアドレスが0に変更され、forループが計算を再開します。バイト数がわからないため、結果が無限ループになる可能性が高いのはなぜですか。 arr配列とiのアドレスの間にありますが、arr配列が範囲外の場合、iにアクセスすると無限ループが発生します

arrとiを逆にすると、スタック領域の使用習慣も原因になります。iがポスト初期化されている場合、iは低アドレス空間であるため、arrの範囲外アクセスはiのアドレスにアクセスしません。印刷後にプログラムがクラッシュします。

真ん中の空きスペースの量は、コンパイラ独自の書き込み方法の問題です。たとえば、他のプラットフォームに移動すると、空きスペースが異なる場合があるのに、なぜ無限ループが発生し、エラーが報告されないのでしょうか。プログラムが実行されているため、forループは停止していません。arrは範囲外でアクセスされますが、エラーを報告する前にプログラムを実行する必要があり、1つのことを完了する必要があります。


	int main()
	{
		int i = 0;
		int arr[10] = {1,2,3,4,5,6,7,8,9,10};
		printf("%p\n", &i);
		printf("%p\n", &arr[9]);
		}
 

可以佐证,栈区空间使用习惯先使用高地址,再使用低地址

 


2.3ショートカットキーの使用方法

1.F5でデバッグを開始します

使用方法:F5を直接押してプログラムを終了します。単独では使用できません。F9(ブレークポイント)と一緒に使用する必要があります。

(F5を複数回押すと、元の論理ブレークポイントで停止します。たとえば、ループが1回から2回に変わり、次のループブレークポイントで停止します)、F5を押しても応答しないことに気付く人もいますが、 Fn+F5またはFn+F10を追加する必要があります

2.Ctrl + F5はデバッグせず、コードを直接実行します

3.F9ブレークポイントの設定/キャンセル

使用方法:コードを実行し、F5キーを押して、ブレークポイントF9に遭遇したときにのみ停止します

4. Ctrl+Fでキーワードを検索します

5. Ctrl + K + Cでコメントを追加(すべて選択で使用)Ctrl + K+Uでコメントを解除

6. C言語でのコピーでは、コピーを選択する必要はありません。コピーする必要がある行でCtrl+Vを押すだけです。

さらに、より多くのショートカットキーがあります。ブログは以下に追加されています

VSで一般的に使用されるショートカットキー-MrLiskyのブログ-CSDNブログ-VSショートカットキー

一部の学生は、[デバッグ]-> [ウィンドウ]を開いたときに、監視が見つからなかったことに気付きました。なぜですか?

これは、監視中の多くのウィンドウがデバッグ開始時にのみ表示されるためです。監視はコードと連携してエラーをチェックするために使用され、より多くの練習が必要です。


2.4質問の例:153が指数であるかどうかを知りたいのですが、愚かでF10を押し続けていますか?i = 100000の場合、どのくらい時間がかかりますか?


int main()
{
	int i = 0;
	for (i = 0; i <= 100000; i++)
	{
		int tmp = i;
		int n = 1;
		//第一步判断是几位数
		while (tmp / 10)
		{
			n++;
			tmp = tmp / 10;
		}
		//计算每一位次方和
		tmp = i;
		int sum = 0;
		while (tmp)
		{
			sum += pow(tmp % 10, n);
			tmp = tmp / 10;
		}
		//3.判断
		if (i == sum)
		{
			printf("%d ", i);
		}
	}
	return 0;
}

F5を使用してF9と連携し、F9ブレークポイント->条件(条件付きブレークポイント)を右クリックして、i == 153(条件付き設定)と入力し、この時点でF5を押すと、152ビットをスキップして移動することがわかります。直接153に


2.5 F10(ステップバイステップで、関数に遭遇すると、関数に入らず、完全な関数コンテンツを直接実行します)

  F11(ステートメントごとのステートメント。関数に遭遇すると、関数に入り、コードのすべての詳細を実行します)

int Add(int a, int b)
{
	return a + b;
}

int main()
{
	int a = 1;
	int b = 2;
	int ret = Add(a,b);
	printf("%d", ret);
	return 0;
}

機能追加に到達するまでF10、F11を押して機能に入り、監視ウィンドウで使用します。監視と自動監視(非推奨)に加えて、自動監視はすべての変数を解放するのに役立ちます。監視は簡単です。指定された番号エラー、モニターで必要な番号を監視する必要があります

メモリモニタリング


呼び出しスタック、関数呼び出しロジックが複雑な場合は、スタックの呼び出しロジックを表示できます

void test2()
{
	printf("hehe\n");
}
void test1()
{
	test2();
}
void test()
{
	test1();
}
int main()
{
	test();
	return 0;
}

 3.1デバッグによるコードの問題の特定

実装コード:1を要求してください!+2!+3!... + n!;オーバーフローは考慮されません。(以下のコードにエラーがあります)

int main()
{
 int sum = 0;//保存最终结果
 int n = 0;
 int ret = 1;//保存n的阶乘
 scanf("%d", &n);
 for(int i=1; i<=n; i++)
 {

 for(int j=1; j<=i; j++)//n的阶乘
 {
 ret *= j;
 }
 sum += ret;
 }
 printf("%d\n", sum);
 return 0;
}


//1!=1
//2!=2
//3!=6
//9
3つ欲しい!、3を入力しますが、結果が15であることがわかります。コードに問題があるはずなので、F10を押してデバッグを開始し、監視で監視する変数を入力します。

最初の実行、

コードに問題はありません、1!は1です

 

2回目の処刑、2!2ですが、実行を続けますが、ret値は2であることがわかります。retの役割は1 * 2 * ... * nの役割であり、ret値が変更され、結果は当然間違っています。

int main()
{
 int sum = 0;//保存最终结果
 int n = 0;
 int ret = 1;//保存n的阶乘
 scanf("%d", &n);
 for(int i=1; i<=n; i++)
 {
   ret = 1;
 for(int j=1; j<=i; j++)//n的阶乘
 {
 ret *= j;
 }
 sum += ret;
 }
 printf("%d\n", sum);
 return 0;
}

3.2良い(デバッグしやすい)コードの書き方 

1.コードは正常に実行されます2.バグはほとんどありません3.高効率4.高い読みやすさ5.高い保守性6.明確なコメント7.完全なドキュメント

一般的なコーディング手法:

1.assertを使用する

2.constを使用してみてください

3.優れたコーディングスタイルを開発する

4.必要なコメントを追加します

5.コーディングの落とし穴は避けてください。

3.3ライブラリ関数のシミュレーション:strcpy(文字列コピー)

元のポインターが指すコンテンツを宛先スペースポインターが指すスペースにコピーし、\0を過去にコピーします

 

10ポイントのうち、このコードは5ポイントを与えて失敗しますが、彼は私たちが達成したい文字列コピーを完了することもできます

void my_strcpy(char* dest, char* src)//将src空间处的字符拷贝到目标空间dest
{
	while (*src != '\0')
	{
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src;//再把\0拿下来
}

int main()
{
	char arr1[] = "hello bit";
	char arr2[20] = "xxxxxxxxxxx";
	my_strcpy(arr2,arr1);

	return 0;
}

改善方法?まず、文字列のコピーが2回行われますが、最適化できますか

投稿++に変更できます

void my_strcpy(char* dest, char* src)
{
	while (*src != '\0')
	{
		*dest++ = *src++;
	}
	*dest = *src;
}
assertはassertに使用されます。渡す2つのポインタがNULLであり、アプリケーションがアサーションなしで実行されると、メモリに不正にアクセスし、コードが危険にさらされます。
上記のコードはまだ十分に単純化されていません。2つの文を組み合わせることができますかdest=src、
#include <assert.h>

void my_strcpy(char* dest, char* src)
{
	//assert(dest != NULL);//断言
	//assert(src != NULL);//断言

	assert(dest && src);//优化断言,一个为假(NULL)便报错

	while (*dest++ = *src++)//先执行src,src指向hello bit,由于++是后置,先使用再++
                            //h被*dest内容复制,表达式结果是h的ASCII码值
                            // \0的ASCII码值为0,先执行完\0 = x,表达式不成立结束循环
	{
		;
	}
}

次に、strcpyの公式バージョンの紹介を見ていきます

char * strcpy( char * strDestination const char * strSource );

my_strcpy関数は、関数への連鎖アクセスを実現するために値型char*を返すように設計されています

また、コードエラーを回避するために、srcターゲットスペースの変数を変更できない(定数変数になる)ことができないように、constを追加する必要があります。

完璧な機能デザイン!

char* my_strcpy(char* dest, const char* src)
{
	assert(dest && src);//断言
	char* ret = dest;
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

int main()
{
	char arr1[] = "hello bit";
	char arr2[20] = "xxxxxxxxxxx";
	char* p = NULL;

	printf("%s\n", my_strcpy(arr2, arr1));

	return 0;
}

4.1Const演習の説明

Constはコードをより堅牢にすることですが、constを使用して、必要なコードを正確に制限するにはどうすればよいでしょうか。

int main()
{
	int num = 10;
	num = 20;//第一种方法改num变量值
	int* p = &num;
	*p = 100;//指针方法改变num变量值

	return 0;
}

constを追加するとどうなりますか?

int main()
{
    const int num =0;//常变量,不可被修改
    num = 20;  //编译器不通过
    int * p =&num;
    *p = 20;//成功改掉,编译器通过,证明我们可以使用指针来去改被修饰的常变量
    printf("%d\n", num);//20 这种操作破坏了const,本意不改却被投机取巧改掉了
	return 0;
}

ドアをロックしてドアから入るのを防いだが、窓を壊して飛び込んだ。入ったのに違法だった

int main()
{
    const int * p =&num;
    *p = 20;//X 窗户也给你封死
    int * const p =&num;
    p = 20 ; //X  门封死
    const int * const p =&num;//X 窗户, 门封死也给你封死
	return 0;
}

constはポインタを変更できます:
    constは*の左側に配置されます(const int * p;)
    constは* pを変更します。つまり、pが指すオブジェクトはpで変更できませんが、p変数のアドレスは変更できます。変更されました
    。constはに配置されます*(int * const p;)constの右側
    はpを変更します。つまり、pの内容は変更できませんが、pが指すオブジェクトはpを介して変更できます。


 

5.1プログラミングでよくある間違い

5.2コンパイルエラーは構文エラーです

int main()
{
   return 0     //漏掉了;  在编译过程中报错,没有运行起来
}

 解決策:エラーメッセージを直接見て(ダブルクリック)、問題を解決します。または、経験を積んで行うこともできます。比較的簡単です。

5.3チェーンエラー 

//没有引头文件
//int Add(int x, int y)//或者没有定义Add函数
//{
	//return x + y;
//}

int main()
{
	int a = 10;
	int b = 20;
	
	int c = add(a, b);//函数名写错了
	
	printf("%d\n", c);

	return 0;
}

 

解決策:エラーメッセージを確認し、主にコードのエラーメッセージで識別子を見つけてから、問題を特定します。通常、識別子名が存在しないか、スペルが間違っています。

5.4ランタイムエラー-デバッグで解決されたエラー(階乗の例)


C言語の基礎知識は終わりました!

おすすめ

転載: blog.csdn.net/weixin_63543274/article/details/123756664