C语言-------Linux编译&GDB调试&Makefile&结构体大小计算

目录

    • include "" <>
    • 编译
    • Makefile
    • 常用gdb调试命令
    • DEBUG
    • strace ./a.out
    • 多行注释
    • 结构体
    • 收获

include “” <>

  1. <> 头文件寻找顺序: -I指定路径-》系统环境变量指定头文件路径
  2. ""头文件寻找顺序: 当前头文件工作目录-》-I指定路径-》系统环境变量指定头文件路径

编译

  1. gcc编译程序包含了四个步骤:
    (1)预处理:读取C语言源文件,头文件,对以“#”开头的语句进行处理
    gcc -E main.c - o main.i

    当于根据预处理指令组装新的C/C++程序。经过预处理,会产生一个没有宏定义没有条件编译指令,没有特殊符号的输出文件,这个文件的含义同原本的文件无异,只是内容上有所不同。

    (2)编译:对 main.i 进行词法分析,试图找出所有不符合语法规则的部份,打印错误或警告。若没有任何语法问题,则将其翻译为功能等价的汇编代码main.s
    gcc -S main.i -o main.s

    将预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后,产生相应的汇编代码文件。

    (3)汇编:把汇编代码main.s翻译成目标机器代码(二进制文件)main.o
    gcc -c main.s -o main.o

    (4)链接:将一个文件中引用的符号(函数、外部变量等)与该符号在另文件中的定义和实现连接起来,从而使有关的目标文件连成一个整体,最终生成可以被操作系统执行的可执行文件app.out
    gcc main.o -o app.out

  2. 静态库:静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。“空间换时间”.

----- 静态库的编译与加载
ar rcs libfun.a fun1.o fun2.o fun3.o
//对目标机器文件进行归档

gcc main. c -o main ./libfun.a(静态库的路径)
 //编译可执行程序并加载静态库
  1. 动态库:动态编译可执行程序并加载静态库库代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。 “时间换空间”
---- 动态库的编译与加载
gcc -c -fpic  fun1.c
//生成源文件的目标机器文件

gcc -fpic -shared -o libfun.so fun1.o fun2.o fun3.o
cp libfun.so /usr/lib/
//编译出动态库

gcc main.c -lfun(-l+库名)
//编译可执行程序并加载动态库

Makefile

  1. 目标的时间必须晚于依赖条件的时间,否则,更新目标
    $@:在规则命令中,表示规则中的目标
    $^:在规则的命令中,表示所有依赖条件
    $<:在规则的命令中,表示第一个依赖条件,如果将该变量应用在模式规则中,它可将依赖条件列表中的依赖以此取出,套用模式规则
TARGET = app 
SOURCES = $(wildcard ./src/*.c)
OBJS = $(patsubst ./src/%.c, ./obj/%.o, $(SOURCES) )
CC = gcc 
CFLAGS = -Wall -g -c -D__DEBUG
LIBS = -lm -lpthread
INCLUDES = -I./include
#终极目标
ALL:$(TARGET)
#模式规则
$(TARGET):$(OBJS)
    $(CC) $(LIBS) $^ -o $@
#静态模式规则
$(OBJS):./obj/%.o:./src/%.c
    $(CC) $(CFLAGS) $(INCLUDES) $< -o $@
#伪目标
.PHONY:clean
clean:
    rm -f $(OBJS) $(TARGET)    
                    

常用gdb调试命令

  • 在调试过程中运行 Linux 命令:shell 命令
  1. l (list)显示10行代码
    list 5,10:显示第 5 行到第 10 行的代码
  2. r (run) 运行程序,如果没有断点,一直执行到程序结束
    run 字符串1 字符串2 … :设置main函数命令行参数
    set args 字符串1 字符串2 … : 设置main函数命令行参数
  3. b (break)设置断点: b 行号/函数名
    以条件表达式设置断点,命令格式:break 行号或函数名 if 条件(i=5)
    程序运行中当条件表达式的值发生改变时暂停:watch 条件表达式
  4. disable 断点编号 //可以使某个断点失效。(断点依然存在于程序中)
    enable 断点编号 //可以使某个断点恢复有效
    彻底删除断点:clear 行号
    删除该函数的断点:clear 函数名
    删除指定编号的断点:delete 断点编号(如果一次要删除多个断点,各 断点编号以空格隔开。)
  5. s (step) 执行下一步代码
  6. n (next) 执行下一步代码,不进入函数
  7. p x (print) 打印变量x
  8. ptype 变量 :查看变量类型
  9. c (continue) 继续执行
  10. kill 强行终止正在调试的程序
  11. finish 连续运行到当前函数返回为止
  12. info breakpoints 查看当前设置的中断点
  13. d 删除目前所有断点
  14. whatis 变量或表达式 显示数据类型
  15. display 变量或表达式 跟踪变量-----undisplay 变量编号 取消跟踪显示
  16. bt:列出当前程序正存活这的栈帧
  17. frame 编号:根据栈帧编号,切换栈帧

DEBUG

#include 

#define _DEBUG

#ifdef _DEBUG
#define DEBUG(fromat,...) printf("FILE: "__FILE__";      LINE: %d;     check:"fromat"\n", __LINE__, ##__VA_ARGS__)
#else 
#define DEBUG(fromat,...)
#endif

int main(){
	
	int a=6;
	DEBUG("%d",a); 
	
	return 0;
} 

strace ./a.out

跟踪进程执行时的系统调用和所接收的信号

多行注释

#if 0
	被注释的代码
#endif

结构体

  1. 结构体指针
  • 其访问的一般形式为:
    (*结构指针变量).成员名

  • 或为:
    结构指针变量->成员名

    例如:
    (*pstu).num
    或者:
    pstu->num

  1. 结构体的函数传参数时,不要用按值传递(浪费内存),要用地址方式传递
  2. 结构体大小
    char a:4;//位域,占据4个bit,半个字节。一个字节所剩空间不够存放另一位域时,从下一单元起存放该位域。
    (1)起始位置:x%元素大小==0 或者 x %对齐字节数 ==0,如果不满足补位
    (2)结构体大小必须是最大元素(数组按元素类型大小算,而不是数组长度,当然计入大小的时候还是要计入数组的空间大小,只是倍数关系是按照类型算)的整倍数
    空结构体 :c语言编译器 0字节,c++编译器 1字节
    对齐字节数:32位操作系统4字节 64位8字节

收获

今日有介绍一些代码规范,意识到自己写的代码很多地方还不够规范,比如不够易读,嵌套过深等需要改正注意,要记得加上注释解释说明.

你可能感兴趣的:(#,C语言,指针,编译器)