História paralela do salvador da linguagem C (habilidades práticas de depuração do Windows)

Nota: A ferramenta de depuração para o ambiente de desenvolvimento Linux é gdb, que será apresentada posteriormente no blog.

contente

Uma técnica importante para os programadores dominarem é aprender a depurar

1.1 O que é depuração? Quão importante é?

1.2 Etapas básicas de comissionamento

2.1 Depuração e Liberação

2.2 A diferença entre as versões Debug e Release dos seguintes programas

2.3 Como usar as teclas de atalho

2.4 Exemplo de pergunta: Eu quero saber se 153 é um expoente. Você é burro e continua pressionando F10, quanto tempo leva quando i=100000?

 3.1 Identifique problemas de código por depuração

Código de implementação: peça 1! +2! +3! ...+ n! ; overflow não é considerado. (há um erro no código abaixo)

3.2 Como escrever código bom (fácil de depurar) 

3.3 Simule a função da biblioteca: strcpy (cópia de string)

A função my_strcpy foi projetada para retornar o tipo de valor char* para obter acesso encadeado à função

4.1 Explicação do Exercício Const

5.1 Erros comuns na programação

5.2 Erros de compilação são erros de sintaxe

5.3 Erros de encadeamento 

5.4 Erros de tempo de execução - Erros resolvidos com depuração (exemplo de fatorial)


Uma técnica importante para os programadores dominarem é aprender a depurar

Algumas mulheres são como o Windows, embora muito boas, mas os riscos de segurança são muito grandes. 
Algumas mulheres são como UNIX, ela é muito boa, mas nem todos sabem jogar. 
Algumas mulheres são tão bonitas quanto C#, mas não conseguem fazer as tarefas domésticas. 
Algumas mulheres são como C++, ela silenciosamente fará muitas coisas por você. 
Algumas mulheres são como JAVA, ela irá atendê-lo em todos os lugares com apenas um pequeno pagamento. 
Algumas mulheres são como scripts JAVA, embora tenham cuidado com ela, ainda assim não apresentam resultados no final. 
Algumas mulheres são como uma compilação, embora seja problemática, mas às vezes você tem que pedir. 
Algumas mulheres são como SQL, e ela será de grande ajuda no seu desenvolvimento. 
O amor é um ciclo sem fim, uma vez executado, cai nele. 
Apaixonar-se por alguém é um vazamento de memória, e você nunca pode liberá-lo. 
Quando você realmente se apaixona por alguém, isso é constante e nunca mudará. 
Namorada é uma variável privada que somente minha classe pode chamar. 
O amante é o ponteiro, você deve prestar atenção ao usá-lo, caso contrário, trará um grande desastre.


1.1 O que é depuração? Quão importante é?

Deve haver vestígios de tudo o que acontece. Se você tem a consciência limpa, não precisa encobrir e não haverá sinais. Se você tem consciência, deve encobrir, e deve haver sinais. Quanto mais sinais, mais fácil é seguir a videira E acima, esta é a maneira de raciocinar. Descer por este caminho é um crime, subir a corrente é a verdade.

 Depuração (inglês: Depuração / Depuração), também conhecido como depuração, é um processo de encontrar e reduzir erros de programa em programas de computador ou instrumentos eletrônicos.


1.2 Etapas básicas de comissionamento

Encontrar a existência de erros de programa

Localize erros isolando, eliminando, etc.

Determine a causa do erro

propor soluções para corrigir erros

Corrija os erros do programa e teste novamente


2.1 Depuração e Liberação

A depuração é muitas vezes referida como a versão de depuração, que contém informações de depuração e não faz otimizações, tornando mais fácil para os programadores depurar programas.

A versão é chamada de versão de lançamento e geralmente é otimizada de várias maneiras para tornar o programa ideal em tamanho de código e velocidade de execução, para que os usuários possam usá-lo bem. (não é possível depurar)

2.2 A diferença entre as versões Debug e Release dos seguintes programas

#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;
}

Na versão de depuração, o programa faz um loop infinito; na versão de lançamento, o programa pode executar

O motivo é que a ordem em que as variáveis ​​são criadas na memória mudou, o que afeta o resultado da execução do programa.

