【c2】编译预处理,gdb,makefile,文件,多线程,动静态库

文章目录

  • 1.编译预处理:C源程序 - 编译预处理【#开头指令和特殊符号进行处理,删除程序中注释和多余空白行】- 编译
  • 2.gdb调试:多进/线程中无法用
  • 3.makefile文件:make是一个解释makefile中指令的命令工具
  • 4.文件:fprint/fgets/fwrite/fread,ftell/rewind/fseek/fflush
    • 4.1 文本文件:FILE结构体
    • 4.2 二进制文件:没有行概念
    • 4.3 文件定位:linux下文本文件模式和二进制文件模式没有区别。fgets和fprintf以行方式读写文本文件,但不能读写二进制文件。用fread和fwrite可以读写文本文件和二进制文件
  • 5.多线程:pthread_create(),查看线程top -H,ps -xH | grep
    • 5.1 子线程未执行:join
    • 5.2 线程传参区分线程:"th1"
    • 5.3 两子线程数字相加:分别加到自己线程变量中
    • 5.4 两个线程同时加到一个全局变量s中:5000数字小不会影响
      • 全局变量S++要加锁:数字大出现race condition
    • 5.5 假共享:两线程分别加到自己result数组中,0和1两个线程,两个result数组(一个数字累加)
  • 6.动静态库:.a,指定.so,LD_
    • 6.1 静态库:链接库的文件名是libpublic.a,链接库名是public,缺点使用的静态库发生更新改变,程序必须重新编译
    • 6.2 动态库:动态库发生改变,程序不需要重新编译,动态库升级方便
    • 6.3 libc(标准):gnu libc(glibc)(实现)
    • 6.4 编译时为什么要加上 –lm ?:man exp:Link with -lm


1.编译预处理:C源程序 - 编译预处理【#开头指令和特殊符号进行处理,删除程序中注释和多余空白行】- 编译

在这里插入图片描述
条件编译:最常用的两种格式#ifdef和#ifndef 。#undef :取消已定义的标识符
在这里插入图片描述
在这里插入图片描述
如下book145.c和_public.c都有 #include"_public.h",会重复包含。
在这里插入图片描述
在_public.c中如下这样写,_public.h就不会被重复包含。
在这里插入图片描述

2.gdb调试:多进/线程中无法用

root 用户:yum -y install gdb,gdb -v。
在这里插入图片描述
在这里插入图片描述

3.makefile文件:make是一个解释makefile中指令的命令工具

vi gcc.sh 如下,sh gcc.sh。gcc -o 目标 依赖1 依赖2。makefile命令能被执行条件有两个:1.目标不存在,2.依赖已更新。
在这里插入图片描述
如上若只需要编译book2,单个文件改变不重复编译其他文件即增量编译。vi makefile,$前一个tab键不能8个空格。make默认是make all,如果将all这行book3删除,则make不会编译book3,可指定make book3,book3相当于标签。-欧2是让编译效率最高,一般正式发布用。gcc命令选项 :-c编译不链接。
在这里插入图片描述

4.文件:fprint/fgets/fwrite/fread,ftell/rewind/fseek/fflush

4.1 文本文件:FILE结构体

在这里插入图片描述
在这里插入图片描述
vi /tmp/test1.txt,可见有5行记录,不管执行多少次都是5行记录,因为文件打开方式是w,每次打开文件时都会清空原文件中的记录。
在这里插入图片描述
在这里插入图片描述

int main() {
    int fd;
    char buffer[5] = {0};
    ssize_t bytesRead;

    fd = open("/home_a/abcd", O_RDONLY, 0444);
    bytesRead = read(fd, buffer, 4);
    if (bytesRead == -1) {
        printf("read error");
        return 1;
    }
    printf("Read %zd bytes: %s\n", bytesRead, buffer);  //Read 4 bytes: 0xff
    return 0;
}

