实用调试技巧

16 篇文章 0 订阅
订阅专栏

目录:

1.什么是bug?

2.调试是什么?有多重要?

3.debug和release的介绍

4.Windows环境调试介绍

5.一些调试的实例

6.如何写出好(易于调试)的代码

7.编程常见的错误


1.什么是bug?

bug--->臭虫、虫子。

为什么含义是臭虫、虫子呢?

答案是:第一次被发现的导致计算机错误的是一只飞蛾,也是第一个计算机程序的错误。

2.调试是什么?有多重要?

前言:

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

        顺着这条途径顺流而下就是犯罪,逆流而上就是真相。

        一名优秀的程序员都是一名出色的侦探。

        每一次调试都是尝试破案的过程。

2.1调试是什么?

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

2.2调试的基本步骤

①发现程序错误的存在

②以隔离、消除等方式对错误进行定位

③确定错误产生的原因

④提出纠正错误的解决办法

⑤对程序错误予以改正,重新测试

2.3Debeg和Release的介绍

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

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

(1)在VS2019中如何转换?

(2)Release对代码大小上是最优的:

代码:

#include<stdio.h>

//自定义函数——实现对整数数组的冒泡排序
void bubble_sort(int* str, int sz)
{
	//趟数
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		//一趟冒泡排序的过程
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			//升序
			if (str[j] > str[j + 1])
			{
				int tmp = str[j];
				str[j] = str[j + 1];
				str[j + 1] = tmp;
			}
		}
	}
}