O resultado da execução do código: a alta probabilidade é um loop infinito

Em primeiro lugar, arr tem apenas 10 elementos, mas i faz um loop para 12 vezes, e arr[10] foi acessado fora dos limites. Primeiro, i e arr são variáveis ​​locais, primeiro crie i e depois crie arr, e porque local as variáveis ​​são colocadas na pilha Na área, o hábito de uso da área da pilha é usar primeiro o endereço alto e depois usar o endereço baixo

Se i for inicializado primeiro, coloque-o no topo e arr for um array, mas o endereço de crescimento do subscrito do array cresce de baixo para alto. Se o array estiver fora dos limites, ele pode cruzar o limite para o endereço de i uma vez, e coloque o endereço de i e arr O endereço do array é alterado para 0, e o loop for reinicia o cálculo. Por que você diz que o resultado provavelmente será um loop infinito, porque você não saber quantos bytes existem entre os endereços do array arr e i, mas se o array arr estiver fora dos limites para acessar o i causará um loop infinito

Colocar arr e i em sentido inverso também é devido aos hábitos de uso da área da pilha. Se i for pós-inicializado, i é o espaço de endereço baixo, então o acesso fora dos limites nunca acessará o endereço de i, então o programa irá travar após a impressão.

A quantidade de espaço vazio no meio é uma questão do próprio método de escrita do compilador.Por exemplo, se você for para outras plataformas, o espaço vazio pode ser diferente, então por que há um loop infinito e nenhum erro é relatado? Como o programa está em execução, o loop for não parou. Embora arr seja acessado fora dos limites, o programa deve ser executado antes que possa relatar um erro, e uma coisa deve ser concluída.


	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 Como usar as teclas de atalho

1.F5 para iniciar a depuração

Como usar: Pressione F5 diretamente para finalizar o programa, não pode ser usado sozinho, precisa ser usado com F9 (ponto de interrupção)

(Pressione F5 várias vezes, ele parará no ponto de interrupção lógico original, por exemplo, o loop muda de uma vez para duas vezes e para no próximo ponto de interrupção do loop), algumas pessoas descobrirão que pressionar F5 não responde, então você precisa adicionar Fn+F5 ou Fn+F10

2.Ctrl + F5 não depura, executa o código diretamente

3.F9 definir/cancelar ponto de interrupção

Como usar: execução de código, pressione F5 e pare apenas quando o ponto de interrupção F9 for encontrado

4. Ctrl + F para encontrar palavras-chave

5. Ctrl + K+C para adicionar um comentário (usado com selecionar tudo) Ctrl + K+U para descomentar

6. Copiar em linguagem C não precisa selecionar copiar, basta pressionar Ctrl+V na linha que precisa ser copiada para copiar

Além disso, existem mais teclas de atalho, blog adicionado abaixo

Teclas de atalho comumente usadas no VS - blog do MrLisky - blog CSDN - teclas de atalho do VS

Alguns alunos acharam que quando abri Debug -> Window, não encontrei o monitoramento.

Isso ocorre porque muitas janelas no monitoramento são exibidas apenas quando a depuração é iniciada.O monitoramento é usado para cooperar com o código para verificar erros, e é necessária mais prática.


2.4 Exemplo de pergunta: Eu quero saber se 153 é um expoente. Você é burro e continua pressionando F10, quanto tempo leva quando 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;
}

Podemos usar F5 para cooperar com F9, clique com o botão direito do mouse no ponto de interrupção F9 -> condição (ponto de interrupção condicional), digite i==153 (configuração condicional), pressione F5 neste momento, você descobrirá que i pula 152 bits e vai diretamente para 153


2.5 F10 (passo a passo, ao encontrar uma função, não entrará na função, e executará diretamente o conteúdo completo da função)

  F11 (declaração por declaração, ao encontrar uma função, ele entrará na função e executará cada detalhe do código)

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 até chegar na função Add, pressione F11 para entrar na função, use-a com a janela de monitoramento, além de monitoramento e monitoramento automático (não recomendado), o monitoramento automático te ajuda a liberar todas as variáveis, fica fácil observar o Erro de número especificado, só precisamos observar o número que queremos no monitor