4.2 二进制文件:没有行概念

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <stdio.h>
int main()
{
    char *filename = "Shanghai";
    FILE *fp = fopen(filename, "rb");
    if (fp == NULL)
    {
        printf("打开%s文件错误", filename);
        return -1;
    }
    int n;
    for (int i = 0; i < 10000; i++)
    {
        fread(&n, sizeof(int), 1, fp);
        printf("%d\n ", n);
    }
    fclose(fp);
    return 0;
}

在这里插入图片描述
在这里插入图片描述

4.3 文件定位:linux下文本文件模式和二进制文件模式没有区别。fgets和fprintf以行方式读写文本文件,但不能读写二进制文件。用fread和fwrite可以读写文本文件和二进制文件

文件内部有一个位置指针,用来指向当前读写的位置,也就是读到第几个字节。在文件打开时,如果打开模式是r和w,位置指针指向文件的第一个字节。如果打开模式是a,位置指针指向文件的尾部,每当从文件里读n个字节或文件里写入n个字节后,位置指针会后移n个字节。

文件位置指针与C中指针不是一回事,位置指针仅仅是一个标志,表示文件读写到的位置即读写到第几个字节,不表示地址。文件每读写一次,位置指针就会移动一次,不需要你在程序中定义和赋值,由系统自动设置。
在这里插入图片描述

#include <stdio.h>
int main(int argc, char **argv) 
{
    FILE *fp=fopen("/sys/bus/i2c/devices/20-0048/hwmon/hwmon1/in0_input","w");
    if(!fp)
    {
        puts("fail");
    }
    fclose(fp);
}

FILE *fptime;
fptime=fopen("/tmp/time","w");
time_t time_log = time(NULL);
struct tm* tm_log = localtime(&time_log);
fprintf(fptime, "flag[%d]  LINE[%d]    %04d-%02d-%02d %02d:%02d:%02d\r\n",sensor_flag, __LINE__, tm_log->tm_year + 1900, tm_log->tm_mon + 1, tm_log->tm_mday, tm_log->tm_hour, tm_log->tm_min, tm_log->tm_sec);
fflush(fptime);
fclose(fptime);

if(rc<0)
{
   FILE *fpLedLog=fopen("/tmp/error","a");
   fprintf(fpLedLog,"error__%u__\r\n",__LINE__);
   fclose(fpLedLog);
   goto err;
}

FILE *fpLedColor=fopen(led_color,"w");
fseek(fpLedColor,0,SEEK_SET);
fprintf(fpLedColor,"%s",sensor_flag?LED_GREEN_CODE:LED_YELLOW_CODE);
fflush(fpLedColor);
fclose(fpLedColor);

int main(int argc, char **argv) 
{
     if(2 == argc)
     {
         FILE *fpLedCtrl=fopen("/sys/bus/i2c/devices/0-000d/sys_led_ctrl","w");
         FILE *fpLedColor=fopen("/sys/bus/i2c/devices/0-000d/sys_led_color","w");
         FILE *fpLedLog=fopen("/var/log/sensorMon.log","w");    
         fprintf(fpLedCtrl,"0x1");
         fprintf(fpLedColor,"%s",argv[1]);
         fprintf(fpLedLog,"%s\r\n",argv[1]);    
         fclose(fpLedCtrl);
         fclose(fpLedLog);
         fclose(fpLedColor);
     }
}

int mysprintf(char *outBuffer, char *format, ...)
{
    va_list aptr;
    int ret;
    va_start(aptr, format);
    ret = vsprintf(outBuffer, format, aptr);
    va_end(aptr);
    return(ret);
}

if( realvalue >= 0 )
{
	CompareValueThreshold(realvalue,&node[i]);
	strcat(node[i].path,node[i].node);
	if(0==strcmp("P1V8_VDDO(SWITCH)",node[i].name) || 0==strcmp("P1V2(SWITCH)",node[i].name))
       RecordEventLog(LOG_ERR,"\n [%d] throw a %s\n",i,sta?"SENSOR_ABNORMAL":"SENSOR_NORMAL");
}

FILE *fright=fopen("/tmp/right","w");
for(i=0;i<arraysize;i++)
{
   fprintf(fright,"[%d] %s (%s)\r\n",i,node[i].path,node[i].desc);
}
fclose(fright);

