实用调试技巧 Debug Release F10 F11 初始化 越界访问 堆栈 模拟实现库函数

本文介绍了计算机程序中的bug概念及调试的重要性,详细讲解了Windows环境下调试的方法与技巧,包括使用各种快捷键、查看程序状态等。同时,文章还提供了多个调试实例,并探讨了常见编程错误及其分类,最后给出了编写高质量、易调试代码的建议。

目录


一、什么是bug

第一次被发现的导致计算机错误的飞蛾,也是第一个计算机程序错误。
指代计算机上存在的漏洞 软件缺陷


二、调试是什么?有多重要?

所有发生的事情都一定有迹可循,如果问心无愧,就不需要掩盖也就没有迹象了,如果问心有 愧,就必然需要掩盖,那就一定会有迹象,迹象越多就越容易顺藤而上,这就是推理的途径。顺 着这条途径顺流而下就是犯罪,逆流而上,就是真相。

1、调试

调试

(英语:Debugging / Debug),又称除错,是发现和减少计算机程序或电子仪器设备中程 序错误的一个过程。

2、调试的基本步骤

  1. 发现程序错误的存在
    • 程序员
    • 测试人员 --> 发布
    • 用户
  2. 以隔离、消除等方式对错误进行定位
  3. 确定错误产生的原因
  4. 提出纠正错误的解决方法
  5. 对程序错误予以改正,重新测试

三、Debug和Release

Debug - 调试版本 - 可以调试

Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。

Release - 发布版本 - 用户使用的,不能调试

Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优 的,以便用户很好地使用。


四、windows环境调试介绍

1、调试环境的准备

在环境中选择 debug 选项,才能使代码正常调试

2、学会快捷键

Ctrl+U 改大小 Ctrl+Shift+U

VS改大小写的快捷键
改成小写:Ctrl+U

改成大写:Ctrl+Shift+U

记得要选中要修改的一段英文。

F5 启动调试

启动调试,经常用来直接跳到断点处

在执行流程中的断点,想要跳到下一个断点,取消当前循环的断点,F5跳到下一个循环的断点处。

F9 启用/关闭断点

创建断点和取消断点 (F5要和F9配合使用 )

断点的重要作用,可以在程序的任意位置设置断点。

这样就可以使得程序在 想要的位置随意停止执行,继而一步步执行下去。

点住断点 右击 条件断点 可以设置条件 不用一一循环

F10 逐过程

逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句。

F11 逐语句

逐语句,就是每次都执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部(这是 最常用的)。

CTRL + F5 开始执行(不调试)

开始执行不调试,如果你想让程序直接运行起来而不调试就可以直接使用。

#include <stdio.h>

void test() //F11 进入函数内部
{
    printf("hehe\n");
}

int main()
{
    int arr[10] = {0};
    int i = 0;
    test(); //F11
    
    for(i=0; i<10; i++)
    {
        scanf("%d", arr[i]);
	}
    
    for(i=0; i<10; i++)
    {
        printf("%d ", arr[i]);
	}
    
    return 0;
}

其他快捷键

Ctrl+F5:查找

Ctrl+H: 替换

Ctrl G:转到行

Ctrl+K C: 注释选定内容

Ctrl+K U: 取消选定注释内容

END选择至行尾

HOME选择至行开始处


3、调试的时候查看程序当前信息

在这里插入图片描述

3.1、查看临时变量的值

监视 delete删除

#include <stdio.h>

void test() //F11 进入函数内部
{
    printf("hehe\n");
}

int main()
{
    int arr[10] = {0};
    int i = 0;
    test(); //F11
    
    for(i=0; i<10; i++)
    {
        scanf("%d", arr[i]);
	}
    
    for(i=0; i<10; i++)
    {
        printf("%d ", arr[i]);
	}
    
    return 0;
}

3.2、查看内存信息
3.3、查看调用堆栈

通过调用堆栈,可以清晰的反应函数的调用关系以及当前调用所处的位置。

#include <stdio.h>

void test2()
{
    printf("test2\n");
}

void test1()
{
    test2();
}

void test()
{
    test1();
}

int main()
{
    test();

    return 0;
}

在这里插入图片描述


3.4、查看汇编信息

在调试开始之后,有两种方式转到汇编:

(1)第一种方式:右击鼠标,选择【转到反汇编】:

(2)第二种方式:调试 窗口 反汇编

3.4、查看寄存器信息

可以查看当前运行环境的寄存器的使用信息。



