引言  - 从"HelloWorld"开始

  Makefile 是Linux C 程序开发最重要的基本功. 代表着整个项目编译和最终生成过程.本文重点是带大家了解真实项目中那些简易的Makefile规则构建.

本文参照资料

   GNU make   -  https://www.gnu.org/software/make/manual/make.html  

   跟我一起写Makefile  - http://wiki.ubuntu.org.cn/%E8%B7%9F%E6%88%91%E4%B8%80%E8%B5%B7%E5%86%99Makefile:%E6%A6%82%E8%BF%B0 

   入门基础Makefile概述  - https://github.com/loverszhaokai/GNUMakeManual_CN

推荐需要简单看看上面资料. 特别是第三个入门教程, 了解基础make语法.  看完后那我们扩展之路开始了, 先hello world 讲起. 素材 mian.c

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <time.h>
  5.  
  6. #define ALEN(arr) (sizeof(arr)/sizeof(*arr))
  7.  
  8. /*
  9. * 简单的demo测试
  10. */
  11. int main(int argc, char * argv[]) {
  12. int i;
  13. const char * strs[] = {
  14. "走着走着,就散了,回忆都淡了",
  15. "看着看着,就累了,星光也暗了;",
  16. "听着听着,就醒了,开始埋怨了;",
  17. "回头发现,你不见了,突然我乱了。",
  18. };
  19.  
  20. srand((unsigned)time(NULL));
  21. for(;;) {
  22. /*
  23. * \e[ 或 \033[ 是 CSI,用来操作屏幕的。
  24. * \e[K 表示从光标当前位置起删除到 EOL (行尾)
  25. * \e[NX 表示将光标往X方向移动N,X = A(上) / B(下) / C(左) / D(右),\e[1A 就是把光标向上移动1行
  26. */
  27. printf("\033[1A\033[K"); //先回到上一行, 然后清除这一行
  28.  
  29. // 随机输出一段话
  30. i = rand()%ALEN(strs);
  31. puts(strs[i]);
  32.  
  33. sleep();
  34. }
  35.  
  36. return ;
  37. }

编译上面程序的第一个Makefile 文件内容如下

  1. main.out:main.c
  2. gcc -g -Wall -o $@ $^

执行过程就是通过shell执行make, 我们简单翻译一下上面写法的含义.

  目标 main.out  依赖 main.c ;  main.c 已经存在(因为是存在的文件) 那就执行规则 (gcc -g -Wall -o $@ $^).

  其中 $@ 表示所有目标, $^表示所有依赖. 

是不是很简单.当然上面Makefile还存在一些潜规则.

  所有执行规则都是以\t开始; 第一个目标就是make过程唯一执行的起点;

再讲之前我们再扯一点gcc 相关的积累知识. 否则写Makefile都是无米之炊.

  1. # 中间插入一段关于gcc 的前戏
  2. gcc E o main.i mian.c    # -E是预处理展开宏,生成详细c文件, -o是输出
  3. gcc S o main.s main.i    # -S 是编译阶段, 将c文件生成汇编语言
  4. gcc c o main.o main.s    # -c 是汇编阶段, 生成机器码
  5. gcc o main.exe main.o     # 链接阶段, -o 生成目标执行程序
  6.  
  7. gcc g      # 编译中加入调试信息, 方便gdb调试, 还有-ggdb3 支持宏调试等
  8. gcc Wall    # 输出所有警告信息
  9. gcc O2 # 开启调优, O2等级调优
  10.  
  11. gcc I(i大写) # 导入头文件目录,方便 *.h文件查找
  12. gcc L(l 大写)   # 导入库文件目录,方便 *.so和*.a文件查找
  13. gcc l(l 小写) # 导入库文件, 例如-lpthread, 相当于依次查找导入 libpthread.so/libpthread.a 文件
  14. gcc static l(l 小写) # 指定只查找 静态库 lib*.a 文件, linux约定库文件都是 lib开头
  15.  
  16. ar rc libheoo.a hello.o world.o # 将*.o 文件打包成 libheoo.a 静态库
  17. gcc fPIC shared o libheoo.so hello.o world.o # 将*.o 文件打包成 libheoo.so 动态库

