GuguMelon's Blog

今天所做之事勿候明天,自己所做之事勿候他人。

0%

写在前面

妈妈再也不用担心我离开$IDE$就不会敲代码啦!

对于$gdb$功能的基础介绍

  • 设置断点
  • 逐行执行代码
  • 查看变量的值

基础使用

首先我们在$gcc$程序的时候需要添加$-g$选项

1
2
//本文仍以test.c为例
gcc -g test.c -o test

此时生成的$test$便可通过$gdb$调试辣!

我们执行

1
gdb test

会发现出现了多行信息,只需要添加$-q$,世界会瞬间变得清净起来。

踏入了$gdb$的世界,下面让我们来具体学习一下操作吧!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
file 文件名 //装载文件,如果是直接gdb进入而没有附加文件,则调试前需要装载文件
list begin,end //展示从begin行到end行的代码,若没加行数限制则默认每次展示10行,回车继续展示
b(reak) num //在num行设置断点
b(reak) 文件名:函数名
r(un) //运行
p(rint) var //输出变量的值
c(ontinue) //继续执行
n(ext) //下一行
s(tep) //下一行,与next区别为会进入函数内部
set var= //修改变量值
breakpoints //查看断点
disable num//使第num行的断点失效
enable num //使第num行的断点生效
delete num//删除num行的断点
delete //删除所有断点
kill //停止当前进程
q(uit) //退出gdb

上面便是我们常用的$gdb$的基本操作辣!

你是不是还有一个疑问呢?如果发生段错误(内存错误)我们怎么知道是哪里出了问题呢?

这就涉及到一个新的知识点。

利用$gdb$和$core dump$快速定位段错误

内存错误的程序通常会崩溃掉,即$coredum$(核心转储),这时候会有一个镜像文件留存下来,即$core$,我们可以从其中找到内存错误的代码片段。

比如这段错误的代码。

1
2
3
4
5
6
7
8
#include<string.h>
#include<stdio.h>
int main()
{
char *s;
gets(s);
puts(s);
}

运行后,结果为

去同目录下找$core$文件,可能会找不到,因为默认空间设置为0,我们给空间设置为1000。

即执行

1
ulimit -c 1000

然后执行

1
gdb -q err core

会得到

我们通过$bt$命令便可以查看错误的点的信息(具体行数)

如图所示

以上。

写在前面

今天学习了$gcc$的基本指令,之前总是望而却步,今天一口气。

前置知识

我们知道,$gcc$在编译$C$语言或者$C++$时,分为四个步骤。

  • 预处理 替换头文件和宏,移除注释(.i文件)
  • 编译,生成汇编代码 (.s文件)
  • 汇编 生成目标文件(机器码 .o文件)
  • 链接 生成可执行文件

    编译命令

1
2
3
4
5
6
7
8
9
10
11
12
13
//以下均以test.c为例
gcc test.c //此行代码会直接生成可执行文件,并默认保存为a.out
gcc test.c -o test.o //-0指令表示生成文件命名为-o后的名字,若无其他指令则与后缀无关,都是可执行文件
//关于-o,以下几种需要-o重定向输出文件
gcc -S test.c//只处理预处理和编译 生成汇编代码
gcc -E test.c //只预处理
gcc -c test.c //只有预处理、编译、汇编三步,生成obj(.o)文件
gcc -I 路径 test.c //所调用的头文件所涉及的路径
gcc -Dmarco test.c //宏定义
gcc -Dmarc=def test.c//宏定义
gcc -g test.c//编译时,生成调试信息,常常与gdb配合使用
gcc -std=c9//指定语言标准
gcc -On(0-3) //开启编译优化选项,一般使用-O2

大明星:$makelist$

怎么样?看了上面的编译命令,是不是觉得原来也不过如此?

但是想一下,如果我们需要编译链接许多文件呢?有没有什么简便的方式呢?

这个时候,$makefile$就要闪耀登场了!

什么是$makefile$?之前用$windows$我从来没见过,因为强大的/一般的$IDE$已经为我们做好了一切事情。

$makefile$其实可以简单理解为指令的集合,是对指定的一些文件进行编译的指令,如果我们一个个写出来会很耗费时间,但是$makelist$可以让我们通过简单的$make$,实现相同的功能。

格式