五、一些调试的实例

1、求 1!+2!+3! …+ n! 不考虑溢出。

int main()
{
    int i = 0;
    int sum = 0;//保存最终结果
    int n = 0;
    int ret = 1;//保存n的阶乘

    scanf("%d", &n);

    for (i = 1; i <= n; i++)
    {
        int j = 0;
        for (j = 1; j <= i; j++)
        {
            ret *= j;
        }
        sum += ret;
    }

    printf("%d\n", sum);
    
    return 0;
}

这时候我们如果3,期待输出9,但实际输出的是15。

通过调试发现是ret没有初始化

初始化后:

int main()
{
    int i = 0;
    int sum = 0;//保存最终结果
    int n = 0;

    scanf("%d", &n);

    for (i = 1; i <= n; i++)
    {
        int ret = 1;//保存n的阶乘

        int j = 0;
        for (j = 1; j <= i; j++)
        {
            ret *= j;
        }
        sum += ret;
    }

    printf("%d\n", sum);

    return 0;
}

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

越界访问,有可能访问到i,死循环

i<=10时报错,因为一直在循环,没有时间报错

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述


NICE 笔试题

在这里插入图片描述


3、Debug版本对比

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[0]);
	printf("%p\n", &arr[9]);

	return 0;
}

Debug

此时,i 的地址比数组最后一个元素还大,说明如果越界访问,有可能虎访问到i,导致错误。

Release版本

没有报错,打印了 个hehe,没有死循环,

Release数组的地址比 i 的地址大,

因为进行了优化,把i放在arr之后,

《C陷阱与缺陷》
在这里插入图片描述



六、编程常见的错误

常见的错误分类:

1、编译型错误

直接看错误提示信息(双击),解决问题。或者凭借经验就可以搞定。相对来说简单。

2、链接型错误

看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。一般是标识符名 不存在或者拼写错误。

3、运行时错误

借助调试,逐步定位问题。最难搞。

在这里插入图片描述
在这里插入图片描述



七、如何写出好(易于调试)的代码。

优秀的代码:

  1. 代码运行正常
  2. bug很少
  3. 效率高
  4. 可读性高
  5. 可维护性高
  6. 注释清晰
  7. 文档齐全

常见的coding技巧:

  1. 使用assert
  2. 尽量使用const
  3. 养成良好的编码风格
  4. 添加必要的注释
  5. 避免编码的陷阱

一、模拟实现库函数:strcpy

Null - \0
null - \0
NULL - 空指针

1、strcpy

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

int main()
{
	//strcpy - string copy - 字符串拷贝
	char arr1[] = "abcdef";
	char arr2[10] = { 0 };
	strcpy(arr2, arr1); //arr2目标数组 

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

	return 0;
}

2、my_strcpy

1

