
C
文章平均质量分 57
跟随 bennyhuo 老师重新学C
gltzlike
To be a better man !
展开
-
14-8 打包发布 GTK 程序
为了使 GTK 程序在他人的Windows 系统上运行,需要将该程序进行打包发布。打包程序的关键只有两步,一,将执行程序需要和动态链接库放置在同一目录;二,设置程序可以直接点击运行,不会同时启用控制台窗口。1. 放置动态链接库 1)准备工作,新建文件夹用来放置配置文件、执行程序及动态链接库。 2)使用 Listdlls.exe (微软网站下载)查看程序所依赖的动态链接库。具体做法为:在一个控制台下启动 04.config_t...原创 2021-08-29 21:21:02 · 1140 阅读 · 2 评论 -
14-7 使用 css 调控样式
1. 修改前端样式可以将 ui 文件与 css 文件进行关联,类似于 html 和 css 的关系。只不过需要注意的是,前端样式可以借助 glade 进行修改,但并没有将修改应用至程序窗口,仅仅提供了预览功能。样式修改后还需要修改后台代码应用至程序窗口。后台代码获取到 css 样式文件,并将其运用到窗口程序。 前端 ui 文件与 css 文件进行关联的具体过程为: 1)点击 glade 的项目设置,设定 ui 文件链接的 css 文件目录。 ...原创 2021-08-29 13:52:43 · 436 阅读 · 0 评论 -
14-6 使用 glade 完成布局
1. 使用glade 新增对象及信号 前端:使用 glade 修改布局与信号。例如,在 glade 中新建对象 GtkTextBuffer,在将其 id 命名为 button2_data,内容为 “你好,中国!”。 设置构件按钮2点击信号的回调函数为 print_hello2,且在调用该函数时传入button2_data 参数。2. 实现代码与glade 新增对象及信号的链接 后端:修改代码,实现前端修改的样式。具体修改流程为: ...原创 2021-08-29 11:01:33 · 571 阅读 · 0 评论 -
14-5 使用 xml 完成布局
14-3 小节中,helloworld程序创建窗口使用代码创建。此外,也可使用 xml 完成窗口布局,代码仅仅用来处理数据逻辑,实现布局和数据处理相分离。布局的 ui 代码,builder.ui:<interface> <object id="window" class="GtkWindow"> <property name="visible">True</property> &...原创 2021-08-27 17:39:18 · 417 阅读 · 0 评论 -
14-4 为 GTK 应用配置主题(部分待定)
GTK 默认的主题风格是 Linux,可以将其设定为 Windows 和 Mac 相应的主题。主题设定完成后,窗口、按钮等构件的样式会发生变化。1. 全局修改 将所有样式(包括 glade 内部所有样式)进行一次性修改。修改流程如下: 1)修改配置文件,将 GTK样式设置为 Windows 样式。前往 msys 的安装路径,找到 msys\mingw64\etc\gtk-3.0 目录,打开 settings.ini 文件(如果没有就创建),写入如下内容...原创 2021-08-27 16:09:47 · 1389 阅读 · 2 评论 -
14-3 编写第一个 GTK 程序
1. 前端后端,此部分属于后端内容。如何给页面上的按钮添加点击事件。如何利用代码写一份布局。原创 2021-08-27 13:52:26 · 432 阅读 · 0 评论 -
14-2 安装 GTK
注意,Windows 系统下安装 GTK 利用的是 msys,这意味着后续所有代码只能依靠 mingw 编译器实现,不能使用 msvc。 具体安装流程如下: 1)打开 msys2.exe,输入以下代码:pacman -S mingw-w64-x86_64-gtk3 2)打开 mingw64.exe,查询编译环境中是否含有GTK 相关的包。输入如下代码。pkg-c...原创 2021-08-25 15:56:45 · 540 阅读 · 0 评论 -
14-1 GUI 开发概述
图形用户界面 GUI(Graphical User Interface)的编程情况大致如下。其中 GTK 扩展到了 Windows 和 Mac 系统,Qt 框架偏重于 PC 端开发,而 Flutter 偏重于移动端的开发。只要添加一个中间层,可以轻松将 GTK+ 应用至其他语言。(注意,GTK 和 GTK+ 是两种事物)...原创 2021-08-25 15:20:27 · 451 阅读 · 0 评论 -
13-6 使用 C++ 调用 C 程序
在 C++ 工程下,调用C 语言书写的函数,其具体流程如下: 1)准备工作,创建 C++ 工程 06.call_from_cpp,其中需要引入两个头文件,一个头文件利用 C 语言实现,一个头文件利用 C++ 语言实现。 06.call_from_cpp.cpp 文件内容为:#include <iostream>#include "factorial.h"#include "fibonacci.h...原创 2021-08-25 14:38:45 · 356 阅读 · 0 评论 -
13-5 使用 Conan 管理依赖
1. Conan 的安装 类似于 Java 使用 maven 管理依赖,C/C++ 可使用 Conan 进行依赖的管理。 安装 Conan 非常简单,在安装完成 python 的前提下,使用 pip install conan 即可进行 Conan 的安装。安装完成后,在控制台下输出 conan 可以查看相应的命令使用方法:2. 利用Conan 引入 libcurl 1)首先需要安装相关依赖,以 libcurl 为例。在主工程...原创 2021-08-25 11:40:12 · 1206 阅读 · 0 评论 -
13-4 使用 CMake 完成库的编译和链接
在正常生产过程中,很少使用命令行编译和运行静态链接库和动态链接库,而是使用 CMake 完成静态链接库和动态链接库的编译。 以 04.library_with_cmake.c 为例,将子工程 fibonacci 编译为动态链接库,子工程 factorial 编译为静态链接库。 04.library_with_cmake.c 的内容为:#include <stdio.h>#include "factorial/factorial.h"#include ...原创 2021-08-24 16:55:50 · 1191 阅读 · 0 评论 -
13-3 动态链接库的编译和使用
1. 静态链接库与动态链接库 由于静态链接库不能共享,且依赖的符号的对应目标文件与主程序文件需要一同编译,故静态链接库内存空间占用较大。而动态链接库具有共享性质,通过特定路径即可引用,可以有效减少内存空间的占用。 此外,可使用 ldd 命令查看一个可执行程序所依赖的动态链接库。2. 编译动态链接库以主程序文件03.shared_library.c 为例,编译运行时需要的动态链接库,便于后续外部函数的执行。此主程序文件与上一小节的主程序文件内...原创 2021-08-24 00:42:30 · 1613 阅读 · 0 评论 -
13-2 静态链接库的构建和使用
1. 静态链接库与动态链接库 程序编译时发生的动作称为静态行为,程序运行时发生的动作称为动态行为。故链接共分为两种,静态链接和动态链接。目前来看,链接使用的原因在于,主程序文件执行时需要引入头文件,执行外部函数。而引入头文件时,在编译阶段确定外部函数的地址还是在运行阶段确定外部函数的地址,由此产生了静态链接和动态链接两种方案。静态链接是在编译阶段确定外部符号地址,并将依赖的符号其对应的目标文件与主程序文件一同编译,形成最后的可执行程序。动态链接是在程...原创 2021-08-23 22:33:02 · 234 阅读 · 0 评论 -
13-1 可执行程序的编译过程
1. 编译流程 使用 gcc 编译器,将源代码 .c 文件一步一步编译至可执行程序。gcc 编译器在 Windows,Linux,Mac 均可正常编译。编译具体过程如下: 1)打开msys 目录下的 mingw64.exe,跳转至待编译源码的目录。 2)利用预处理器,将 .c 源代码文件转为 .i 宏替换后的源代码文件。gcc 编译器中输入以下代码实现宏替换。注...原创 2021-08-23 19:30:57 · 333 阅读 · 0 评论 -
12-10 案例:并发任务执行与函数回调
1. 问题来源 使用多线程进行文件下载,下载功能的实现由线程睡眠模拟,下载进度在控制台进行查看。 此问题可使用一个主线程与多个子线程解决。其中主线程功能为:1) 查看各子线程的下载进度2) 查看剩余的待下载任务数量子线程功能为:1) 执行下载任务2) 下载任务完成后,发出通知,执行回调函数#include...原创 2021-08-23 14:46:21 · 231 阅读 · 0 评论 -
12-9 案例:处理复杂的线程返回结果
1. 问题来源 thrd_create() 函数功能为新建一个线程,传入待执行的函数。待执行函数的格式要求如下:typedef int (*thrd_start_t)(void *arg);这意味着待执行函数只能返回 int 类型值,接收 void* arg 参数。若待执行函数想要返回一个结构体,该如何操作?2. 解决方案 解决方案一:由于返回值为 int 类型,指针的本质也是一串数字,所以可将结构体指针进行返回,从而返回结构...原创 2021-08-23 08:58:18 · 136 阅读 · 0 评论 -
12-8 副作用与纯函数
1. 副作用函数副作用:指当调用函数时,除了返回函数值之外,还对主调用函数产生附加的影响。例如修改全局变量(函数外的变量)或修改参数。表达式副作用:在表达式求值过程中,需要获取变量的值,但并不改变这些变量的值,这样的表达式称为无副作用的表达式。一个表达式在求值过程中,对使用的变量不但引用,对它们的值还加以改变,这样的表达式称为有副作用的表达式。例如 count++,表达式获取的是 count 值,但在获取过程中对 count 变量的值进行了加 1 操作,故此表达式是有...原创 2021-08-22 23:29:30 · 299 阅读 · 0 评论 -
12-7 Thread Local 线程变量(部分待定)
1. _Thread_local _Thread_local 可以将指定变量当做线程的内部变量,避免线程之间的变量共享。以 12-3 节的 count++ 为例,若 count 变量是每个线程内部的变量,则不会出现线程安全问题。故将代码修改如下:#include <stdio.h>#include <tinycthread.h>// 创建线程生命周期的变量 main_count,线程结束时自动释放_Thread_local int main_count...原创 2021-08-21 23:10:37 · 198 阅读 · 0 评论 -
12-6 锁
加锁可保证原子性操作和共享资源的可见性,但加锁成本相对较高。 mingw 编译器下,可用 mtx_t 定义一个锁,mtx_init() 函数初始化一个锁,mtx_destory() 函数销毁一个锁,mtx_lock() 函数加锁,mtx_lock() 函数解锁。mtx_timedlock() 函数和 mtx_lock() 函数功能类似,mtx_lock()函数加锁不成功时会一直等待,而 mtx_timedlock() 函数加锁不成功时可设置等待时间。mt...原创 2021-08-21 18:10:27 · 424 阅读 · 0 评论 -
12-5 原子类型
1. atomic_int 12-3 中线程安全问题来源之一为非原子操作,而利用 C11 新增的 atomic_int 类型,其定义的变量的相关操作均为原子操作。修改12-3 第 1 小节的部分代码,将 int count = 0; 改为 atomic_int count = 0。程序最终结果为 count 等于2000000,与12-3 第 1 小节的情况截然不同。 但目前仅 gcc 编译器支持 <stdatomic.h>,msvc 不支持。#...原创 2021-08-21 16:26:18 · 1851 阅读 · 0 评论 -
12-4 volatile
1. 使用情况 volatile 与 12-3 中的代码重排序有关。在 12-3 的第三小节,由于添加set(CMAKE_C_FLAGS "-O3") 导致编译器对汇编指令进行了优化。此时可使用volatile 关键字,确保被修饰的变量对应的汇编指令不被优化。#include <stdio.h>#include <tinycthread.h>int flag = 0;int a = 0;int x = 0;int T1(vo...原创 2021-08-17 17:09:37 · 85 阅读 · 0 评论 -
12-3 资源的线程安全问题
1. 线程安全问题的来源之一 ——对共享资源进行非原子操作的修改 使用两个线程执行 Counter 函数,对 count 变量分别自加 100 万次,最后结果为 1049042,并非 200 万。 原因在于 count++ 操作并非原子操作。count++ 操作可拆解为 3 步:int temp = count,count = temp + 1,returntemp。线程 1 执行完第一步,还没执行第二步时,线程 2 可能此时已经开始执行第一步,即 temp = co...原创 2021-08-17 16:03:56 · 125 阅读 · 0 评论 -
12-2 线程的基本用法
1. 创建线程 使用 thrd_create() 函数创建新线程。thrd_create() 函数接收三个参数,一是新定义的线程地址,二是函数指针,三是函数指针对应的函数的参数。整体意义为开辟一个新线程,运行传入的函数指针指向的函数。#include <stdio.h>#include <tinycthread.h>int SayHello(char *name){ printf("This is a new thread, location: [...原创 2021-08-16 22:58:36 · 354 阅读 · 0 评论 -
12-1 线程基础
1. 下载相关的线程开源库由于 msvc 并没有完整的线程包,所以需要去相关网站下载线程开源的库:http://tinycthread.github.io/。2. 线程简介 线程是操作系统中进行运算调度的最小单位,它解决了同一个进程同一时刻只能占用一个核的问题,解决了同一个进程只能串行执行程序的问题。 线程是程序的载体。...原创 2021-08-16 18:45:05 · 132 阅读 · 0 评论 -
11-14 其他常见的文件操作
1. 删除文件 删除文件使用 remove() 函数,如果成功删除返回 0,不成功返回 -1。void TestRemove(){ int result = remove("data/main.c"); printf("result: %d\n", result); // result: 0 int result2 = remove("data/main2.c"); printf("result: %d\n", result2); // result: -1...原创 2021-08-16 16:42:47 · 116 阅读 · 0 评论 -
11-13 输入输出流的位置
1. 获取文件流的读取位置 文件打开后,默认从 0 位置开始读取。读取一定字节后,文件流的位置和字节数保持一致。使用 ftell() 函数可以获取当前文件流的读取位置。原创 2021-08-16 14:27:48 · 184 阅读 · 0 评论 -
11-12 统计文件字符个数(部分待定)
1. 统计中文文本的字符个数使用宽字符相关函数 wcslen() 以及宽字符文件读取相关函数 fgetws() 统计中文文本的字符个数。需要注意的是,设定 GBK 编码的代码在不同系统下不同。windows 系统下 设定 GBK 编码的代码为 setlocale(LC_ALL, "chs"),其他系统(Linux,Mac)下设定 GBK 编码的代码为 setlocale(LC_ALL, "zh_CN.gbk")。设定 GBK 编码后,读取文件的代码是一致的,均为fi...原创 2021-08-14 22:13:48 · 379 阅读 · 0 评论 -
11-11 重定向标准输入输出流(部分待定)
1. freopen() 将 stdout 重定向到其他文件流 stdout 是输入到控制台中的文件流,但可以通过 freopen() 函数将其重定向到其他文件流,即输出内容不再显示在控制台,而在指定文件流(例如自定义的文件)中。例如,将 stdout 原本输出到控制台的内容输出到 output.txt 文件中。void TestFreopen(){ // attention: Chinese cannot be written here! freopen("output....原创 2021-08-14 15:33:54 · 418 阅读 · 0 评论 -
11-10 格式化文本的输入输出
1. scanf() 和printf() scanf() 和 printf() 函数日常使用较多,此处不多介绍了。详细内容可参考:https://zh.cppreference.com/w/c/io/fscanf。2.sscanf() 和sprintf() sscanf() 可类比于 scanf() 函数。scanf() 从控制台读取格式化输入,传入到特定变量中。例如 scanf("%d", &a) 把控制台输入的内容传入到 a 中。而 sscanf...原创 2021-08-14 09:18:38 · 262 阅读 · 0 评论 -
11-9 复制一个文件(方法三)
1. 利用 fread() 和 fwrite() 函数进行文件复制 方法一,利用读写一个字符的函数进行文件复制,速度较慢,但是适用于文本和二进制文件。方法二,利用读写一行字符的函数进行文件复制,速度较快,但仅适用于文本文件。使用 fread() 和 fwrite() 函数复制文件,速度最快,同时适用于文本和二进制文件。注意,任何文件本质都是二进制文件,故适用于二进制文件的函数,一定适用于文本文件。fread() 和 fwrite() 函数复制文件的代码如下所示。注意,前...原创 2021-08-13 21:55:07 · 350 阅读 · 0 评论 -
11-8 序列化和反序列化的基本实现
1. x xx原创 2021-08-13 20:45:17 · 125 阅读 · 0 评论 -
11-7 读写指定大小的字节
1. 字节 一个字节 8 位。例如在 ASCII 码表中,0000 1010 表示换行。若从十六进制角度看,则结果为 0a。CLion debug 便是以十六进制查看的字节。2. 读字节 fread() 函数用于指定字节大小的读取,该函数可读取二进制文件。fread() 共四个参数:一,void *,传入一个指定类型去读。二,指定类型中每个元素的大小。三,指定类型中元素的个数。四,文件流。例如传入 int 类型数组,每个元素大小为 4 个字节,再传入数组元素总个数...原创 2021-08-13 17:47:50 · 409 阅读 · 0 评论 -
11-6 复制一个文件(方法二)
1. 采用读一行字符和写一行字符的相关函数复制文件 使用 fgets() 和 fputs() 函数进行文件的复制。需要注意 fopen() 文件流打开失败时返回 NULL,ferror() 是判断文件读取是否失败,feof() 是判断文件是否读取结束。#include <stdio.h>#include <sys/timeb.h>#include <locale.h>// 定义七种情况#define COPY_SUCCESS 0#def...原创 2021-08-13 15:16:50 · 154 阅读 · 0 评论 -
11-5 读写一行字符
1. 读一行字符 gets() 与 gets_s() 都可以用做读取一行字符。gets() 仅接收一个参数 char*,读取到换行符结束。该函数的问题在于 char* 对应的 char buffer[ ] 数组大小不易设置。2. 写一行字符 xx不能用于二进制文件,只能用作文本文件。...原创 2021-08-13 13:40:25 · 416 阅读 · 0 评论 -
11-4 复制一个文件(方法一)
1. 采用读一个字符和写一个字符的相关函数复制文件注意点一:判断目标文件是否打开的前提是,源文件已经可以正常打开了。如果目标文件打开失败,需要将源文件的文件流关闭,以免消耗系统资源。 注意点二:函数 ferror()的用法。先利用 getc() 函数读取字符,获得其返回值。若返回值为 EOF,则判断 EOF 的原因是正常读取结束还是读取失败。ferror() 函数接收文件流参数,如果返回值非 0,说明读取失败。 注意点三:通常,写入文件的...原创 2021-08-12 00:02:32 · 169 阅读 · 0 评论 -
11-3 读写一个字符
1. 读一个字符 读一个字符的函数有三种,分别为 getchar(),getc() 以及 fgetc()。其中 getc() 和 fgetc() 实现方式不一样,但功能相同。getc() 利用宏实现,fgetc() 利用函数实现。此外,注意返回值是个整数。 stdin 是控制台输入,getc(stdin) 可以获得2. 写一个字符 x...原创 2021-08-11 20:58:18 · 164 阅读 · 0 评论 -
11-2 文件流的缓冲
1. 缓冲的来源通常,文件数据的读写效率远低于内存读写效率。如果不存在缓冲,CPU 在读取文件数据时需要先发送指令给 DMA(专门处理 IO数据读写的硬件),而后 DMA 从文件数据读取一个 0 到内存,CPU 再处理该数据。此过程 CPU 全程参加,极大浪费了 CPU 资源。文件流缓冲的目的正是降低 IO 对于 CPU 占用。如图所示,在添加缓冲区后,DMA 可以将文件数据先读取到缓冲区中,等待缓冲区满时,再通知 CPU,此时 CPU 将缓冲区的数据读取并处理...原创 2021-08-11 16:22:32 · 419 阅读 · 0 评论 -
11-1 输入输出流
1. 文件打开方式 打开方式通常有 'r' 'w' 'a' 三种,分别代表 只读,只写,附加。三种操作加 't'表示以文本文件形式打开,加 'b' 表示以二进制文件形式打开。什么都不加的情况下,默认打开文本文件。二进制文件和文本文件的区别: 1)在 windows 系统文本文件下,文件以 "\r\n" 代表换行。若以文本模式打开文件,并用 fputs 等函数写入换行符 "\n" 时,函数会自动在 "\n" 前面加上 "\r" 。即实际写入文件的是 "...原创 2021-08-11 14:02:36 · 108 阅读 · 0 评论 -
10-6 计算时间差
时间差可以用来衡量程序运行效率,计算其有三种方法,一是使用 difftime() 函数,二是使用自定义的 TimeInMillisecond() 函数,三是使用clock()函数。1. 使用 difftime() 函数计算秒级时间差 三种方法本质相同,都是利用执行程序前的时间和程序执行后的时间进行时间差的计算。需要注意的是,difftime() 函数传入的参数为 time_t 类型,这意味着时间精度是秒级。最终程序运行结果为 times: 0.000000,说明程序...原创 2021-08-10 11:22:14 · 1079 阅读 · 0 评论 -
10-5 解析时间
1. Linux平台下,利用strptime() 函数解析字符串时间 利用 strftime() 函数可将struct tm 类型转化为字符串格式的时间,同样也可将字符串格式的时间转化为 struct tm 类型。在 Linux 平台下,可以借助 strptime() 函数完成此功能。strptime() 共三个参数,一是待解析的字符串,二是待解析字符串的格式,三是解析完成后的 struct tm 类型的时间变量。返回值为未能解析的部分字符串。#define _XOPEN_SOU...原创 2021-08-10 09:31:03 · 347 阅读 · 0 评论