【C1】数据类型,常量变量,输入输出,运算符,if/switch/循环,/数组,指针,/结构体,文件操作,/编译预处理,gdb,makefile,线程

news2024/9/28 19:20:17

文章目录

  • 1.数据类型:单双引号,char(1B),int/float(32位系统,大小一样4B,但存储方式不同),double(8B),long double(12B)
  • 2.常量和变量:memset,const
  • 3.输出和输入:gets获取一行数据,并作为字符串处理
  • 4.运算符:前先加1,sizeof
  • 5.if/switch/循环:上下,else if
  • 6.数组:存数据类型相同的数据,数组下标越界导致core dump段错误
  • 7.指针:指针数组:这个数组的所有元素都是指针类型。数组指针:这个指针存放着一个数组的首地址
    • 7.1 p是地址,*p是指向的内容:01指0x01。&a:拿a的地址赋给。地址(p),值(*p),变量名
    • 7.2 函数调用:复制a值,指向a
    • 7.3 函数返回:复制指针
    • 7.4 数组和指针转换:指针转不了数组,数组可转为指针
    • 7.5 const型指针:不可修改
  • 8.main函数的参数:main函数的参数是从命令提示符下执行程序时传入
  • 9.static/extern/堆内存:realloc扩建
  • 10.动静态库:.a,指定.so,LD_
    • 10.1 静态库:链接库的文件名是libpublic.a,链接库名是public,缺点使用的静态库发生更新改变,程序必须重新编译
    • 10.2 动态库:动态库发生改变,程序不需要重新编译,动态库升级比较方便
    • 10.3 libc:gnu libc(glibc),musl libc(alpine)
  • 11.结构体:struct,memcpy,memset
  • 12.文件操作:FILE结构体,fprint/fgets,fwrite/fread,ftell/rewind/fseek,fflush
    • 12.1 二进制文件的读写:没有行概念
    • 12.2 文件定位:linux下文本文件模式和二进制文件模式没有区别。fgets和fprintf以行方式读写文本文件,但不能读写二进制文件。用fread和fwrite可以读写文本文件和二进制文件
  • 13.编译预处理:C源程序 - 编译预处理【对预处理指令即以#开头指令和特殊符号进行处理,删除程序中注释和多余空白行】- 编译
  • 14.gdb调试:set args,b/r,n/s
  • 15.makefile文件:依赖文件功能
  • 16.多线程:pthread_create(),查看线程top -H,ps -xH | grep
    • 16.1 子线程未执行:join
    • 16.2 线程传参区分线程:"th1"
    • 16.3 两子线程数字相加:分别加到自己线程变量中
    • 16.4 两个线程同时加到一个全局变量s中:5000数字小不会影响
    • 16.5 全局变量S++要加锁:数字大出现race condition
    • 16.6 假共享:两线程分别加到自己result数组中


1.数据类型:单双引号,char(1B),int/float(32位系统,大小一样4B,但存储方式不同),double(8B),long double(12B)

编译器(compiler)与解释器(interpreter)编译型语言像C/C++/golang/rust等都会用到编译器,并且都是AOT(ahead of time)预先编译,编译将源码编译成机器码生成二进制文件,可直接运行该文件,因为是机器码,所以运行效率很高,缺点是不够灵活,改代码的话要重新编译,此外平台依赖严重,linux平台编译出来的二进制文件无法在windows运行,跨平台还需借助交叉编译。

解释型语言像python/js/php等使用解释器逐行解释,不需要生成二进制文件,灵活如线上php系统,改了代码功能直接生效,但解释型语言运行效率低。还有一些语言本身是用解释器在运行,但在运行时使用了编译器进行JIT(just in time)实时编译如java,是半解释半编译型语言,JIT将运行到的代码块在运行时编译成机器码,既可保证跨平台性,又能使热代码高效运行。

app可直接调os接口,也可以调c库,c库再去调os接口。一个程序只有一个main函数。#include:包含其他文件预处理指令。main(){}:这个是最简单的C程序。<stdio.h>头文件包含printf。yum -y install gcc,yum install gcc-c++。

gcc main.c int_sum.c float_sum.c -o(output) main -Wall(显示所有警告) -I../include(-I后面没空格,不加-I就在main.c中指定头文件的相对路径)。gcc main.c -o main -Wall -I../include -L../lib -lsum(有库文件/lib/ibsum.so就只要main.c就行)。