5.多线程:pthread_create(),查看线程top -H,ps -xH | grep

5.1 子线程未执行:join

如下线程thread进程process区别:process不能共享内存。
在这里插入图片描述
如下线程主函数void* 。pthread_create的第四个参数是myfunc的参数。
在这里插入图片描述
在这里插入图片描述

5.2 线程传参区分线程:“th1”

在这里插入图片描述
在这里插入图片描述

5.3 两子线程数字相加:分别加到自己线程变量中

在这里插入图片描述
在这里插入图片描述
如下解决上面代码重复太多问题,将0-2500和2500-5000当参数传进来。
在这里插入图片描述
在这里插入图片描述

5.4 两个线程同时加到一个全局变量s中:5000数字小不会影响

在这里插入图片描述

全局变量S++要加锁:数字大出现race condition

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.5 假共享:两线程分别加到自己result数组中,0和1两个线程,两个result数组(一个数字累加)

在这里插入图片描述
在这里插入图片描述
如下定义s为局部变量 = 结构体取出result,比上面要快。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
time ./example6始终比example5快,将50000000多加一个0,快的更多。为什么 ? 因为假共享false sharing,如下是一个框即单核cpu不会false sharing。
在这里插入图片描述
如下多核+运算结果距离近:example5里result变量在线程主函数外,cpu线程计算要从RAM中拉取。example6里的s为局部变量放在两个线程主函数里即cpu缓存里做计算,cpu两个核里两个缓存不会互相影响。所以example6不会falsing sharing,速度快。
在这里插入图片描述
如下解决假共享:cpu的cache短,RAM里很长,第一个线程结果保存在0位置,第二个线程结果保存在100位置,cache只更新自己长度的一小段如下4段(空间换时间)。
在这里插入图片描述

#if 0
int a=200;
int b=100;
pthread_mutex_t lock; //互斥锁的宏
void ThreadA(void)
{
    printf("线程A.....\n");
    pthread_mutex_lock(&lock);
    a-=50; //a=a-50
    sleep(5);
    b+=50; //b=b+50
    printf("a:%d,b:%d\n",a,b);
    pthread_mutex_unlock(&lock);
}
void ThreadB(void)
{
    printf("线程B.....\n");
    sleep(1);
    pthread_mutex_lock(&lock);//加锁
    printf("%d\n",a+b);
    pthread_mutex_unlock(&lock);//解锁
}
int main(void)
{
    pthread_t tida,tidb;
    pthread_mutex_init(&lock,NULL);//建立一个互斥锁
    pthread_create(&tida,NULL,(void *)ThreadA,NULL); //创建一个线程,1.句柄,2.线程属性,3.线程函数,4.函数的参数
    pthread_create(&tidb,NULL,(void *)ThreadB,NULL);
    pthread_join(tida,NULL);//等待一个线程结束
    pthread_join(tidb,NULL);
    pthread_mutex_destroy(&lock);
    return 1;
}
// -server:~/bak$ gcc test.c -lpthread
// -server:~/bak$ ./a.out 
// 线程A.....
// 线程B.....
// a:150,b:150
// 300
#endif

6.动静态库:.a,指定.so,LD_

公用函数库的public.cpp是源代码,对任何人可见,实际开发出于保密并不希望提供公用函数库源代码。C/C++提供了一个保证代码安全性方法,public.cpp编译成库(静态库与动态库)。

// public.h
#ifndef PUBLIC_H
#define PUBLIC_H 1
#include <stdio.h>
void func();   // 自定义函数的声明
#endif
// public.cpp
#include "public.h"
void func()   // 自定义函数的实现
{
  printf("我心匪石,不可转也。我心匪席,不可卷也。威仪棣棣,不可选也。\n");
}
// book265.cpp
#include "public.h"  // 把public.h头文件包含进来
int main()
{
  func();
}
g++ -o book265 book265.cpp public.cpp
./book265
我心匪石,不可转也。我心匪席,不可卷也。威仪棣棣,不可选也。