到这里储备方面的讲完毕了.   --<-<-<@

前言  -  介绍一下实际例子中语法套路

  首先升级一下上面Makefile文件, 如下(如果你复制没法执行, 请检查规则开头字符是\t)

  1. # 构建全局编译操作宏
  2. CC = gcc
  3. CFLAGS = -g -Wall -O2
  4. RUNE = $(CC) $(CFLAGS) -o $@ $^
  5. RUNO = $(CC) -o $@ $<
  6.  
  7. # 构建伪命令
  8. .PHONY:all clean cleanall
  9.  
  10. # 第一个标签, 是make的开始
  11. all:main.out
  12.  
  13. main.out:main.c
  14. $(RUNE)
  15.  
  16. # 清除操作
  17. clean:
  18. -rm -rf *.i *.s *.o *~
  19. cleanall:clean
  20.    -rm -rf *.out *.out *.a *.so

我们先说一下Makefile中变量的使用, 就是上面 "="那种基础语法说明.

关于Makefile 变量总结如下

  1. 关于上面变量的使用这里做一个总结.
  2.  
  3. a. = 声明变量
  4. 加入存在下面场景

  5. CC = cc

  6. CC = gcc
  7.  
  8. 那么make的时候, $(CC) 就是 gcc, 会全局替换.
  9. 对于 = 声明的可以认为是一个全局递归替换宏.
  10.  
  11. b. := 声明变量
  12.  

  13. srcdir := ./coroutine
  14. tardir := ./Debug

  15. 上面就是一般语言中普通变量.
  16.  
  17. c. ?= 声明变量
  18.  
  19. Foo ?= bar
  20.  
  21. 上面意思是 $(foo)不存在, 那就将 bar 给它. 等同于
  22. ifeq ($(origin FOO), undefined)
  23. FOO = bar
  24. endif
  25.  
  26. d. += 声明变量
  27.  
  28. objects = main.o foo.o bar.o utils.o
  29. objects += another.o
  30. 等同于
  31.  
  32. objects = main.o foo.o bar.o utils.o
  33. objects := $(objects) another.o

趁着热度举个例子, 先不解惑.

  1. CC = cc
  2. FOO := foo
  3. BAR ?= bar
  4. HEO := heo
  5.  
  6. all :
  7. echo $(CC)
  8. echo $(FOO)
  9. echo $(BAR)
  10. echo $(HEO)
  11.  
  12. HEO += world
  13. FOO := FOO
  14. CC = gcc

执行结果如下, 如下图 . 通过Demo外加上下面运行结果图, 应该会有收获.

通过上面我们可以发现 := 和 = 声明的变量都是最终全局替换之后的结果. 他们二者细微差别, 我还是通过例子来说吧.

一切都在不言中, 那么关于Makefile变量中语法讲解完毕. 顺带说一些小细节吧,

  1). Makefile 中 一切从简单开始, 能用 = 就不要用 :=

  2). 变量具备全部作用域 , 推荐全部用大写命名

  3). 多查最开始我推荐的资料

接着变量往后讲,继续分析其它例子

上面 .PHONY 是 Makefile中伪命令. 默认套路写法. 定义命令名称, 可以通过 make 命令名称调用.

其中 all 是Makefile第一个运行目标,  从它入口. clean , cleanall 伪命令 通过 make clean ; make cleanall 执行.

主要是清除生成的中间文件. 希望你能明白, 自己演示一下, 是不是这样的.