1.基本类型字符型(char):描述单个字符,用单引号包含起来,如’a’,‘A’,‘1’,'$'等,键盘能输入的英文和半角符号都是字符。关键字char是character(字符)的简写。中文里的汉字和标点符号是两个字节,不能算一个字符'¥','好','。'这些都是非法的。

整型(int):整数在计算机上是准确表示的,如123,500,0,-10等。关键字int是integer(整数)的简写。

浮点型(double):描述实数有小数位,如10.0,123.55,3459.98,-50.3,实数在计算机上一般是近似表示的。只有三种:float,double,long double。

2.字符串:描述多个字符,用双引号包含起来,可以是英文,数字,中文,标点符号,半角全角,空字符串都可以。

3.构造类型:由已知的基本类型通过一定的构造方法构造出来的类型,包括数组和结构体。

4.指针类型:指针可以指向内存地址,主要用于函数的参数传递。
在这里插入图片描述
在这里插入图片描述
浮点数不是2的0次方+2的1次方…,用下面公式。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
不用管如上公式,记住:浮点数最小可识别精度和浮点数即0.1本身大小相关,而不是和float相关。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
1.获取字符串的长度(strlen)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
5.字符串拼接(strncat)
在这里插入图片描述
6.字符串比较(strcmp,strncmp)
在这里插入图片描述

#include <stdio.h>
#include <string.h>
int main()
{
	  char *names = "PSU";
	  if(strcmp(names,"PSU")==0)
	  {
		  puts("aaaa");
	  }
}

int main() {      // error: expected ‘)’ before string constant    少了int main()
    char name_str[30];
    char name_str1[30];
    strcpy(name_str,"/sys/bus/i2c/devices/");
    strcpy(name_str1,"17-0064");
    strcat(name_str,name_str1);
    printf("is : %s\n", name_str);   //    /sys/bus/i2c/devices/17-0064
}

putchar('w');putchar('w');
printf("[%s]__%4d__[%s]  %s \n", __FILE__, __LINE__, __FUNCTION__, n->name); 

在这里插入图片描述
8.字符串查找(strstr)
在这里插入图片描述
在这里插入图片描述

#include <stdio.h>
#include <string.h>
int main ()
{
   char str1[15];
   char str2[15];
   int ret;
   strcpy(str1, "abcdef");
   strcpy(str2, "ABCDEF");
   ret = strcmp(str1, str2);
   if(ret < 0)
   {
      printf("str1 小于 str2");
   }
   else if(ret > 0) 
   {
      printf("str1 大于 str2");
   }
   else 
   {
      printf("str1 等于 str2");
   }
   return(0);    //str1 大于 str2
}
#include<stdio.h>
#include<string.h>
#include <pthread.h>
#include <unistd.h>

#if 0
int main() { 
    // int a;
    // char pNum[]="0x7f";
    // a=strtoul(pNum,0,16);
    // printf("%d\n",a);  //127
    // return 0;
   char str[30] = "2030300 This is test";
   char *ptr;
   long ret;
   ret = strtoul(str, &ptr, 10);
   printf("数字(无符号长整数)是 %lu\n", ret);  // 2030300
   printf("字符串部分是 |%s|\n", ptr);  // This is test
   return 0;
}
#endif

#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);
    pthread_create(&tidb,NULL,(void *)ThreadB,NULL);//创建一个线程,1.句柄,2.线程属性,3.线程函数,4.函数的参数
    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

2.常量和变量:memset,const

1.静态数据:永久性数据,一般存储在硬盘中,只要硬盘没坏数据都是存在的,电脑关机重启后依然存在。写程序目的就是告诉计算机如何输入,处理,传输,存储和展示数据。

2.动态数据(临时数据):程序运行过程中,动态产生的临时数据,一般存储在内存中,软件或者电脑关闭则这些临时数据会被清除。为什么不把动态数据存放到硬盘呢?因为访问内存的速度比访问硬盘快很多倍。

3.数据大小:(1)数据都是由二进制的0和1组成。
(2)数据都会占用空间,静态数据占硬盘空间,动态数据占内存空间。
(3)数据占用空间的最小单位是比特(bit)。一个1或者0就是一个比特,即1bit。
(4)计算机中,以字节为单位存储数据。1字节=8比特(1Byte=8bit)。
(5)1B=8bit,1KB=1024B,1MB=1024KB,1GB=1024MB…