//dest 是指向目标空间的
//src 是指向原字符串的 
void my_strcpy(char* dest, char* src)//目标 源头
{
	while (*src != '\0') // \0=0 当指向\0时停止
	{
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src; //strcpy把\0一起拷贝 出循环指向的是\0 拷贝\0
}

int main()
{
	//strcpy - string copy - 字符串拷贝
	char arr1[] = "abcdef";
	char arr2[10] = { 0 };
	my_strcpy(arr2, arr1); //arr2目标数组 

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

	return 0;
}

2优化 后置++

void my_strcpy(char* dest, char* src)
{
	while (*src != '\0')
	{
		*dest++ = *src++; //后置++
	}
	*dest = *src; // \0
}

3优化

void my_strcpy(char* dest, char* src)
{
	//1. 拷贝字符
	//2. 遇到\0 循环停止
	while (*dest++ = *src++)
	{
		;
	}
}

4优化 判断空指针

#include <assert.h>

void my_strcpy(char* dest, char* src)
{
	/*if (src == NULL || dest == NULL)
	{
		return;
	}*/

	assert(src != NULL); //断言 - release版本可以优化掉
	assert(dest != NULL);
	//assert发现问题会告诉我们

	/*assert(src);
	assert(dest);

	assert(src && dest);*/

	while (*dest++ = *src++)
	{
		;
	}
}

const

用法

int main()
{
	const int num = 10; //num是常变量
	//num = 20; //err

	int* p = &num;
	*p = 20; //可以修改

	printf("%d\n", num);
}

const修饰指针 *左右

int main()
{
	const int num = 10;

	//const修饰指针
	
	//const 放在*的左边
	//修饰的是指针指向的内容(*p),指针指向的内容,不能通过指针来改变了
	//但是指针变量本身(p)是可以改变的
	int n = 100;
	const int* p = &num; //写法相同int const * p = &num;
	*p = 20;
	p = &n;
	printf("%d\n", num); //修饰的是p 此时p不能存放n的地址

	//const 放在*的右边
	//修饰的是指针变量本身(p),指针变量本身(p)不能修改了
	//指针指向的内容(*p)可以修改,
	int n = 100;
	int * const p = &num; //修饰的是*p 指针所指向的对象 
	*p = 20;
	p = &n;

	printf("%d\n", num);
}

5优化 const

健壮性/鲁棒性 - 抗风险

#include <assert.h>

void my_strcpy(char* dest, const char* src) //修饰*src 保护src不被修改
{
	assert(src != NULL);
	assert(dest != NULL);

	while (*src++ = *dest++) //如果写反
	{
		;
	}
}

6优化 返回类型

#include <assert.h>

char* my_strcpy(char* dest, const char* src)
{
	assert(src != NULL);
	assert(dest != NULL);

	char* ret = dest; //先存起来 下面已经指向src对应得\0的位置 不是起始位置
	
	while (*dest++ = *src++)
	{
		;
	}

	return ret; //返回目标起始位置
}

int main()
{
	char arr1[] = "abcdef";
	char arr2[10] = "xxxxxxxxxx";
	char* ret = my_strcpy(arr2, arr1); //arr2目标数组 

	printf("%s\n", ret); 
	//只打印abcdef 没有打印后面的xxxx是因为字符串的结束标志是\0

	return 0;	
}

数组的错误情况

int main()
{
	//1
	char arr1[] = "abcdef"; //[a b c d e f \0]
	char arr2[] = "xxxxxxxxx";

	//2 - 原字符串中一定要有\0
	char arr1[] = { 'a', 'b', 'c' };
	char arr2[10] = "xxxxxxxxx";

	//3 - 目标空间一定要足够大
	char arr1[] = "abcdef";
	char arr2[3] = { 0 };

	//4 - 目标空间必须可修改
	char arr1[] = "abcdef";
	const char* arr2 = "xxxxxxxxxxxxxxx"; //指针指向的内容 不希望被修改时 加const 更严谨安全
	//arr2是指针 指向常量字符串,常量字符串是放在常量区,是不能修改的
	//不能拷贝,因为目标空间arr2不能被修改

	char* ret = my_strcpy(arr2, arr1);
	printf("%s\n", ret);

	return 0;
}

打印字符串 解引用打印一个字符

int main()
{
	char arr[] = "abcdef";
	const char* str = "kel";
	//打印字符串
	printf("%s\n", arr);
	printf("%s\n", str);

	//字符指针 解引用 只能访问一个字符 
	//arr访问的是a 打印的是字符%c
	printf("%c\n", *arr); //a
	printf("%c\n", *str); //k

	return 0;
}

常量字符串首字符地址存放在指针

int main()
{
	const char* str = "abcdef";
	//常量字符串abcdef在内存的常量区里 
	//把常量字符串首字符a的地址存放在str中
	printf("%s\n", str); //abcdef
	printf("%c\n", *str); //a

	return 0;
}

二、模拟实现strlen函数

#include <assert.h>

//size_t - unsigned int 扩大范围
unsigned int my_strlen(const char* str)
{
	assert(str != NULL);

	int count = 0;
	while (*str)
	{
		count++;
		str++;
	}

	return count;
}

int main()
{
	char arr[] = "abcdef";
	
	int ret = my_strlen(arr);

	printf("%d", ret);

	return 0;
}

strlen返回类型是无符号数

int main()
{
	//3-6 = -3
	//10000000000000000000000000000011 - 原
	//11111111111111111111111111111100 - 反
	//11111111111111111111111111111101 - -3补码
	//在内存中被解析为无符号数 全都是有效位
	//补码即原码 是一个很大的正数
	if (strlen("abc") - strlen("abcdef") > 0)
	{
		printf("hehe\n"); //hehe
	}
	else
	{
		printf("haha\n");
	}

	return 0;
}

八、练习

1、前置 / 后置++

#include <stdio.h>

int main()
{
	int a, b, c;

	a = 5;
	c = ++a;
	b = ++c, c++, ++a, a++;
	b += a++ + c;

	printf("a = %d b = %d c = %d\n:", a, b, c);
	//a:9, b:23, c:8
	return 0;
}

2、统计二进制中1的个数

写一个函数返回参数二进制中 1 的个数。
比如: 15 0000 1111 4 个 1

2.1、用&1和>>方法

size_t count_bit_one(int n)
{
	int i = 0;
	int count = 0;
	for (i = 0; i < 32; i++) //i==31时 最高位来到最低位
	{
		//最低位和1按位与 如果是1按位与后结果就是1
		//00000000000000000000000000001111 - 15
		//00000000000000000000000000000001 - 1
		//00000000000000000000000000000001 - &1 最低位是1 结果就是1
		//按位与后>>
		if (((n >> i) & 1) == 1) //当i=0时 没有右移
		{
			count++;
		}
	}

	return count;
}

int main()
{
	int num = 0;
	scanf("%d", &num);

	int ret = count_bit_one(num);

	printf("%d\n", ret);

	return 0;
}

2.2、用%2方法

十进制%10 /10可以得到每一位
123%10=3
12/10=12
12%10=2

//负数算不了 用无符号数
size_t count_bit_one(unsigned int n) //把负数当成无符号数求
{
	int count = 0;
	while (n)
	{
		if (n % 2 == 1)
		{
			count++;
		}
		n /= 2;
	}

	return count;
}

int main()
{
	int num = 0;
	scanf("%d", &num);

	int ret = count_bit_one(num);

	printf("%d\n", ret);

	return 0;
}

2.3、用n-1&

size_t count_bit_one(int n)
{
	int count = 0;
	while (n)
	{
		n = n & (n - 1);
		count++;
	}
	return count;
}

int main()
{
	int num = 0;
	scanf("%d", &num);

	int ret = count_bit_one(num);

	printf("%d\n", ret);

	return 0;
}

3、写一个代码,判断一个数是不是2的次方数

2 – 0010
4 – 0100
8 – 1000
16 – 10000

int main()
{
	int n = 0;
	scanf("%d", &n);
	if ((n & (n - 1)) == 0)
	{
		printf("YES\n");
	}
	else
	{
		printf("N0\n");
	}

	return 0;
}

在这里插入图片描述


IO型 - 自己写输入,输出
接口型 - 只要完成指定接口就可以了

4、统计二进制中1的个数

题目内容:
写一个函数返回参数二进制中 1 的个数。
比如: 15 0000 1111 4 个 1

//C++
class Solution {
public:
    int  NumberOf1(int n) {
        int count = 0;
        while (n)
        {
            n = n & (n - 1);
            count++;
        }
        return count;
    }
};

5、求两个数二进制中不同位的个数

题目内容:
编程实现:两个int(32位)整数m和n的二进制表达中,有多少个位(bit)不同?
输入例子 :
1999 2299
输出例子 : 7

循环

int main()
{
	int n1 = 0;
	int n2 = 0;
	scanf("%d %d", &n1, &n2);
	int count = 0;
	int i = 0;
	for (i = 0; i < 32; i++)
	{
		if (((n1 >> i) & 1) != ((n2 >> i) & 1))
		{
			count++;
		}
	}
	printf("%d\n", count);

	return 0;
}

函数

int count_diff_bit(int n1, int n2)
{
	int count = 0;
	int i = 0;
	for (i = 0; i < 32; i++)
	{
		if (((n1 >> i) & 1) != ((n2 >> i) & 1))
		{
			count++;
		}
	}
	return count;
}

int main()
{
	int n1 = 0;
	int n2 = 0;
	scanf("%d %d", &n1, &n2);

	int count = count_diff_bit(n1, n2);
	printf("%d\n", count);

	return 0;
}

对应的二进制位^ 同为0异为1

int NumberOf1(int n) //求一个数二进制有多少个1
{
    int count = 0;
    while (n)
    {
        n = n & (n - 1);
        count++;
    }
    return count;
}

int count_diff_bit(int n1, int n2)
{
    int count = 0;
    int t = n1 ^ n2;
    count = NumberOf1(t);
    return count;

}

int main()
{
	int n1 = 0;
	int n2 = 0;
	scanf("%d %d", &n1, &n2);

	int count = count_diff_bit(n1, n2);
	printf("%d\n", count);

	return 0;
}

6、打印整数二进制的奇数位和偶数位

题目内容:
获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列

/偶数位 1 3 5 7。。。31
奇数位 0 2 4 6 。。。30
倒着打印 所以从后开循环

int main()
{
	int num = 0;
	scanf("%d", &num);
	int i = 0;
	printf("偶数位:");
	for (i = 31; i >= 1; i -= 2)
	{
		printf("%d ", (num >> i) & 1);
	}
	printf("\n奇数位:");
	for (i = 30; i >= 0; i -= 2)
	{
		printf("%d ", (num >> i) & 1);
	}
	printf("\n");

	return 0;
}

7、判断是元音还是辅音

多组输入,每行输入一个字母。
输出描述:
针对每组输入,输出为一行,如果输入字母是元音(包括大小写),
输出“Vowel”,如果输入字母是非元音,输出“Consonant”。

循环判断

int main()
{
    int ch = 0;
    //循环判断部分其实是在读取字符
    while ((ch = getchar()) != EOF)
    {
        //判断是否为元音字母
        if (ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || ch == 'U' || ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u')
        {
            printf("Vowel\n");
        }
        else
        {
            printf("Consonant\n");
        }
        getchar(); //清理 \n
    }

    return 0;
}

字符数组判断

int main()
{
    int ch = 0;
    char vowel[] = "aeiouAEIOU";

    while ((ch = getchar()) != EOF)
    {
        int i = 0;
        int flag = 0;
        for (i = 0; i < 10; i++)
        {
            if (ch == vowel[i])
            {
                flag = 1;
                break;
            }
        }
        if (flag == 1)
        {
            printf("Vowel\n");
        }
        else
        {
            printf("Consonant\n");
        }
        getchar(); //清理 \n
    }

    return 0;
}

//3.改大小写
//4.strstr

8、矩阵相等判断

第一行包含两个整数n和m,表示两个矩阵包含n行m列,用空格分隔。

从2到n + 1行,每行输入m个整数(范围 - 231~231 - 1),用空格分隔,共输入n * m个数,表示第一个矩阵中的元素。

从n + 2行到2n + 1,每行输入m个整数(范围 - 231~231 - 1),用空格分隔,共输入n * m个数,表示第二个矩阵中的元素。
1 < n, m < 10
输出描述:
一行,如果两个矩阵相等输出"Yes"并换行,否则输出"No"并换行。

int main()
{
	int arr1[10][10] = { 0 };
	int arr2[10][10] = { 0 };
	int n = 0;
	int m = 0;

	scanf("%d %d", &n, &m);
	int i = 0;
	int j = 0;
	//输入了矩阵1
	//n行
	for (i = 0; i < n; i++)
	{
		//m列
		for (j = 0; j < m; j++)
		{
			scanf("%d", &arr1[i][j]);
		}
	}
	//输入了矩阵2
	for (i = 0; i < n; i++)
	{
		//m列
		for (j = 0; j < m; j++)
		{
			scanf("%d", &arr2[i][j]);
		}
	}
	//判断
	int flag = 1; //默认相同
	for (i = 0; i < n; i++)
	{
		for (j = 0; j < m; j++)
		{
			if (arr1[i][j] != arr2[i][j])
			{
				flag = 0;
				break;
			}
		}
	}

	if (flag == 1)
	{
		printf("Yes\n");
	}
	else
	{
		printf("No\n");
	}

	return 0;
}

优化 读一个判断一个

int main()
{
	int arr1[10][10] = { 0 };
	int arr2[10][10] = { 0 };
	int n = 0;
	int m = 0;

	scanf("%d %d", &n, &m);
	int i = 0;
	int j = 0;

	for (i = 0; i < n; i++)
	{
		for (j = 0; j < m; j++)
		{
			scanf("%d", &arr1[i][j]);
		}
	}

	int flag = 1; //默认相同
	for (i = 0; i < n; i++)
	{
		for (j = 0; j < m; j++)
		{
			scanf("%d", &arr2[i][j]);
			if (arr1[i][j] != arr2[i][j])
			{
				flag = 0;
				break;
			}
		}
	}

	if (flag == 1)
	{
		printf("Yes\n");
	}
	else
	{
		printf("No\n");
	}

	return 0;
}

9、矩阵转置

第一行包含两个整数n和m,表示一个矩阵包含n行m列,用空格分隔。 (1≤n≤10,1≤m≤10)
从2到n+1行,每行输入m个整数(范围-231~231-1),用空格分隔,共输入n*m个数,表示第一个矩阵中的元素。
输出描述:
输出m行n列,为矩阵转置后的结果。每个数后面有一个空格。

int main()
{
	int n = 0;
	int m = 0;
	int arr[10][10] = { 0 };

	scanf("%d %d", &n, &m);
	int i = 0;
	int j = 0;
	//输入
	for (i = 0; i < n; i++)
	{
		for (j = 0; j < m; j++)
		{
			scanf("%d", &arr[i][j]);
		}
	}

	//转置输出
	for (i = 0; i < m; i++) //打印3行2列
	{
		for (j = 0; j < n; j++)
		{
			printf("%d ", arr[j][i]);
		}
		printf("\n");
	}

	return 0;
}

short解引用

#include <stdio.h>

int main()
{
	int arr[] = { 1,2,3,4,5 };
	short* p = (short*)arr;
	
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		*(p + i) = 0;
	}

	for (i = 0; i < 5; i++)
	{
		printf("%d ", arr[i]);
	}
	
	return 0;
}

1 2 3 4 5
short 解引用每次访问2个字节
00 00 3 3 5


10、VS小端存储

#include <stdio.h>

int main()
{
    int a = 0x11223344;
    
    char* pc = (char*)&a;
    *pc = 0;
    
    printf("%x\n", a);
    
    return 0;
}

低位放在低地址 高位放在高地址
// 44 33 22 11
// %d - 10进制
// %o - 8进制
// %x - 16进制


11、sizeof返回

#include <stdio.h>

int i; //全局变量,没有给初始值时,编译其会默认将其初始化为0
int main()
{
    i--; // -1
    if (i > sizeof(i))
    {
        printf(">\n");
    }
    else
    {
        printf("<\n");
    }
    
    return 0;
}

sizeof - 返回的是无符号整数
i和sizeof(i)比较 算术转换 int强制类型转化为size_t
-1对应的无符号整形是一个非常大的数字


12、使用指针打印数组内容

写一个函数打印arr数组的内容,不使用数组下标,使用指针。
arr是一个整形一维数组

#include <assert.h>

void print(const int* p, int sz)
{
	int i = 0;
	assert(p);

	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
}

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//数组在内存中是连续存放的 只要知道起始位置 就能找到数组
	print(arr, sz);

	return 0;
}