这里我们开始一个新的例子了. 具体参照

  C协程库的编译文件  https://github.com/wangzhione/scoroutine/blob/master/Makefile

  1. # 全局替换变量
  2. CC = gcc
  3. CFLAGS = -g -Wall -O2
  4. RUNE = $(CC) $(CFLAGS) -o $@ $^
  5.  
  6. # 声明路径变量
  7. SRC_PATH := ./coroutine
  8. TAR_PATH := ./Debug
  9.  
  10. # 构建伪命令
  11. .PHONY:all clean cleanall
  12.  
  13. # 第一个标签, 是make的开始
  14. all:$(TAR_PATH)/main.out
  15.  
  16. $(TAR_PATH)/main.out:main.o scoroutine.o
  17. $(CC) $(CFLAGS) -o $@ $(addprefix $(TAR_PATH)/, $^ )
  18.  
  19. $(TAR_PATH):
  20. mkdir $@
  21.  
  22. %.o:$(SRC_PATH)/%.c | $(TAR_PATH)
  23. $(CC) $(CFLAGS) -c -o $(TAR_PATH)/$@ $<
  24.  
  25. # 清除操作
  26. clean:
  27.   -rm -rf $(TAR_PATH)/*.i $(TAR_PATH)/*.s $(TAR_PATH)/*.o $(TAR_PATH)/*~
  28. cleanall:clean
  29.   -rm -rf $(TAR_PATH)

从头开始分析它的具体含义.

1) 开头全局变量定义部分, 个人习惯问题其实也可以用 := . 最终得到 RUNE = gcc -g -Wall -O2 -o $@ $^ .

2) 路径声明部分, 用 := 声明, 支持中间拼接. 用=也可以, 都是条条大路同罗马, 自己多检查一下. 以后我可能全部用 = 声明全局递归的字面变量声明.

3) .PHONY 声明了 3个伪命令. 不会立即执行的命令, 依赖 make 命令名称 主动调用

4) all 依赖 于 $(TAR_PATH)/main.out 就是依赖于 ./coroutine/main.out. 刚好下面存在

  1. $(TAR_PATH)/main.out:main.o scoroutine.o
  2. $(CC) $(CFLAGS) -o $@ $(addprefix $(TAR_PATH)/, $^ )

这条规则. 其中又依赖于 main.o 和 scoroutine.o 目标. 那么二者也会做新的目标, 就这样递归的找下去.
后面找到了 %.o, Makefile中%是匹配符, 例如 main.o % 就相当于 main部分.
其中addprefix 是GNU make内置的函数的其中一个, 需要用到的时候多查文档就行了.

为每一个可以分割的子单元上加上一个前缀, 这个前缀就是它的第一个参数.

5) 对于下面这段很实用, 通配符 + | 生成必要文件的语法

  1. %.o:$(SRC_PATH)/%.c | $(TAR_PATH)
  2. $(CC) $(CFLAGS) -c -o $(TAR_PATH)/$@ $<

以上是一个通用匹配规则, %.o 目标依赖于 ..../%.c 具体文件. 后面 | 跟的也是一个依赖目标. 这个目标只会在第一次不存在的时候才会被构建.

更加详细的说明可以参照第一个参照资料 "4.3 Types of Prerequisites" 部分.  这个语法用的很多, 用于构建一次生成所需的目录信息.

6) 最后就是剩余clean, cleanall伪命令. 定义清除中间文件等.

是不是想骂die, 但是上面那些都自行捣鼓了一遍, 基本就越过Makefile初级部分, 能够写出能看的编译文件O(∩_∩)O哈哈~

正文  - 来个小框架Makefile试试水

  先找一个特别老的, 很水的一个Makefile 试试. 具体参照

  一个控制台小项目编译文件  https://github.com/wangzhione/sconsole_project/blob/master/linux_sc_template/Makefile

  1. C = gcc
  2. DEBUG = -g -Wall -D_DEBUG
  3. #指定pthread线程库
  4. LIB = -lpthread -lm
  5. #指定一些目录
  6. DIR = -I./module/schead/include -I./module/struct/include
  7. #具体运行函数
  8. RUN = $(CC) $(DEBUG) -o $@ $^ $(LIB) $(DIR)
  9. RUNO = $(CC) $(DEBUG) -c -o $@ $^ $(DIR)
  10.  
  11. # 主要生成的产品
  12. all:test_cjson_write.out test_csjon.out test_csv.out test_json_read.out test_log.out\
  13. test_scconf.out test_tstring.out
  14.  
  15. #挨个生产的产品
  16. test_cjson_write.out:test_cjson_write.o schead.o sclog.o tstring.o cjson.o
  17. $(RUN)
  18. test_csjon.out:test_csjon.o schead.o sclog.o tstring.o cjson.o
  19. $(RUN)
  20. test_csv.out:test_csv.o schead.o sclog.o sccsv.o tstring.o
  21. $(RUN)
  22. test_json_read.out:test_json_read.o schead.o sclog.o sccsv.o tstring.o cjson.o
  23. $(RUN)
  24. test_log.out:test_log.o schead.o sclog.o
  25. $(RUN)
  26. test_scconf.out:test_scconf.o schead.o scconf.o tree.o tstring.o sclog.o
  27. $(RUN)
  28. test_tstring.out:test_tstring.o tstring.o sclog.o schead.o
  29. $(RUN)
  30.  
  31. #产品主要的待链接文件
  32. test_cjson_write.o:./main/test_cjson_write.c
  33. $(RUNO)
  34. test_csjon.o:./main/test_csjon.c
  35. $(RUNO)
  36. test_csv.o:./main/test_csv.c
  37. $(RUNO)
  38. test_json_read.o:./main/test_json_read.c
  39. $(RUNO)
  40. test_log.o:./main/test_log.c
  41. $(RUNO) -std=c99
  42. test_scconf.o:./main/test_scconf.c
  43. $(RUNO)
  44. test_tstring.o:./main/test_tstring.c
  45. $(RUNO)
  46.  
  47. #工具集机械码,待别人链接
  48. schead.o:./module/schead/schead.c
  49. $(RUNO)
  50. sclog.o:./module/schead/sclog.c
  51. $(RUNO)
  52. sccsv.o:./module/schead/sccsv.c
  53. $(RUNO)
  54. tstring.o:./module/struct/tstring.c
  55. $(RUNO)
  56. cjson.o:./module/schead/cjson.c
  57. $(RUNO)
  58. scconf.o:./module/schead/scconf.c
  59. $(RUNO)
  60. tree.o:./module/struct/tree.c
  61. $(RUNO)
  62.  
  63. #删除命令
  64. clean:
  65. rm -rf *.i *.s *.o *.out __* log ; ls -hl
  66. .PHONY:clean

上面那些注释已经表达了一切了吧, 确实好水. 但是特别适合练手, 每一个生成目标都有规则对应. 费力但是最直接. 实在没有没有好讲的, 扯一点

1) GNU make 指定的编译文件是 makefile 或 Makefile. 推荐用Makefile, 是一个传统吧. 因为C项目都是小写, 用大写开头以作区分.

2) Makefile 中 同样以 \ 来起到一整行的效果

3) 其它目标, 依赖, 规则.只要存在那么Makefile就可以自动推导. 当然它依赖文件创建时间戳, 只有它变化了Makefile才会重新生成目标.

Makefile点心结束了. 以上就是make使用本质, 生成什么, 需要什么, 执行什么. 推荐练练手, 手冷写不了代码.

最后来点水果

  simplec c的简易级别框架  https://github.com/wangzhione/simplec/blob/master/Makefile

  1. ##################################################################################################
  2. # .前期编译辅助参数支持 #
  3. ##################################################################################################
  4. SRC_PATH ?= ./simplec
  5. MAIN_DIR ?= main
  6. SCHEAD_DIR ?= module/schead
  7. SERVICE_DIR ?= module/service
  8. STRUCT_DIR ?= module/struct
  9. TEST_DIR ?= test
  10. TAR_PATH ?= ./Output
  11. BUILD_DIR ?= obj
  12.  
  13. # 指定一些目录
  14. DIR = -I$(SRC_PATH)/$(SCHEAD_DIR)/include -I$(SRC_PATH)/$(SERVICE_DIR)/include \
  15. -I$(SRC_PATH)/$(STRUCT_DIR)/include
  16.  
  17. # 全局替换变量
  18. CC = gcc
  19. LIB = -lpthread -lm
  20. CFLAGS = -g -Wall -O2 -std=gnu99
  21.  
  22. # 运行指令信息
  23. define NOW_RUNO
  24. $(notdir $(basename $())).o : $() | $$(TAR_PATH)
  25. $$(CC) $$(CFLAGS) $$(DIR) -c -o $$(TAR_PATH)/$$(BUILD_DIR)/$$@ $$<
  26. endef
  27.  
  28. # 单元测试使用, 生成指定主函数的运行程序
  29. RUN_TEST = $(CC) $(CFLAGS) $(DIR) --entry=$(basename $@) -nostartfiles -o \
  30. $(TAR_PATH)/$(TEST_DIR)/$@ $(foreach v, $^, $(TAR_PATH)/$(BUILD_DIR)/$(v))
  31.  
  32. # 产生具体的单元测试程序
  33. define TEST_RUN
  34. $() : $$(notdir $$(basename $())).o libschead.a $() | $$(TAR_PATH)
  35. $$(RUN_TEST) $(LIB)
  36. endef
  37.  
  38. ##################################################################################################
  39. # .具体的产品生产 #
  40. ##################################################################################################
  41. .PHONY:all clean cleanall
  42.  
  43. all : main.out\
  44. $(foreach v, $(wildcard $(SRC_PATH)/$(TEST_DIR)/*.c), $(notdir $(basename $(v))).out)
  45.  
  46. # 主运行程序main
  47. main.out:main.o simplec.o libschead.a libstruct.a test_sctimeutil.o
  48. $(CC) $(CFLAGS) $(DIR) -o $(TAR_PATH)/$@ $(foreach v, $^, $(TAR_PATH)/$(BUILD_DIR)/$(v)) $(LIB)
  49.  
  50. # !!!!! - 生成具体的单元测试程序 - 依赖个人维护 - !!!!!
  51. $(eval $(call TEST_RUN, test_array.out, array.o))
  52. $(eval $(call TEST_RUN, test_atom_rwlock.out))
  53. $(eval $(call TEST_RUN, test_cjson.out, tstr.o))
  54. $(eval $(call TEST_RUN, test_cjson_write.out, tstr.o))
  55. $(eval $(call TEST_RUN, test_csv.out, tstr.o))
  56. $(eval $(call TEST_RUN, test_json_read.out, tstr.o))
  57. $(eval $(call TEST_RUN, test_log.out))
  58. $(eval $(call TEST_RUN, test_scconf.out, tstr.o tree.o))
  59. $(eval $(call TEST_RUN, test_scoroutine.out, scoroutine.o))
  60. $(eval $(call TEST_RUN, test_scpthread.out, scpthread.o scalloc.o))
  61. $(eval $(call TEST_RUN, test_sctimer.out, sctimer.o scalloc.o))
  62. $(eval $(call TEST_RUN, test_sctimeutil.out))
  63. $(eval $(call TEST_RUN, test_tstring.out, tstr.o))
  64. $(eval $(call TEST_RUN, test_xlsmtojson.out, tstr.o))
  65.  
  66. ##################################################################################################
  67. # 2.先产生所需要的所有机器码文件 #
  68. ##################################################################################################
  69.  
  70. # 循环产生 - 所有 - 链接文件 *.o
  71. SRC_CS = $(wildcard\
  72. $(SRC_PATH)/$(MAIN_DIR)/*.c\
  73. $(SRC_PATH)/$(TEST_DIR)/*.c\
  74. $(SRC_PATH)/$(SCHEAD_DIR)/*.c\
  75. $(SRC_PATH)/$(SERVICE_DIR)/*.c\
  76. $(SRC_PATH)/$(STRUCT_DIR)/*.c\
  77. )
  78. $(foreach v, $(SRC_CS), $(eval $(call NOW_RUNO, $(v))))
  79.  
  80. # 生产 -相关- 静态库
  81. libschead.a : $(foreach v, $(wildcard $(SRC_PATH)/$(SCHEAD_DIR)/*.c), $(notdir $(basename $(v))).o)
  82. ar cr $(TAR_PATH)/$(BUILD_DIR)/$@ $(foreach v, $^, $(TAR_PATH)/$(BUILD_DIR)/$(v))
  83. libstruct.a : $(foreach v, $(wildcard $(SRC_PATH)/$(STRUCT_DIR)/*.c), $(notdir $(basename $(v))).o)
  84. ar cr $(TAR_PATH)/$(BUILD_DIR)/$@ $(foreach v, $^, $(TAR_PATH)/$(BUILD_DIR)/$(v))
  85.  
  86. ##################################################################################################
  87. # 3.程序的收尾工作,清除,目录构建 #
  88. ##################################################################################################
  89. $(TAR_PATH):
  90. -mkdir -p $@/$(BUILD_DIR)
  91. -mkdir -p $@/test/config
  92. -cp -r $(SRC_PATH)/test/config $@/test
  93.  
  94. # 清除操作
  95. clean :
  96. -rm -rf $(TAR_PATH)/$(BUILD_DIR)/*
  97.  
  98. cleanall :
  99. -rm -rf $(TAR_PATH)

具体可以参照simplec 项目查看, 我们抽一部分重点讲解

  1. define NOW_RUNO
  2. $(notdir $(basename $())).o : $() | $$(TAR_PATH)
  3. $$(CC) $$(CFLAGS) $$(DIR) -c -o $$(TAR_PATH)/$$(BUILD_DIR)/$$@ $$<
  4. endef

上面定义了一个语句块 NOW_RUNO. 其中语句块中除了要接收的参数可以用$(1), $(2) ..., 其它都是两个$$开头, 否则就被替换了. 使用方法就是

  1. $(eval $(call NOW_RUNO, $(v)))

通过$eval(), $(call ) 这种套路调用. call NOW_RUNO, 后面添加都是 NOW_RUNO语句块的函数了.

这里说一个Makefile处理的潜在小问题, 当你传入参数是依赖项时候, 如果不是直接通过唯一一个参数传入进去,

那么解析的是当成多个依赖项处理.所以上面只有 $(1)做依赖项.

Makefile中 foreach语法也很好用等同于shell语法传参方式.

  1. $(foreach v, $^, $(TAR_PATH)/$(BUILD_DIR)/$(v))
    将第二个$^通过空格分隔成单个的v代替, 被替换为第三个中一部分. $(foreach ...)执行完毕最终返回一个拼接好的串

在简单补充几个函数说明 例如

  1. $(1) => $$(notdir $$(basename $())).o <=> ./simplec/main/main.c => main.o

其中 nodir函数得到文件名, basename函数得到文件名不包过.和.后面部分.
wildcard 函数是得到指定匹配规则下的文件全路径拼接.
最后面 -rm 那些, 加了前缀 - 是为了当Makefile执行到这如果运行出错, 不停止继续前行.
通过上面Makefile最终跑起来后, 会生成一个Output目录, 再在内部生成 obj, test, ...
还是很有学习价值的. 有兴趣的可以试试.
希望通过上面讲解, 能够使你以后阅读其它更高级项目的编译文件不那么生疏. (* ̄(エ) ̄)

后记  -  突然想起了什么, 笑了笑 我自己 ...

  伽罗  -  http://music.163.com/#/artist/desc?id=21309

Makefile 跟着走快点的更多相关文章

  1. 跟着Sedgewick学算法(week 1 UnionFind)

    发现笔记转过来,没有图的~~~~~~~~~~~悲剧,给出共享笔记链接 https://www.evernote.com/pub/yanbinliu/algorithm 很久之前就在coursera看到 ...

  2. 一个五年 Android 开发者百度、阿里、聚美、映客的面试心经

    花絮 也许会有人感叹某些人的运气比较好,但是他们不曾知道对方吃过多少苦,受过多少委屈.某些时候就是需要我们用心去发现突破点,然后顺势而上,抓住机遇,那么你将会走向另外一条大道,成就另外一个全新的自我. ...

  3. 萝卜德森的sublime笔记中文翻译版

    我已经使用subliem编辑器版本2接近2个月了,并且我在其中找到了一堆有用的技巧.我发觉应该写下这些技巧,为那些对此感兴趣的人们.我会尽力的详细描述,那些看起来像魔法一样的东西,因为很多非常“酷”的 ...

  4. Java和.net对比分析

    .Net和Java是国内市场占有率最高的两门技术,对于准备学习编程语言的初学者来说,.Net和Java是初学者首先考虑的两门技术,因此很多人一遍遍的问“学.Net还是学Java”,社区中也每天都有“. ...

  5. 如何进入百度、阿里,一个6年Android老司机的面经

    花絮 也许会有人感叹某些人的运气比较好,但是他们不曾知道对方吃过多少苦,受过多少委屈.某些时候就是需要我们用心去发现突破点,然后顺势而上,抓住机遇,那么你将会走向另外一条大道,成就另外一个全新的自我. ...

  6. [LeetCode] Remove Nth Node From End of List 移除链表倒数第N个节点

    Given a linked list, remove the nth node from the end of list and return its head. For example, Give ...

  7. 熟悉HTML CSS布局模型

    HTML最难的地方来了!这个我反复了很多遍, 包括现在写博客, 也对我自己算是一种温习, 我这块怕是没办法写的很好懂, 因为我自己还不能把我学到的准确通俗易懂的表达出来, 给自己记个笔记, 以后再来一 ...

  8. 菜鸟的Python学习之路(流水账)

    揭开Python的面纱 开始是因为别人说Python简单才开始学的,然后那段时间刚考完研,也没什么事,就多少瞅了瞅,然后发现语法的确简单很多,或者说简洁更合适. 当时看的是简明Python教程,没用多 ...

  9. CSS补充之--页面布局、js补充,dom补充

    CSS补充之--页面布局 主站一:(下面是一个大致的模板) <div class="pg-header"> <div style="width: 120 ...

随机推荐

  1. linux 服务器丢包故障排查

    项目开了个P2P服务器,但是运行一段时间就会出现丢包问题,具体表现为:1.udp丢包严重(一分钟收发分别1.5W) 2.ssh(用于运维指令)连接不上该服务器(超时) 3.服务器运行好像没什么异常,u ...

  2. 【刷题】BZOJ 1001 [BeiJing2006]狼抓兔子

    Description 现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的,而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一个 ...

  3. [CF1037H] Security

    题目链接 codeforces. 洛谷. Solution 按照套路,可以\(SAM\)上线段树合并求出\(endpos\)集合,然后随便贪心一下就好了. #include<bits/stdc+ ...

  4. BZOJ1901:Zju2112 Dynamic Rankings——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=1901 Description 给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序 ...

  5. IP协议简介

    一.IP 1.IP是TCP/IP协议簇中最为核心的协议,所有的TCP.UDP.ICMP及IGMP数据都是以IP数据报格式传输. 2.IP提供不可靠.无连接的数据报传送服务 (1)不可靠:不保证IP数据 ...

  6. ContestHunter暑假欢乐赛 SRM 01 - 儿童节常数赛 爆陵记

    最后15min过了两题...MDZZ 果然是不适合OI赛制啊...半场写完三题还自信满满的,还好有CZL报哪题错了嘿嘿嘿(这算不算犯规了(逃 悲惨的故事*1....如果没有CZL的话T1 10分 悲惨 ...

  7. A星寻路算法-Mind&Hand(C++)

    //注1:Mind & Hand,MIT校训,这里指的理解与实现(动脑也动手) //注2:博文分为两部分:(1)理解部分,为参考其他优秀博文的摘要梳理:(2)代码部分,是C++代码实现的,源码 ...

  8. 关于jmf不能播放mp3的问题解决

    想写个JAVA的MP3音乐管理器,使用JMF插件,但发现运行时总报一个异常: Unable to handle format: mpeglayer3, 44100.0 Hz, 16-bit, Ster ...

  9. centos中mysql的安装

    一:前沿 过完年了,花了不少钱啊!本来还打算买电脑的了,结果这个事情还是的延期啊!苍天啊!刚刚也看了下,一台苹果也大概是1w左右!买吧!boy!别犹豫了吧!好吧现在来说说我自己的工作吧!现在过完年到公 ...

  10. 数据结构:hash_map

    在C++11以上的开发环境中,请直接把map替换成unordered_map,不要使用hash_map 之前我们专门有一篇介绍哈希表,多维哈希表的博文,当时就是以map举例子,然后说了一句把map替换 ...