常量:即常数,表示固定的数据:(1)字符常量 如’6’,‘a’,‘F’(不能是中文,例如:‘男’)
(2)整数常量 如6,27,-299
(3)浮点数常量 如5.43,-2.3,5.67,6.0
(4)字符串常量 如’‘625’‘,’‘女’‘,’‘nanbc’‘,’‘西施’’

变量:如果数据的值不确定的,或者经常需要改变,或者临时取值的,则用变量取值。常量指具体数据,变量指存数据的容器,水与水桶一样。变量使用前必须先进行声明(或定义),在内存中分配一块存储空间给变量,用于存放数据。

1.整数型,字符型,浮点型变量的定义
int ii; //定义整数型变量,用于存放整数,关键字int,是单词integer(整数)的缩写。
char cc; //定义字符串变量用关键字char,是单词charcter(字符)的缩写,存放字符。
double money; //定义浮点型变量,用于存放浮点数,关键字double。

2.字符串变量的定义
C语言中没有字符串这个数据类型,而是用字符数组来表达字符串。char name[21];定义一个可以存放20字符的字符串即20个英文或10个中文。字符串不是C语言的基本数据类型,不能用=赋值,不能用><比较大小,不能用+拼接,不能用==和!=判断两个字符串是否相等,要用函数。

3.变量的命名
变量名属于标识符,需要符合标识符的命名规范,如下:
1)变量名的第一个字符必须是字母或下划线,不能是数字和其它字符。
2)变量名中的字母是区分大小写的。比如 a 和 A 是不同的变量名,num 和 Num 也是不同的变量名。
3)变量名绝对不可以是C语言关键字。

关键字也称为保留字,共32个,也就是说这些单词在C语言中有特别的含义,程序员不能把它用于变量或函数的命名。auto :声明自动变量。break:跳出当前循环。case:开关语句分支。char :声明字符型变量或函数返回值类型。const :声明只读变量。continue:结束当前循环,开始下一轮循环。default:开关语句中的“默认”分支。do :循环语句的循环体。double :声明双精度浮点型变量或函数返回值类型。else :条件语句否定分支(与 if 连用)。enum :声明枚举类型。extern:声明变量或函数是在其它文件或本文件的其他位置定义。float:声明浮点型变量或函数返回值类型。for:一种循环语句。goto:无条件跳转语句。if:条件语句。int: 声明整型变量或函数。long :声明长整型变量或函数返回值类型。register:声明寄存器变量。return :子程序返回语句(可以带参数,也可不带参数)。short :声明短整型变量或函数。signed:声明有符号类型变量或函数。sizeof:计算数据类型或变量长度(即所占字节数)。static:声明静态变量。struct:声明结构体类型。switch:用于开关语句。typedef:用以给数据类型取别名。unsigned:声明无符号类型变量或函数。union:声明共用体类型。void:声明函数无返回值或无参数,声明无类型指针。volatile:说明变量在程序执行中可被隐含地改变。while:循环语句的循环条件。


变量的初始化:在main函数内部定义的变量称为局部变量,局部变量被定义后,系统不会对其初始化,必须自己写程序初始化。

多变量定义:int ii,jj;,const是constant缩写,意思是“恒定不变的”!它是定义只读变量的关键字,或者说 const 是定义常变量的关键字。用 const定义常变量方法很简单,就在通常定义变量时前面加 const 即可,如:const double pi = 3.1415926;用 const 定义的变量的值是不允许改变的,即不允许给它重新赋值,即使赋相同的值也不可以。所以说它定义的是只读变量。这也就意味着必须在定义的时候就给它赋初值,如果程序中试图改变它的值,编译的时候就会报错。

1.整数型,字符型,浮点型变量初始化:就是赋0值,也可在定义时初始化。
int ii=0; // 定义整数型变量并初始化
char cc=0; // 定义字符型变量并初始化
double money=0; // 定义浮点型变量并初始化
也可以先定义,然后再初始化
int ii; // 定义整数型变量
char cc; // 定义字符型变量
double money; // 定义浮点型变量
ii=0; // 初始化ii为0
cc=0; // 初始化cc为0
money=0; // 初始化money为0

2.字符串变量的初始化:对字符串变量来说,初始化就是把内容清空。
char name[21]; // 定义一个可以存放20字符的字符串
memset(name,0,sizeof(name)); // 清空字符串name中的内容