6.1 静态库:链接库的文件名是libpublic.a,链接库名是public,缺点使用的静态库发生更新改变,程序必须重新编译

gcc -c -o libpublic.a public.cpp

使用静态库的方法一,直接把调用者源代码和静态库文件名一起编译:

g++ -o book265 book265.cpp libpublic.a

使用静态库的方法二,用L参数指定静态库文件的目录,-l参数指定静态库名:如果要指定多个静态库文件的目录,用法是“-L/目录1 -L目录2 -L目录3”;如果要指定多个静态库,用法是“-l库名1 -l库名2 -l库名3”

g++ -o book265 book265.cpp -L/home/w/demo -lpublic
./book265
我心匪石,不可转也。我心匪席,不可卷也。威仪棣棣,不可选也。

6.2 动态库:动态库发生改变,程序不需要重新编译,动态库升级方便

g++ -fPIC -shared -o libpublic.so public.cpp

使用动态库的方法与使用静态库的方法相同。如果在动态库文件和静态库文件同时存在,优先使用动态库编译:

g++ -o book265 book265.cpp -L/home/w/demo -lpublic

执行程序./book265时,出现以下提示:/book265: error while loading shared libraries: libpublic.so: cannot open shared object file: No such file or directory,因为采用了动态链接库的可执行程序在运行时需要指定动态库文件的目录,Linux系统中采用LD_LIBRARY_PATH环境变量指定动态库文件的目录。采用以下命令设置LD_LIBRARY_PATH环境变量。

export LD_LIBRARY_PATH=/home/w/demo:.

如果要指定多个动态库文件的目录,用法是“export LD_LIBRARY_PATH=目录1:目录2:目录3:.”,目录之间用半角的冒号分隔,最后的圆点指当前目录。接下来修改动态库中func函数的代码:

// printf("我心匪石,不可转也。我心匪席,不可卷也。威仪棣棣,不可选也。\n");
printf("生活美好如鲜花,不懂享受是傻瓜;\n");

如下重新编译动态库,无需重新编译book265,直接执行程序。

g++ -fPIC -shared -o libpublic.so public.cpp
./book265
生活美好如鲜花,不懂享受是傻瓜;

6.3 libc(标准):gnu libc(glibc)(实现)

编译【预处理(语法检查),编译(.c->.s汇编文件),汇编(.s->.o二进制文件),链接(多个.o合并成1个执行文件)】的最后阶段将依赖引入过程叫链接:so文件通过mmap加载进内存,动态链接的a.out文件小且内存占用小,此外动态链接在so库更新后不需重新编译,一般首选。很多进程用到C语言libc.so里stdio.h里打印函数,如果通过静态链接,这样占用的内存多。
在这里插入图片描述
在这里插入图片描述
static指定静态链接。gcc是gnu的编译工具集合,gcc不光编译c语言且支持很多平台
在这里插入图片描述
如下系统没有glibc库。
在这里插入图片描述

6.4 编译时为什么要加上 –lm ?:man exp:Link with -lm

// 代码一
#include <stdio.h>
#include <math.h> //exp
int main(int argc, char const *argv[]){
   printf("The exponential value of %lf is %lf\n", 0, exp(0));
   printf("The exponential value of %lf is %lf\n", 0+1, exp(0+1)); //e的1次幂
   printf("The exponential value of %lf is %lf\n", 0+2, exp(0+2));
   return(0);
}

在这里插入图片描述

// 代码二
#include <stdio.h>
#include <math.h>
int main(int argc, char const *argv[]){
   double x = 0;
   printf("The exponential value of %lf is %lf\n", x, exp(x));
   printf("The exponential value of %lf is %lf\n", x+1, exp(x+1));
   printf("The exponential value of %lf is %lf\n", x+2, exp(x+2));
   return(0);
}