1
2
3
4
5
6
7
8
9
10
11
12
目标文件:[依赖文件集合]
指令 //注意:此处开头必须打tab!

//实例
main:test.o delete.o add.o
gcc $^ -o $@
test.o:test.c
gcc -c test.c -o test.o
add.o:add.c
gcc -c add.c -o add.o
delete.o:delete.c
gcc -c delete.c -o delete.o

上面只是3个文件,需要一行一行写出来,如果有1000个需要处理的文件呢?还要一行一行写出来未免效率也太低了。这个时候,$makefile$的扩展用法就要登场了。

变量定义

预定义变量
1
2
3
4
5
$< 代表第一个依赖文件的名称

$@ 代表目标文件的名称

$^ 代表所有依赖文件的名称

此外还可以自己定义一些变量。

自定义变量
1
2
3
CC = gcc
CFLAGS = -I ../include
//注意等号左右两边都需要有空格

举个例子,上面代码可改为

1
2
3
CC = gcc
main:test.o delete.o add.o
$(CC) $^ -o $@

隐含规则

模式规则

将一个带有某种后缀的文件转换为另一种后缀的文件

1
2
.c.o:
$(CC) $(CCFLAGS) -c -o $@ $<
后缀规则

冒号后为依赖文件,冒号前为目标文件

个人感受

1
2
%.o:%.c(若有多个依赖文件则多写几个%)
$(CC) $(CCFLAGS) -c -o $@ $<

综合以上规则,$makefile$文件可写为

1
2
3
4
5
CC = gcc
main:test.o delete.o add.o
$(CC) $^ -o $@
.c.o:
$(CC) -c $< -o $@

写在前面

字符串是我们经常要处理的东西,今天在处理方面比如读入处理等方面做一个小小的总结。

读字符串之前

如果读字符串之前有读入数的操作,那么需要把其后的换行符给读入消去,$getchar()$即可。

整行读入字符串的方式

1
2
3
4
5
6
7
8
9
//C++标准string
string s;
getline(cin,s);
//char字符数组
char s[100];
scanf("%[^\n]%*c",s);
gets(s);
cin.get(s,100);
cin.getline(s,100);

整行读入原理解释

1
scanf("%[^\n]%*c",s);

​ $%[\qquad ]$是很有趣的参数,意义是读入一个字符集合,一旦遇到不在集合中的就停止,同时遇到^后面的字符也会停止,所以^\n就是遇到换行符停止,后面的%*c是为了把换行符读入,防止影响接下来的读入。

cin.get()和cin.getline()之区别

两者都是只支持$char*$字符数组。

我们知道读入时存在缓冲区这一说法,比如$cin$,在键盘输入结束后,将数据存入缓冲区m$cin$函数直接从缓冲区读取数据,所以如果缓冲区有残留数据,$cin$会直接读取。

问题的关键在于,$cin.get$()在一次输入结束后不会删除缓冲区的结束符,而$cin.getline$()会自动丢弃缓冲区字符。

1
2
3
cin.get(数组名,长度,结束符);
cin.getline(数组名,长度,结束符);
//其中,结束符默认为换行符

如果要用$cin.get()$读入多组数据,需要写成 $cin.get(数组名,长度).get()$。

主角

C++中的全排列函数很是强大,生成一个长度为$n$的序列的全排列复杂度仅为$O(n)$。

今天总结一下用法。

默认$cmp$规则

对一个数据有序容器$s$,如下代码便可实现全排列。

为什么必须是有序呢?如果不是有序,$next_permutation$函数便只能生成有序条件下该序列之后的排列。

1
2
3
do{

}while(s.begin(),s.end());

自定义$cmp$规则

那么,如果容器内数据的排序规则和正常的排序规则不太一样呢?

不要慌,我们可以自定义排序规则,手写$cmp$。

举个栗子。

1
2
3
4
5
6
7
int cmp(char a,char b) 
{
if(tolower(a)!=tolower(b))//tolower 是将大写字母转化为小写字母.
return tolower(a)<tolower(b);
else
return a<b;
}//自定义字符比较规则

那么在使用$next_permutation$函数的时候,如下方式实现。

1
2
3
4
do
{

}while(next_permutation(ch,ch+strlen(ch),cmp));

TLE

  1. 是不是写了死循环

MLE

  1. 是不是没有清空队列

WA

  1. 是不是写多组用例了