3.未初始化的变量示例:未初始化的变量会导致在内存中可能有垃圾值。

3.输出和输入:gets获取一行数据,并作为字符串处理

数据输出:在C语言中,有三个函数可以把数据输出到屏幕。
putchar 用于输出单个字符。
puts 输出字符串。
printf函数是格式化输出函数, 用于向屏幕输出数据,printf函数的调用方法是: printf(格式化字符串,参数列表);

1.输出描述性的文字
输出的文字用双引号包含起来,在文字最后加上\n表示换一行,多个\n可以换多行。
printf(“我心非席,不可卷也,我心非石,不可转也。\n”);
以上代码将在屏幕上输出文字:我心非席,不可卷也,我心非石,不可转也。
输出文字之后,再输出一个换行。

2.输出整数
输出的整数常量或整数变量用%d表示,在参数中列出待输出的整数常量或整数变量。
printf(“我年龄是%d岁。\n”,18);
int age=18;
printf(“我年龄是%d岁。\n”,age);

3.输出字符
输出的字符常量或字符变量用%c表示,在参数中列出待输出的字符常量或字符变量。
printf(“我姓别是:%c。\n”,‘x’); // 姓别:x-男;y-女
char xb=‘x’;
printf(“我姓别是:%c。\n”,xb);

4.输出浮点数
输出的浮点型常量或浮点型变量用%lf表示,在参数中列出待输出的浮点型常量或浮点型变量。
printf(“我体重是%lf公斤。\n”,62.5);
double weight=62.5;
printf(“我体重是%lf公斤。\n”, weight);

5.输出字符串
输出的字符串常量或字符串型变量用%s表示,在参数中列出待输出的字符串常量或字符串变量。
printf(“我的姓名是%s。\n”,“豫让”);
char name[21];
memset(name,0,sizeof(name));
strcpy(name, “豫让”);
printf(“我的姓名是%s。\n”,name);
如下short是普通int的一半,d是十进制。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如下32指32字节。
在这里插入图片描述


scanf输入:scanf函数是格式化输入函数, 用于向接受键盘输入的数据,用户输入数据完成后,需按回车键(Enter)结束输入。scanf函数的调用方法是: scanf(格式化字符串,参数列表)。注意,不要在scanf的格式化字符串的最后加\n。

1.输入整数
输入整数的格式用%d表示,在参数中列出整数型变量名,用于保存输入的数据。
在输入数据之前,一般要先输出一句话提示用户。下同。
int age=0;
printf(“请输入你的年龄:”); // 提示文字不要换行,让用户在后面输入,下同。
scanf("%d",&age); // 在变量名前要加符号&,键盘输入值放入age变量中去。

2.输入字符
输入字符的格式用%c表示,在参数中列出字符型变量名,用于保存输入的数据。
char xb=0;
printf(“请输入你姓别:”);
scanf(“%c”,&xb); // 在变量名前要加符号&

3.输入浮点数
输入浮点数的格式用%lf表示,在参数中列出浮点型变量名,用于保存输入的数据。
double weight=62.5;
printf(“请输入你体重:”);
scanf(“%lf”,&weight); // 在变量名前要加符号&。

4.输入字符串
输入字符串的格式用%s表示,在参数中列出字符串变量名,用于保存输入的数据。
char name[21];
memset(name,0,sizeof(name));
printf(“请输入你姓名:”);
scanf(“%s”,name); // 注意了,变量名前不加符号&,也不要问原因,以后再介绍。

5.输入多个内容
调用一次scanf函数可以输入多个数据,注意了,与单个数据的输入不同,多个数据的输入要等全部的数据输入完成后才按回车键。
int age=0;
char xb=0;
double weight=0;
char name[21];
memset(name,0,sizeof(name));
printf("请输入你的姓名、姓别(x-男;y-女),年龄和体重,中间用空格分隔:");
scanf("%s %c %d %lf",name,&xb,&age,&weight); // 只有name变量前没加&,其它的都加了。因为name数组名就是首地址名。
scanf函数第一个参数(格式化字符串)的格式与后面的参数列表(变量的列表)要一一对应,不能多,不能少,顺序也不能错,否则会产生意外的结果。查找头文件:man memset显示如下。man 3 printf。1,2,3去试。
在这里插入图片描述

4.运算符:前先加1,sizeof