在这里插入图片描述
在这里插入图片描述
代码一调用exp传入的参数是常量为0 。代码二调用exp传入的参数是变量 x,代码一会不会在运行之前就计算好了呢?如下代码一没有看到调用exp的身影,当传入参数为常量时就已计算好了值,最后不需调用exp函数。代码二通过如下main.s汇编代码可见多次调用call函数。
在这里插入图片描述
math.h中声明的库函数还有一点特殊之处,gcc命令行必须加-lm选项,因为数学函数位于libm.so库文件中(这些库文件通常位于/lib目录下),-lm选项告诉编译器,程序中用到的数学函数要到这个库文件里找。

gcc a.c -o a.outarm-linux-gcc a.c -o b.out,如果执行out文件出现No such file or directory,则将如下两个so文件互相ln -s建软链接。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/736424.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

常见的七大排序

目录 前言 冒泡排序 选择排序 插入排序 堆排序 希尔排序 快排 归并排序 前言 本文介绍七种常见的排序方式&#xff1a;冒泡排序&#xff0c;选择排序&#xff0c;插入排序&#xff0c;堆排序&#xff0c;希尔排序&#xff0c;快排&#xff0c;归并排序 冒泡排序 将每2…

Rsync未授权访问-vulfocus

1.原理 Rsync是linux上文件传输的协议&#xff0c;如果有返回直接可以看到&#xff0c;部分主机使用协议的时候不会加密码&#xff0c;就容易造成未授权访问漏洞 2.复现 打开vulfocus.io,搜索rsync关键字&#xff0c;打开环境 在自己的主机上去连接远程服务器&#xff1a; r…

linux高级编程(1)

linux操作系统编程: 实现一个 用户程序 (1).库函数 --来实现 (2).系统调用 也就是说&#xff0c;程序要进行系统调用的话&#xff0c;有直接和间接&#xff08;通过库函数&#xff09;两种方式 linux里面对文件的处理: 思想: 一切皆文件 everything is file&…

轻松上手MYSQL:MYSQL事务隔离级别的奇幻之旅

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》《MYSQL》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 ✨欢迎加入探索MYSQL索引数据结构之旅✨ &#x1f44b; 大家好&#xff01;文本学习…

国产AI算力训练大模型技术实践

ChatGPT引领AI大模型热潮&#xff0c;国内外模型如雨后春笋&#xff0c;掀起新一轮科技浪潮。然而&#xff0c;国内大模型研发推广亦面临不小挑战。面对机遇与挑战&#xff0c;我们需保持清醒&#xff0c;持续推进技术创新与应用落地。 为应对挑战&#xff0c;我们需从战略高度…

【Linux详解】冯诺依曼架构 | 操作系统设计 | 斯坦福经典项目Pintos

目录 一. 冯诺依曼体系结构 (Von Neumann Architecture) 注意事项 存储器的意义&#xff1a;缓冲 数据流动示例 二. 操作系统 (Operating System) 操作系统的概念 操作系统的定位与目的 操作系统的管理 系统调用和库函数 操作系统的管理&#xff1a; sum 三. 系统调…

matplotlib之常见图像种类

Matplotlib 是一个用于绘制图表和数据可视化的 Python 库。它支持多种不同类型的图形&#xff0c;以满足各种数据可视化需求。以下是一些 Matplotlib 支持的主要图形种类&#xff1a; 折线图&#xff08;Line Plot&#xff09;&#xff1a; 用于显示数据随时间或其他连续变量的…

【web2】jquary,bootstrap,vue

文章目录 1.jquary&#xff1a;选择器1.1 jquery框架引入&#xff1a;$("mydiv") 当成id选择器1.2 jquery版本/对象&#xff1a;$(js对象) -> jquery对象1.3 jquery的页面加载事件&#xff1a;$ 想象成 window.onload 1.4 jquery的基本选择器&#xff1a;$()里内容…

大模型参数高效微调学习笔记

大模型参数高效微调学习笔记 github地址 billbill链接 1.分类 图中有五个大类&#xff1a; selective&#xff08;选择性微调&#xff09;&#xff1a;BitFit&#xff0c;Attention Tuningsoft prompts&#xff08;提示微调&#xff09;&#xff1a;Prompt-tuning&#xff0c…