int main()
{
	int arr[] = { 10,9,8,7,5,6,4,1,2,3 };//定义整形数组,并初始化
	int sz = sizeof(arr) / sizeof(arr[0]);//计算数组的大小
	//调用函数,实现升序
	bubble_sort(arr, sz);
	//输出升序后的数组
	int i = 0;//循环变量
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

观察分别在Debeg和Release环境下生成的可执行程序的大小:

(3)Release对代码运行速度上是最优的

代码:

#include <stdio.h>
int main()
{
	int i = 0;
	//数组下标界限0~9
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	for (i = 0; i <= 12; i++)
	{
		//数组下标为10~12时数组越界
		arr[i] = 0;
		printf("hehe\n");
	}
	return 0;
}

在Debeg和环境下该代码死循环,解释在调试的实例二。

在Release环境下该代码打印13次hehe,不死循环,因为编译器优化把i的地址放在数组arr地址的下面了。

3.Windows环境调试介绍

 3.1调试环境的准备

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

3.2学会快捷键

最常使用的几个快捷键:

F5

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

 F9

创建断点和取消断点。

断点的重要作用,可以在程序的任意位置设置断点。这样就可以使得程序在想要的位置停止执行,继而一步步执行下来。

F5和F9常配合使用,F5一般不会单独使用的。断点再多文件、多代码中常用。

F10

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

F11

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

ctrl + F5

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

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

注意:只有先F10开始调试,才能看到程序当前信息。

3.3.1查看临时变量的值

在调试开始之后,用于观察变量的值。

3.3.2查看内存信息

在F10调试开始之后,用于观察内存信息。

3.3.3查看汇编信息

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

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

(2)第二种方式:

可以切换到汇编。

3.3.4查看寄存器信息

在F10调试起来之后,有两种方式观察寄存器信息。

(1)第一种方式:

 (2)知道寄存器的名字,可以在监视中观察寄存器信息

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

3.3.5查看调用堆栈

在F10调试之后,可以观察调用堆栈。

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

 4.多多动手,尝试调试,才能有进步

①一定要熟练掌握调试技巧;

②初学者可能80%的时间在写代码,20%的时间在调试。但是一个程序员可能20%的时间在写代码,但是80%的时间在调试。

③我们现在所讲的都是一些简单的调试,以后可能会出现很复杂的调试场景:多线程程序的调试等。

④多多使用快捷键,提升效率。

5.一些调试的实例

实例一:

实现代码:求 1!+2!+3! ...+ n! ;不考虑溢出。

代码1:实现阶乘

#include<stdio.h>

int main()
{
	//输入求几的阶乘
	int n = 0;
	scanf("%d", &n);
	//实现求n!  n!=n*(n-1)
	int ret = 0;
	int i = 0;
	for (i = 1; i <= n; i++)
	{
		ret *= i;
	}
	//输出结果
	printf("%d\n", ret);
	return 0;
}

 如果我们输入3,想输出6,但实际输出的是0.

why?

这里我们就得找我们的问题:

①首先通过经验推测问题出现的原因,初步确定问题可能的原因最好。

②实际上手调试很有必要。

③调试的时候我们要心里有数。

通过初步推测ret变量有问题,我们在在for循环打断点调试观察变量ret具体有什么问题。

代码2: 求 1!+2!+3! ...+ n!

#include<stdio.h>

int main()
{
	//输入有n个阶乘
	int n = 0;
	scanf("%d", &n);
	//循环 求 1!+2!+3! ...+ n! 
	int ret = 1;
	int i = 0;
	int sum = 0;//存放阶乘的累加和
	for (i = 1; i <= n; i++)
	{
		int j = 0;
		//实现求i的阶乘
		for (j = 1; j <= i; j++)
		{
			ret *= j;
		}
		sum += ret;
	}
	//输出结果
	printf("%d\n", sum);
	return 0;
}

如果我们输入3,想输出9,但实际输出15。

why?

分析:推测循环出错了,第一次调试在第二个循环处打断点,一步步调试监视变量的变化

但是没有发现是哪里错了,第二次调试在断点处右击设置断点条件快速调试到错误处,符合断点条件就停止,再F10观察具体原因。

 实例二:

#include <stdio.h>
int main()
{
	int i = 0;
	//数组下标界限0~9
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	for (i = 0; i <= 12; i++)
	{
		//数组下标为10~12时数组越界
		arr[i] = 0;
		printf("hehe\n");
	}
	return 0;
}

数组越界应该是程序错误,不执行但是我们运行后发现程序死循环了。

why?

我们F10调试起来观察变量。

在调试的时候,我们发现每一次i的值都和arr[12]的值一样,当arr[12]=0时,i也变成0了,所以死循环。

那arr[12]和i是不是地址一样?我们调试观察之后确实是一样的。 

图解:

在i和arr数组中间恰好就是2个整形吗?

答:不一定,该代码只是在VS2019 X86环境下实验的结果。

        如果是VC6.0——i和arr之间没有多余的空间,gcc——i和arr之间有一个整形空间。 

        所以说平时我们写代码要注意不数组越界了

6.如何写出好(易于调试)的代码

6.1优秀的代码:

①代码运行正常

②bug很少

③效率高

④可读性高(如良好的代码风格,函数名、变量名见名知意等)

⑤可维护性高

⑥注释清晰

⑦文档齐全

常见的coding技巧:

①使用assert

②尽量使用const

③养成良好的编码风格

④添加必要的注释

⑤避免编码的陷阱

6.2示范

模拟实现库函数strcpy:

strcpy:

1.函数原型

2.函数功能:

3.函数参数: 

 4.函数的返回类型:

代码1:模拟实现strcpy

分析:

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

//自定义strcpy

//代码1
void my_strcpy(char* dest, const char* src)
{
	//拷贝'\0'之前的字符
	while (*src != '\0')
	{
		*dest = *src;
		dest++;
		src++;
	}
	//拷贝'\0'
	*dest = *src;
}
int main()
{
	//将arr2中的字符串拷贝在arr1
	char arr1[20] = "#############";
	char arr2[] = "hello";
	//调用库函数实现
	//strcpy(arr1, arr2);
	//调用自定义函数实现
	my_strcpy(arr1, arr2);
	//打印拷贝后的arr1
	printf("%s\n", arr1);
	return 0;
}

代码2:优化函数体

#include<stdio.h>
#include<assert.h>

//自定义strcpy

//代码2
void my_strcpy(char* dest, const char* src)
{
	//优化1:使用指针之前一定要检查是否有效,如果无效就报错
	//assert--断言
	// assert中可以放一个表达式,表达式结果为假就报错,为真就啥事都不发生,正常运行
	//assert的头文件是assert.h
	//assert其实在release版本中被优化调了
	assert(dest && src);//断言指针的有效性

	//优化2:使代码简化
	//*dest++ = *src++;
	//等价于
	//*dest = *src;
	//dest++;src++;
	while (*dest++ = *src++)//'\0'的ASCII码值就是0,所以拷贝到'\0'停止
	{
		;
	}
}
int main()
{
	//将p中的字符串拷贝在arr1
	char arr1[20] = "#############";
	char* p = NULL;
	//调用自定义函数实现
	my_strcpy(arr1, p);
	//打印拷贝后的arr1
	printf("%s\n", arr1);
	return 0;
}

程序结果:

代码3:优化函数的形参

如下代码我们程序不报错,但是没有成功完成我们想要的拷贝:

#include<stdio.h>
#include<assert.h>

void my_strcpy(char* dest, char* src)
{
	assert(dest && src);//断言指针的有效性
	while(*src++ = *dest++)//程序员喝酒,写反了这样我们没有实现拷贝的目的
	{
		;
	}//将src所指向内容拷贝到dest所指向数组
}

int main()
{
	char arr[20] = "#############";
	char arr1[20] = "hello";
	my_strcpy(arr, arr1);
	printf("%s\n", arr);
	return 0;
}

 该怎么避免出现这种错误呢?

我们先来学习const的作用:

#include <stdio.h>

void test()
{
	//代码1
	//定义两个整型变量
	int n = 10;
	int m = 20;
	//没有const修饰
	int* p = &n;
	//可以通过指针变量p将指针所指向的内容n的值改成20?
	*p = 20;//ok
	//可以修改指针变量本身?
	p = &m; //ok
}
void test1()
{
	//代码2
	const int num = 10;
	//num = 20;//err,因为num被const修饰,所以不能修改
	//但是通过指针变量p,num能被修改了(p就像卖票的黄牛一样)
	int* p = &num;
	*p = 20;
}
void test2()
{
	//代码3
	int n = 10;
	int m = 20;
	//const放在*的左边
	const int* p = &n;//也可写成:int const* p = &n;
	//*p = 20;//err,因为const修饰的指针p指向的内容,所以不能通过指针来修改
	p = &m; //ok,因为const只修饰的是指针p指向的内容,所以指针变量本身可以修改
}
void test3()
{
	//代码4
	int n = 10;
	int m = 20;
	//const放在*的右边
	int* const p = &n;
	*p = 20; //ok,因为const只修饰的是指针变量本身,所以指针指向的内容可以通过指针改变
	//p = &m;  //err,因为const修饰的是指针变量本身,所以指针变量本身不能被修改
}
int main()
{
	//测试无cosnt的
	test();
	//测试const修饰变量
	test1();
	//测试const放在*的左边
	test2();
	//测试const放在*的右边
	test3();
	return 0;
}

结论:

const修饰指针变量的时候:

1.const如果放在*的左边,const修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变;但是指针变量本身可以修改。

2.const如果放在*的右边,const修饰的是指针变量本身,保证指针变量本身的内容不能被修改;但是指针指向的内容,可以通过指针来改变。

3.const就像法律,不能被修改。

学习了const的作用,我们来修改刚在代码的问题,可以运行但是没有完成拷贝,是因为*dest++和*src++写反了,因为src所指向的内容不变。所以我们可以在把第二个形参改成const int* src,用const修饰指针指向的内容,这样的话如果不小心将*dest++和*src++写反直接就编译错误,不会运行成功,很快就发现代码的错误了。

#include<stdio.h>
#include<assert.h>

void my_strcpy(char* dest,const char* src)
{
	assert(dest && src);//断言指针的有效性
	while (*src++ = *dest++)//程序员喝酒,写反了这样我们没有实现拷贝的目的
	{
		;
	}//将src所指向内容拷贝到dest所指向数组
}

int main()
{
	char arr[20] = "#############";
	char arr1[20] = "hello";
	my_strcpy(arr, arr1);
	printf("%s\n", arr);
	return 0;
}

如下图:

代码4:优化函数的返回类型(最终的优化版本)

#include<stdio.h>
#include<assert.h>

//库函数strcpy的返回值是目的地的起始地址
char* my_strcpy(char* dest,const char* src)
{
	assert(dest && src);//断言指针的有效性
	char* ret = dest;//存放目的地的起始地址
	while (*dest++ = *src++)
	{
		;
	}//将src所指向内容拷贝到dest所指向数组
	return ret;
}

int main()
{
	char arr[20] = "#############";
	char arr1[20] = "hello";
	//优点:链式访问(有返回值才可以)
	printf("%s\n", my_strcpy(arr, arr1));
	return 0;
}

 运行结果:

 练习:模拟strlen

#include<stdio.h>
#include<assert.h>

//size_t是unsigned int的别名,因为长度没有负数
size_t my_strlen(const char* str)
{
	assert(str != NULL);//断言指针的有效性
	size_t count = 0;//计数
	while (*str++)
	{
		count++;
	}
	return count;
}

int main()
{
	char arr[] = "abcdef";
	printf("%d\n", my_strlen(arr));
	return 0;
}

 7.编程常见的错误

7.1编译型错误(语法错误)

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

7.2链接型错误

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

类型1:库函数不包含头文件

类型2:拼写错误

 我们怎么找到错误位置?

7.3运行时错误(编译、链接都没错,但是运行结果有问题)

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

 最后温馨提示:

        做一个有心人,积累排错经验!

[程序调试方法和技巧1]
红羊家园
06-30 1952
调试方法和技巧                                             作者:非凡 便于调试的代码风格1.         不用全局变量2.         所有变量都要初始化,成员变量在构造函数中初始化3.         尽量使用const4.         详尽的注释 VC++编译选项1.         总是使用/W4警告级别2.         在调试
程序的调试技巧
wihkum
03-25 1717
程序的调试技巧
好的调试经验
ffdia的博客
11-15 421
转自 http://blog.sina.com.cn/s/blog_ed2af4b00102w5wp.html Vivado的工程目录路径里不要有空格,后续会避免很多麻烦 使用Create Block Design生成的Block Design只是一个功能块,一般FPGA的开发环境(我以前使用Quartus II)都需要一个顶层设计,所以需要使用Create HDL Wrapper来自动为B...
程序调试经验
是我
09-15 562
现代由于有高级调试器,使得我们编程起来可以相当容易解决问题!但是如果是编写并发量大的服务程序,我们如何进行调试呢?在并发量高峰期才能体现出程序的健壮性? 以下就是我调试程序的经验总结: 1.对于服务端的进程如何进行判断服务端是否有内存泄露的情况,尽管我们会对内存进行优化和回收! 在windows下我们可以打开任务管理器查看内存的情况,或者使用一些其他进程查看器,并且可以查看到cpu的使用情况
调试经验总结
ych_ding的专栏
04-23 727
1. log 打印过多导致视频解码性能下降: 采用gettimeofday()这种方式去测量运行的瓶颈,会发现瓶颈出现的地方随机变化。一般这种情况就要怀疑是因为log输出造成的。因为所有丁IO操作都是可以被中断的,进程就需要放弃处理器,等待下次再被调度执行,这个时间是随机的。
详解Chrome 实用调试技巧
10-18
主要介绍了详解Chrome 实用调试技巧,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
实用调试技巧.xmind
04-17
自己记录的比特鹏哥结构体xmind基本知识框架
第8节-实用调试技巧.pdf
11-23
第8节-实用调试技巧.pdf
简单实用的Android studio 调试技巧
09-01
主要介绍了简单实用的Android studio 调试技巧的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
实用Javascript调试技巧分享(小结)
10-16
主要介绍了实用Javascript调试技巧分享(小结),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
调试程序小技巧
BlueSu的博客
02-23 251
提问:是否遇到过如下情形? 1、代码差一个点AC 2、刚开始有思路,后来写着写着思路忘了 3、测试样例过了,然后莫名全部WA 4、…… 好了,如果有类似的或者一样的人可以看一看下文 (其实,没有的也可以看一看,给我提点改进建议_) 问题一:还差一丢丢AC 像这样 这样 这样 例子还有好多……好多(此处省略无穷个好多),那既然这么多,该怎么解决呢? 从调试代码开始 因为在竞赛中是不可能让各位...
程序调试技巧总结
贝勒里恩的博客
07-06 543
一、assert(断言) 作用:如果它的条件为假,则终止程序执行,并打印报错地点;这个宏通常用来判断程序中是否出现了明显非法的数据,如果出现了终止程序以免导致严重后果,同时也便于查找错误。 注意: 程序一般分为Debug版本和Release版本,Debug版本用于内部调试,Release版本发行给用户使用,断言assert是仅在Debug版本起作用的宏,用于检查“不应该”发生的情况; 在调试结束后,可以在#include<assert.h>语句之前插入#define NDEBUG来禁用a
软件开发技巧程序调试方法
xikangsoon的博客
06-04 679
1. 渐进式程序调试方法,就是对于一段程序。如果编译出bug。就将可疑的代码段都注释掉。使程序编译通过。程序编译通过以后,再逐渐放开注释的代码。 2. 多打印log。在创建类的时候可能会出问题,在构造函数里面打印log。 ...
调试经验——报表工作心得
预见未来to50的专栏
07-18 660
需求描述 在日常工作中,我们提交给用户的成果物(artificat/deliverable)在满足业务需求的前提下越简单越好。我们没有必要将中间的一些数据、公式、代码(SQL/VBA)、Excel名称(range name)等提供给用户。但是,这些对于我们快速、有效地完成数据刷新却是至关重要的。原因有: 1. 有些Excel公式比较复杂,开发、测试、更新过程中耗费了很多精力,如果能不断重用...
《我是程序员》调试经验总结学习
BelleDiao的博客
06-10 1451
其实一直苦于不会调试,自己是半路出家,基本没有调试经验,只得总结别人的调试真传了。纵观各路方法,其实主要还是要多思考,找问题原因进行定位,然后再利用调试方法进行调试,最终找到问题关键所在,解决掉问题。但最重要的还是自己的分析能力和对信息的敏感度,这需要长期的锻炼,没有特别的捷径。对调试经验进行以下总结: 第一步:重现问题。这一步非常重要,如果可以,尽可能能够通过一定的步骤可以稳定的把问题重现出来。即使问题很难重现,能够找到偶尔重现的规律和概率对于解决问题也是很有帮助的。 第二步:缩小范围。这里会有一些技
调试技巧
PhilipsWeng的专栏
03-06 1511
-fsanitize=address:检查数组越界 -fstrapv:检查整型越界
Eclipse DeBug
yaov_yy的专栏
07-29 548
在本文中使用的是Eclipse Juno版(Eclipse 4.2),在开始前给大家提3点建议! 不要使用System.out.println作为调试工具 把所有涉及到的组件日志级别激活并使用 使用日志分析器来读取日志 1.条件断点 如果你不知道如何添加断点,只需点击左边面板(行号前面)断点即被创建。在调试界面中,“断点”视图会把所有被创建的断点列出来。我们可以给它加一个布尔条件,也就是
实用调试技巧
zw的博客
12-20 847
一、调试的基本步骤 在程序运行时,找出导致程序错误的存在,并进行定位 对错误的地方进行标记 确定错误产生的原因 提出纠正错误的解决办法 将改正后的程序,重新运行,重新测试 二、Debug 和 Release ▪Debug Debug,又称为调试版本,它通常包含调试信息,并且不做任何优化,便于程序员调试程序,但当系统执行时,会生成内存比较大的文件,如下图:。 由Debug产生.exe文件足有39KB大小,那为什么会说它生成的文件大呢,接着往下看。 ▪Release Release,又称发布版本,顾名思义
trace32调试技巧
最新发布
05-31
Trace32是一款强大的嵌入式系统调试工具,以下是一些Trace32调试技巧: 1. 使用Trace32的命令行接口(CLI)可以提高调试效率,可以通过脚本自动化执行一系列操作。 2. 监视寄存器和内存变量,可以使用Trace32的...

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
写文章

热门文章

  • C/C++内存管理 1660
  • 数据在内存中的存储,你知道吗? 1449
  • 顺序表详解|顺序表常见错误并调试分析 1303
  • 字符函数和字符串函数(下) 1283
  • C++入门——缺省参数|函数重载 1265

分类专栏

  • 数据结构初阶 14篇
  • Linux 2篇
  • C++初阶 14篇
  • bug记录 1篇
  • Windows 1篇
  • 初阶算法刷题集 1篇
  • C语言刷题大杂烩 14篇
  • C语言小项目 2篇
  • C语言进阶 11篇
  • C语言初阶 16篇
  • 算法小白从0到1训练营 2篇
  • 2023寒假C语言刷题 5篇

最新评论

  • 【Linux学习】(1)Linux环境安装|Xshell安装|创建普通用户

    swr42424: 这篇文章真是一篇佳作!作者运用了生动有趣的语言,将枯燥的理论知识娓娓道来,让人如沐春风。【我也写了一些相关领域的文章,希望能够得到博主的指导,共同进步!】

  • 二叉树的概念|满二叉树与完全二叉树|二叉树的性质|二叉树的存储结构

    白话机器学习: 写的非常详细,是一篇优质博客,干货满满,让我有了全新的认识,感谢博主分享,让我学到了很多,支持支持。

  • C语言操作符经典例题

    RRRRRoyal: 博主文章写的十分细致,结构严谨。感谢博主分享,期待博主持续输出好文,同时也希望可以来我博客指导我一番!

  • 栈的概念|动态顺序栈的详细解析|静态顺序栈&链式栈的代码参考

    洁洁!: 博主的文章干货满满,内容丰富,图文并茂,深入知识核心,优质好文,持续支持!

  • 单链表详细解析|画图理解

    殿下p: 优质好文,学到了很多有用的知识,受益匪浅,支持大佬,希望大佬继续产出高质量文章

大家在看

  • C#如何将目标格式转换为字符串

最新文章

  • 常见的排序算法
  • 【Linux学习】(2)OS的简单了解|Linux的基本指令操作
  • 【Linux学习】(1)Linux环境安装|Xshell安装|创建普通用户
2024年19篇
2023年46篇
2022年19篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值

深圳SEO优化公司许昌网站制作推荐保山网站推广多少钱盐城百度竞价包年推广价格河池关键词按天扣费永新外贸网站设计价格海西网站制作设计哪家好普洱企业网站建设连云港优化报价上饶百度seo公司怒江品牌网站设计报价佛山seo网站推广报价崇左SEO按天扣费报价忻州网站推广系统哪家好玉树百度网站优化排名公司合肥seo优化聊城模板推广垦利网站改版推荐汕尾关键词排名包年推广哪家好武威关键词排名贵阳网站定制价格淮北百度竞价哪家好阿坝SEO按天收费推荐信阳模板制作哪家好昭通网站改版沙井推广网站价格巴中网站优化排名报价淮南百度竞价报价朝阳英文网站建设价格玉林优化推荐自贡模板制作价格歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化