|| 左边的命令返回假(命令返回值 $? == 1),|| 右边的命令才会被执行。这和 c 语言中的逻辑或语法功能相同,即实现短路逻辑或操作: 只要有一个命令返回真(命令返回值 $? == 0),后面的命令就不会被执行。
在这里插入图片描述
运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C 语言内置了丰富的运算符,并提供了以下类型的运算符:1)算术运算符 2)赋值运算符 3)关系运算符 4)逻辑运算符 5)位运算符。如下浮点数没有取余数的说法。
在这里插入图片描述
如下ii++,就是ii=ii+1。
在这里插入图片描述
=是赋值,==才是判断两个数是否相等,C语言没有(之间,中间,之内或在某范围内)的关系运算符,例如年龄在25-30岁之间,只能表达为:年龄大于等于25岁并且年龄小于等于30岁。括号中表达式会优先执行,sizeof(指针)大小永远是8字节,如下是sizeof运算符,sizeof是C语言的关键字,发挥着运算符的作用。它主要用来计算某一个变量在当前系统的内存中所需占用的字节数。变量的类型包括char、int、double、数组、结构体和其它自定义的数据类型。sizeof不是函数。产生这样的疑问主要是因为sizeof的书写确实有点像函数,比如:i = sizeof(int);这样的写法,就很容易让人误以为sizeof是一个函数。sizeof有两种写法:
1.用于数据类型
sizeof使用形式:sizeof(type)
数据类型必须用括号括住。如sizeof(int)。 
printf(“sizeof(int)=%d\n”,sizeof(int)); // 输出结果:sizeof(int)=4

2.用于变量
sizeof使用形式:sizeof(var_name)或sizeof var_name 
变量名可以不用括号括住。如sizeof(var_name),sizeof var_name等都是正确形式。
printf(“sizeof dd =%d\n”,sizeof dd); // 输出结果:sizeof dd =8
printf(“sizeof(dd)=%d\n”,sizeof(dd)); // 输出结果:sizeof(dd)=8

5.if/switch/循环:上下,else if