12、计算求和

求Sn=a+aa+aaa+aaaa+aaaaa的前5项之和,其中a是一个数字,
例如:2+22+222+2222+22222

int main()
{
	int i = 0;
	int a = 0;
	int tmp = 0;
	int Sn = 0;
	scanf("%d", &a);
	for (i = 0; i < 5; i++)
	{
		tmp = tmp * 10 + a;
		Sn = Sn + tmp;
	}
	printf("%d\n", Sn);

	return 0;
}

13、求a的n项

int main()
{
	int a = 0;
	int n = 0;
	int i = 0;
	int sum = 0;
	int k = 0;
	scanf("%d %d", &a, &n);

	for (i = 0; i < n; i++)
	{
		k = k * 10 + a;
		sum += k;
	}
	printf("%d\n", sum);

	return 0;
}

14、打印水仙花数

求出0~100000之间的所有“水仙花数”并输出。
“水仙花数”是指一个n位数,其各位数字的n次方之和确好等于该数本身,如:153=13+53+3^3,则153是一个“水仙花数”

判断i是否是自幂数
#include <math.h>

int main()
{
	int i = 0;
	for (i = 0; i <= 100000; i++)
	{	
		int tmp = i; //计算位数后i会改变 用tmp求
		//1. 计算i的位数 - n
		int cnt = 1;
		int sum = 0;
		while (tmp /= 10)
		{
			cnt++;
		}
		//2. 计算i的每一位的n次方之后
		tmp = i;
		while (tmp)
		{
			sum += (int)pow(tmp % 10, cnt);
			tmp /= 10;
		}
		//3. 判断
		if (sum == i)
		{
			printf("%d ", i);
		}
	}

	return 0;
}
求位数函数
#include <math.h>

int count(int tmp)
{
	int cnt = 1;
	while (tmp /= 10)
	{
		cnt++;
	}
	return cnt;
}

int main()
{
	int i = 0;
	for (i = 0; i <= 100000; i++)
	{
		int tmp = i;
		//1. 计算i的位数 - n
		int sum = 0;
		int cnt = count(i);

		//2. 计算i的每一位的n次方之后
		while (tmp)
		{
			sum += (int)pow(tmp % 10, cnt);
			tmp /= 10;
		}
		//3. 判断
		if (sum == i)
		{
			printf("%d ", i);
		}
	}

	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三春去后诸芳尽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值