Android 自定义软键盘实现 数字九宫格

最近项目在对接美团外卖功能 实现外面小哥凭取货码取货 对接完功能后 用户反馈 弹出的软键盘 很难输入 数字太小了 大概是下面这种显示方式 需求 组长说 要不搞一个自定义软键盘吧 数字搞大点 方便外卖员输入数字 我设置了输入EditText的输入格式为Number 还是不行 那就开…

文件夹或文件已在另一程序中打开,找句柄发现是explorer.exe如何解决

1.找到句柄&#xff1a;ctrl alt del打开任务资源管理器 2.注意是选择CPU -> 关联的句柄&#xff0c;而不是概述 如果发现只有explorer.exe&#xff0c;那肯定是不对的&#xff0c;我们先shfit一个一个删除&#xff0c;发现哪个删不掉&#xff0c;再在这里找句柄&#xff0c…

使用MyBatis Generator自动代码生成器简化Java持久层开发

在Web开发中&#xff0c;数据访问层&#xff08;DAO层&#xff09;的编码工作往往重复且繁琐&#xff0c;尤其是在处理数据库表与Java对象之间的映射时。MyBatis Generator是一款强大的代码生成工具&#xff0c;它能自动生成DAO接口、Mapper XML文件和实体类&#xff0c;极大地…

pytorch国内镜像源安装及测试

一、安装命令&#xff1a; pip install torch torchvision torchaudio -i https://pypi.tuna.tsinghua.edu.cn/simple 二、测试&#xff1a; import torch x torch.rand(5, 3) print(x)

微信小程序入门2

微信开发者工具的安装方法 1.打开微信开发者工具下载页面 在微信小程序管理后台的左侧边栏中选择“开发工具”&#xff0c;然后选择“开发者工具”&#xff0c;即可找到微信开发者工具的下载页面。 2.打开微信开发者工具的下载链接页面 单击“下载” 按钮下载&#xff0c;即…

【软件测试】认识测试

文章目录 1.什么是测试2.软件测试和开发的区别3.优秀的测试人员需要具备的素质 1.什么是测试 软件测试就是验证软件产品特性是否满足用户的需求 产品特性&#xff1a; 功能性能界面易用性 2.软件测试和开发的区别 工作内容 开发以编码为主&#xff0c;而测试以测试为主&…

高考填报志愿不容易,压线考生怎么救?

每年的高考季 就是高考生们水深火热的一大月份&#xff0c;很多考生都会纠结要报考哪些学校&#xff0c;哪些专业好&#xff0c;并非每个学生从小就有明确的目标&#xff0c;很多人到6月份才深思这个问题&#xff0c;此时难免手慌脚乱&#xff0c;更别说一些考生的分数处于一本…

ping命令返回结果实例分析

测试在各相关情况下ping命令回复信息。 网络环境搭建如下图所示&#xff1a; 【1】R1、R2、PC1和PC2没有配置&#xff0c;测试ping命令回复 在路由器没有配置端口IP地址和路由&#xff0c;PC没有配置IP地址、子网掩码和网关的情况下&#xff0c;PC2 ping 192.168.1.1。 在PC没…

代码随想录-Day37

56. 合并区间 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 示例 1&#xff1a; 输入&#xff1a;in…

Program-of-Thoughts(PoT):结合Python工具和CoT提升大语言模型数学推理能力

Program of Thoughts Prompting:Disentangling Computation from Reasoning for Numerical Reasoning Tasks github&#xff1a;https://github.com/wenhuchen/Program-of-Thoughts 一、动机 数学运算和金融方面都涉及算术推理。先前方法采用监督训练的形式&#xff0c;但这…

Qt: QPushButton 按钮实现 上图标下文字

效果如下&#xff1a; 实现有如下几种方式&#xff1a; 1. 使用 QPushButton 设置 setStyleSheet 例&#xff1a; ui->recorder->setStyleSheet("QPushButton{"\"border: 1px solid #00d2ff; "\"min-height: 60px; "\"col…