不要在if(判断条件)后面加分号。有;号就为空语句,下面都执行。c=(a>b)?a:b等同于两行:if(a>b) c=a;else c=b; if(0)即0假。if(a=b)是赋值,不是判断。
在这里插入图片描述
当没有default时,如果所有case都匹配失败,那么就什么都不执行。char day=0,scanf(‘%c’',&day),case ‘0’。
在这里插入图片描述
case后面必须是一个整数,或者是结果为整数的表达式,但不能包含任何变量。
在这里插入图片描述

#include<stdio.h>
int main()
{
  int a=1, b=2, re;
  char c;
  scanf("%c", &c);
  switch(c)
  {
    case '+':
      re = a + b;
      break;
    case '$':
      re = a - b;
      re++;
      break;
    case '#':
    {
      int other = 3;   // switch语句里定义了新的变量 ,加{}
      re = a + b + other;
      break;
    }
    default:
      printf("Illegal input!\n");
      break;
  }
  printf("%d\n", re);
}

$gcc -o main *.c -lm
$main
Illegal input!
0

在这里插入图片描述
在这里插入图片描述
如下do-while先执行一次循环。
在这里插入图片描述
continue跳到循环首部,break跳出循环。
在这里插入图片描述
在这里插入图片描述

6.数组:存数据类型相同的数据,数组下标越界导致core dump段错误

定义数组时,数组的长度必须是整数(可以是常量,也可以是变量)。
在这里插入图片描述
在这里插入图片描述
内存是线性的,b里放了2个a。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如下如果用msg[1],虽然越界了,但是系统给结构体分配很多字节,所以不报错。可用msg[0]到msg[n-1]。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
函数声明和函数定义是两个概念,但变量声明和变量定义是一个概念。
在这里插入图片描述

7.指针:指针数组:这个数组的所有元素都是指针类型。数组指针:这个指针存放着一个数组的首地址

7.1 p是地址,*p是指向的内容:01指0x01。&a:拿a的地址赋给。地址(p),值(*p),变量名

在这里插入图片描述

char str[10];
char strbuf[10];

char *a(void)
{
    FILE *fp=0;
    if ((fp=fopen("ab","rt")) ==0)
    {
        printf("1111111");
    }
    if (fgets(strbuf, 10, fp) == NULL) 
    {
        printf("2222222");
        fclose(fp);
    }
    strcpy(str,"B");
    strcat(str,strbuf);
    fclose(fp);
    return str;
}

int main()
{
    char *b=a();
    printf("%s\n", b);
}

如下fclose要早于printf。
在这里插入图片描述
int *p 未赋值的指针称为野指针,危险的,a=100,p=&a指向合法区域。或如下空指针的两种写法也可防止野指针。
在这里插入图片描述

7.2 函数调用:复制a值,指向a

在这里插入图片描述
1.如下main中走到increament跳到这函数中,拷贝一份给increament中的a。main中的a和increament中的a各自独立一块内存,只是名字一样。
在这里插入图片描述
2.一定要通过increasement函数对a有修改怎么办?如下必须用到指针,increament运行结束后指针销毁。
在这里插入图片描述
在这里插入图片描述

7.3 函数返回:复制指针

如下当调用move_p时,p要往右移一位。move_p参数定义虽是指针,但调用时传入也是指针,复制一份。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7.4 数组和指针转换:指针转不了数组,数组可转为指针

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

7.5 const型指针:不可修改

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

8.main函数的参数:main函数的参数是从命令提示符下执行程序时传入

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
envp参数存放了当前程序运行环境的参数(env命令)。
在这里插入图片描述

9.static/extern/堆内存:realloc扩建

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

10.动静态库:.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
我心匪石,不可转也。我心匪席,不可卷也。威仪棣棣,不可选也。

10.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
我心匪石,不可转也。我心匪席,不可卷也。威仪棣棣,不可选也。

10.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");
printf("傻呀傻呀傻呀傻,比不上小鸟和乌鸦。\n");

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

g++ -fPIC -shared -o libpublic.so public.cpp
./book265
生活美好如鲜花,不懂享受是傻瓜;
傻呀傻呀傻呀傻,比不上小鸟和乌鸦。

10.3 libc:gnu libc(glibc),musl libc(alpine)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
gcc是gnu的编译工具集合,gcc不光编译c语言且支持很多平台。llvm提出中间文件格式IR,所有语言处理成IR格式文件(像java中class文件一样),将IR格式文件处理成可执行的二进制文件。

11.结构体:struct,memcpy,memset

C语言用结构体(struct)数据类型存放一组不同数据类型的数据。
在这里插入图片描述
在这里插入图片描述
结构体复制:基本类型用=,字符串用strcpy,结构体memcpy。
在这里插入图片描述
结构体作为函数的参数:结构体成员较多,函数参数的初始化和赋值的开销很大,最好的办法就是传递结构体变量的地址。
在这里插入图片描述

12.文件操作:FILE结构体,fprint/fgets,fwrite/fread,ftell/rewind/fseek,fflush

在这里插入图片描述
参数mode也是字符串,表示打开文件的模式,打开模式可以是下列值中一个。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
vi /tmp/test1.txt,可见有5行记录,不管执行多少次都是5行记录,因为文件打开方式是w,每次打开文件时都会清空原文件中的记录。
在这里插入图片描述
在这里插入图片描述

12.1 二进制文件的读写:没有行概念

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

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

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

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

os中存在内存缓冲区,调用fprintf、fwrite等函数往文件写入数据时,数据并不会立马写入磁盘文件,而是先写入缓冲区,等缓冲区满了后再写入文件,还有程序调用了fclose或fflush库函数时会把缓冲区数据写入文件。
在这里插入图片描述

#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);

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

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

14.gdb调试:set args,b/r,n/s

多进/线程中无法用gdb调试,还是用printf,但不会把结果显示到界面,写入日志文件中。root 用户:yum -y install gdb,gdb -v。
在这里插入图片描述
在这里插入图片描述

15.makefile文件:依赖文件功能

make是一个命令工具,是一个解释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编译不链接。
在这里插入图片描述

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

16.1 子线程未执行:join

如下线程thread进程process区别:process不能共享内存。
在这里插入图片描述
如下线程主函数void* 。pthread_create的第四个参数是myfunc的参数。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如上没有打印出hello word,如下join等待进行改进。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

在这里插入图片描述
在这里插入图片描述
如下改进上面,往线程里传参区分线程。
在这里插入图片描述
在这里插入图片描述

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

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

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

不用每个线程的result。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

在这里插入图片描述
如下每一条线程加1000000,两条线程应该为2000000。解决就是加锁。
在这里插入图片描述
如下t是时间,r读,w写。
在这里插入图片描述
解决:pthread_mutex_t 结构体,如下一共做了10万次加锁和解锁(时间太长),一段代码前后都要加解锁才能原子性。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如下从时间上看两个for循环各自独立存在(相当于单线程),不如写两个for循环在同一个线程里,这样还省去了4次加锁解锁时间。这就是前面把大数组拆成两段,两个线程分别加自己内容,最后放入main中加起来,这样不会race condition,也不需要通过锁解决。
在这里插入图片描述

16.6 假共享:两线程分别加到自己result数组中

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

补充一些前端面试题

javascript有哪些库指路>js中的库uniapp和vue有什么区别什么是uniappuni-app&#xff08;uni&#xff0c;读you ni&#xff0c;是统一的意思&#xff09;是一个使用Vue.js开发所有前端应用的框架&#xff0c;开发者编写一套代码&#xff0c;可发布到iOS、Android、Web&#…

天工开物 #5 我的 Linux 开发机

首先说一下结论&#xff1a;最终我选择了基于 Arch Linux[1] 的 Garuda Linux[2] 发行版作为基础来搭建自己的 Linux 开发机。Neofetch 时刻发行版的选择在上周末的这次折腾里&#xff0c;我一共尝试了 Garuda Linux 发行版&#xff0c;原教旨的 Arch Linux 发行版&#xff0c;…

R语言读取Excel表格数据并绘制多系列柱状图、条形图

本文介绍基于R语言中的readxl包与ggplot2包&#xff0c;读取Excel表格文件数据&#xff0c;并绘制具有多个系列的柱状图、条形图的方法。 首先&#xff0c;我们配置一下所需用到的R语言readxl包与ggplot2包&#xff1b;其中&#xff0c;readxl包是用来读取Excel表格文件数据的&…

android---WebView实例

现在很多 App 里都内置了 Web 网页&#xff0c;比如电商平台淘宝、京东等。那么这种该如何实现呢&#xff1f;其实这是 Android 里一个叫 WebView 的组件实现的。下面将介绍 WebView 的实例。下面的实例是以组件化为基础搭建的。 新建项目 WebView&#xff0c;然后对整个项目做…

网络安全攻防中,Rock-ON自动化的多功能网络侦查工具,Burpsuite被动扫描流量转发

网络安全攻防中&#xff0c;Rock-ON自动化的多功能网络侦查工具&#xff0c;Burpsuite被动扫描流量转发。 #################### 免责声明&#xff1a;工具本身并无好坏&#xff0c;希望大家以遵守《网络安全法》相关法律为前提来使用该工具&#xff0c;支持研究学习&#xff…

linux003之远程操作

Xshell简介&#xff1a; Xshell 是一个强大的安全终端模拟软件&#xff0c;它支持SSH1, SSH2, 以及Microsoft Windows 平台的TELNET 协议。Xshell 通过互联网到远程主机&#xff0c;可以操控到linux服务器。  Xshell可以在Windows界面下用来访问远端不同系统下的服务器&#…

一些常见错误

500状态码: 代表服务器业务代码出错, 也就是执行controller里面的某个方法的过程中报错, 此时在IDEA的控制台中会显示具体的错误信息, 所以需要去看IDEA控制台的报错404状态码: 找不到资源找不到静态资源 检查请求地址是否拼写错误 检查静态资源的位置是否正确 如果以上都没有问…

Docker中安装Oracle-12c

前言 MySQL和Oracle是开发中常用到的两个关系型数据库管理系统&#xff0c;接上一期内容&#xff0c;这一期在Docker中完成oracle-12c的安装和配置。 安装oracle-12c 1、拉取oracle-12c镜像 启动Docker Desktop后在cmd窗口中执行docker search oracle命令&#xff0c;搜索O…

LeetCode-1138. 字母板上的路径【哈希表,字符串】

LeetCode-1138. 字母板上的路径【哈希表&#xff0c;字符串】题目描述&#xff1a;解题思路一&#xff1a;首先考虑坐标位置&#xff0c;字符是有序的从0开始&#xff0c;当前字符c的行为(c-a)/5,列为(c-a)%5。其次是考虑特殊情况z。若当前从‘z’开始则只能往上走;若是其他字符…

Spring MVC 之返回数据(静态页面、非静态页面、JSON对象、请求转发与请求重定向)

文章目录1. 默认情况下返回静态页面2. 返回一个非静态页面的数据2.1 ResponseBody 返回页面内容2.2 RestController ResponseBody Controller3. 实现登录功能&#xff0c;返回 JSON 对象3.1 前端使⽤ ajax&#xff0c;后端返回 json 给前端3.2 前端发送 JSON 的标准格式4. 请…

ChatGPT 怎么用最新详细教程一看就会

ChatGPT 以其强大的信息整合和对话能力惊艳了全球&#xff0c;在自然语言处理上面表现出了惊人的能力。这么强大的工具我们都想体验一下&#xff0c;那么 ChatGPT 怎么用呢&#xff1f;本文将给你逐步详细介绍。 使用 ChatGPT 主要有4步&#xff1a; 注册 ChatGPT 账号通过短…

Maven_第五章 核心概念

目录第五章 其他核心概念1、生命周期①作用②三个生命周期③特点2、插件和目标①插件②目标3、仓库第五章 其他核心概念 1、生命周期 ①作用 为了让构建过程自动化完成&#xff0c;Maven 设定了三个生命周期&#xff0c;生命周期中的每一个环节对应构建过程中的一个操作。 …

智慧办公管理系统

随着大数据、物联网和新一代通讯技术的发展&#xff0c;智慧地球、智慧城市、智慧办公正在逐步变成现实。 智慧办公管理系统主要分为会议室管理、考勤管理以及系统设置三部分功能。 架构图&#xff1a; 功能介绍 一、会议室管理 &#xff08;1&#xff09;、会议室信息 会议室…

如何将 Windows 11/10 许可证转移到另一台 PC

如果您最近购买了新的台式机或笔记本电脑&#xff0c;您可能希望在上面安装 Windows 11。对于某些用户来说&#xff0c;为新计算机购买新的 Windows 11 许可证可能会有点贵。 但是&#xff0c;如果您在旧计算机上安装了 Windows 11 并购买了一台新计算机来替换它&#xff0c;您…

JavaSE-线程池(1)- 线程池概念

JavaSE-线程池&#xff08;1&#xff09;- 线程池概念 前提 使用多线程可以并发处理任务&#xff0c;提高程序执行效率。但同时创建和销毁线程会消耗操作系统资源&#xff0c;虽然java 使用线程的方式有多种&#xff0c;但是在实际使用过程中并不建议使用 new Thread 的方式手…

【c++】数据类型

文章目录整型实型科学计数法sizeof关键字字符型字符串类型转义字符bool布尔类型c规定在创建一个变量或者常量时&#xff0c;必须要指定出相应的数据类型&#xff0c;否则无法给变量分配内存。 整型 作用&#xff1a;整型变量表示的是整数类型的数据。 实型 float f3.14; //默…

JavaCollection集合

5 Collection集合 5.1 Collection集合概述 是单列集合的顶层接口&#xff0c;它表示一组对象&#xff0c;这些对象也称Collection元素JDK不提供此接口的直接实现&#xff0c;它提供更具体的子接口&#xff08;Set 和 List&#xff09;实现 package ceshi;import java.util.A…

【C++1】函数重载,类和对象,引用,/string类,vector容器,类继承和多态,/socket,进程信号

文章目录1.函数重载&#xff1a;writetofile()&#xff0c;Ctrue和false&#xff0c;C0和非02.类和对象&#xff1a;vprintf构造函数&#xff1a;对成员变量初始化析构函数&#xff1a;一个类只有一个&#xff0c;不允许被重载3.引用&#xff1a;C中&取地址&#xff0c;C中…

C语言深度剖析之文件操作

&#x1f497; &#x1f497; 博客:小怡同学 &#x1f497; &#x1f497; 个人简介:编程小萌新 &#x1f497; &#x1f497; 如果博客对大家有用的话&#xff0c;请点赞关注加关注 &#x1f31e; 什么是文件 磁盘上的文件是文件。 但是在程序设计中&#xff0c;我们一般谈的文…

计算机组成原理(四)

1.理解存储器的分类方法&#xff1b;理解存储器的层次结构&#xff1b;熟悉存储器的几个技术指标&#xff08;主要是存储容量、存取时间、存取周期、存储器带宽等&#xff09;&#xff1b; 存储器分类方法&#xff1a;   按与CPU的连接和功能分类&#xff1a;     主存储…