monitoramento de memória


Pilha de chamadas, quando a lógica de chamada de função é complexa, você pode visualizar a lógica de chamada da pilha

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

 3.1 Identifique problemas de código por depuração

Código de implementação: peça 1! +2! +3! ...+ n! ; overflow não é considerado. (há um erro no código abaixo)

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
Queremos 3! , Digite 3, mas descubra que o resultado é 15, deve haver algum problema com o código, então pressionamos F10 para iniciar a depuração e, em seguida, inserimos a variável que queremos observar no monitoramento

A primeira execução,

Não há problema com o código, 1! é 1

 

A segunda execução, 2! É 2, mas continuamos a executar para baixo, mas descobrimos que o valor de ret é 2. Você deve saber que o papel de ret é o papel de 1*2*...*n, o valor de ret mudou e o resultado é naturalmente errado.

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 Como escrever código bom (fácil de depurar) 

1. O código roda normalmente 2. Existem poucos bugs 3. Alta eficiência 4. Alta legibilidade 5. Alta facilidade de manutenção 6. Comentários claros 7. Documentação completa

Técnicas comuns de codificação:

1. Usando assert

2. Tente usar const

3. Desenvolva um bom estilo de codificação

4. Adicione os comentários necessários

5. Evite armadilhas de codificação.

3.3 Simule a função da biblioteca: strcpy (cópia de string)

Copie o conteúdo apontado pelo ponteiro original para o espaço apontado pelo ponteiro do espaço de destino e também copie \0 para o passado

 

De 10 pontos, este código dá 5 pontos e falha, embora ele também possa completar a cópia da string que queremos alcançar

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;
}

Como melhorar? Primeiro, a cópia da string é feita duas vezes, podemos otimizar

Pode ser alterado para postar ++

void my_strcpy(char* dest, char* src)
{
	while (*src != '\0')
	{
		*dest++ = *src++;
	}
	*dest = *src;
}
assert é usado para assert.Quando os dois ponteiros que passamos são NULL, e a aplicação é executada sem assert, vamos acessar a memória de forma ilegal, e o código fica em risco.
O código acima ainda não está simplificado o suficiente, podemos combinar as duas frases 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,表达式不成立结束循环
	{
		;
	}
}

Então nós olhamos para a introdução da versão oficial do strcpy

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

A função my_strcpy foi projetada para retornar o tipo de valor char* para obter acesso encadeado à função

Também é necessário adicionar const, para que as variáveis ​​no espaço de destino src não possam ser modificadas (tornam-se variáveis ​​constantes), para evitar erros de código

Design de função perfeito!

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.1 Explicação do Exercício Const

Const é tornar nosso código mais robusto, mas como usamos const para restringir exatamente o código que queremos?

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

	return 0;
}

O que acontece se você adicionar const?

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

Trancou a porta e impediu você de entrar pela porta, mas você quebrou a janela e pulou. Embora você tenha entrado, era ilegal

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 pode modificar ponteiros:
    const é colocado no lado esquerdo de * (const int* p;)
    const modifica *p, o que significa que o objeto apontado por p não pode ser alterado por p, mas o endereço na variável p pode ser
    const é colocado em * O lado direito de (int* const p;) const modifica
    p, o que significa que o conteúdo de p não pode ser alterado, mas o objeto apontado por p pode ser alterado através de p


 

5.1 Erros comuns na programação

5.2 Erros de compilação são erros de sintaxe

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

 Solução: Observe diretamente a mensagem de erro (clique duas vezes) para resolver o problema. Ou você pode fazê-lo com experiência. Relativamente simples.

5.3 Erros de encadeamento 

//没有引头文件
//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;
}

 

Solução: Observe a mensagem de erro, encontre principalmente o identificador na mensagem de erro no código e localize o problema. Normalmente, o nome do identificador não existe ou está incorreto.

5.4 Erros de tempo de execução - Erros resolvidos com depuração (exemplo de fatorial)


O conhecimento básico da linguagem C acabou!

Acho que você gosta

Origin blog.csdn.net/weixin_63543274/article/details/123756664
Recomendado
Clasificación