C语言线程库的使用,这篇值得收藏!

点击蓝字

7854d5dafecac48116c650700f987461.png

关注我们

1. 线程概述

线程是轻量级的进程(LWP:light weight process),在 Linux 环境下线程的本质仍是进程。在计算机上运行的程序是一组指令及指令参数的组合,指令按照既定的逻辑控制计算机运行。操作系统会以进程为单位,分配系统资源,可以这样理解,进程是资源分配的最小单位,线程是操作系统调度执行的最小单位

先从概念上了解一下线程和进程之间的区别:

  1. 进程有自己独立的地址空间,多个线程共用同一个地址空间

  • 线程更加节省系统资源,效率不仅可以保持的,而且能够更高

  • 在一个地址空间中多个线程独享:每个线程都有属于自己的栈区,寄存器 (内核中管理的)

  • 在一个地址空间中多个线程共享:代码段,堆区,全局数据区,打开的文件 (文件描述符表) 都是线程共享的

  1. 线程是程序的最小执行单位,进程是操作系统中最小的资源分配单位

  • 每个进程对应一个虚拟地址空间,一个进程只能抢一个 CPU 时间片

  • 一个地址空间中可以划分出多个线程,在有效的资源基础上,能够抢更多的 CPU 时间片

5c5118979d99207ffc966546ac50b826.png
  1. CPU 的调度和切换:线程的上下文切换比进程要快的多

上下文切换:进程 / 线程分时复用 CPU 时间片,在切换之前会将上一个任务的状态进行保存,下次切换回这个任务的时候,加载这个状态继续运行,任务从保存到再次加载这个过程就是一次上下文切换。

  1. 线程更加廉价,启动速度更快,退出也快,对系统资源的冲击小。

在处理多任务程序的时候使用多线程比使用多进程要更有优势,但是线程并不是越多越好,如何控制线程的个数呢?

  1. 文件 IO 操作:文件 IO 对 CPU 是使用率不高,因此可以分时复用 CPU 时间片,线程的个数 = 2 * CPU 核心数 (效率最高)

  2. 处理复杂的算法 (主要是 CPU 进行运算,压力大),线程的个数 = CPU 的核心数 (效率最高)

2. 创建线程

2.1 线程函数

每一个线程都有一个唯一的线程 ID,ID 类型为 pthread_t,这个 ID 是一个无符号长整形数,如果想要得到当前线程的线程 ID,可以调用如下函数:

pthread_t pthread_self(void); // 返回当前线程的线程ID

在一个进程中调用线程创建函数,就可得到一个子线程,和进程不同,需要给每一个创建出的线程指定一个处理函数,否则这个线程无法工作。

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
// Compile and link with -pthread, 线程库的名字叫pthread, 全名: libpthread.so libptread.a

参数:

  • thread: 传出参数,是无符号长整形数,线程创建成功,会将线程 ID 写入到这个指针指向的内存中

  • attr: 线程的属性,一般情况下使用默认属性即可,写 NULL

  • start_routine: 函数指针,创建出的子线程的处理动作,也就是该函数在子线程中执行。

  • arg: 作为实参传递到 start_routine 指针指向的函数内部

返回值:线程创建成功返回 0,创建失败返回对应的错误号

2.2 创建线程

下面是创建线程的示例代码,在创建过程中一定要保证编写的线程函数与规定的函数指针类型一致:void *(*start_routine) (void *):

// pthread_create.c 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>// 子线程的处理代码
void* working(void* arg)
{printf("我是子线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<9; ++i){printf("child == i: = %d\n", i);}return NULL;
}int main()
{// 1. 创建一个子线程pthread_t tid;pthread_create(&tid, NULL, working, NULL);printf("子线程创建成功, 线程ID: %ld\n", tid);// 2. 子线程不会执行下边的代码, 主线程执行printf("我是主线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<3; ++i){printf("i = %d\n", i);}// 休息, 休息一会儿...// sleep(1);return 0;
}

编译测试程序,会看到如下错误信息:

$ gcc pthread_create.c 
/tmp/cctkubA6.o: In function `main':
pthread_create.c:(.text+0x7f): undefined reference to `pthread_create'
collect2: error: ld returned 1 exit status

错误原因是因为编译器链接不到线程库文件(动态库),需要在编译的时候通过参数指定出来,动态库名为 libpthread.so 需要使用的参数为 -l,根据规则掐头去尾最终形态应该写成:-lpthread(参数和参数值中间可以有空格)。正确的编译命令为:

# pthread_create 函数的定义在某一个库中, 编译的时候需要加库名 pthread
$ gcc pthread_create.c -lpthread
$ ./a.out 
子线程创建成功, 线程ID: 139712560109312
我是主线程, 线程ID: 139712568477440
i = 0
i = 1
i = 2

在打印的日志输出中为什么子线程处理函数没有执行完毕呢(只看到了子线程的部分日志输出)?

主线程一直在运行,执行期间创建出了子线程,说明主线程有 CPU 时间片,在这个时间片内将代码执行完毕了,主线程就退出了。子线程被创建出来之后需要抢 cpu 时间片, 抢不到就不能运行,如果主线程退出了, 虚拟地址空间就被释放了, 子线程就一并被销毁了。但是如果某一个子线程退出了, 主线程仍在运行, 虚拟地址空间依旧存在。

得到的结论:在没有人为干预的情况下,虚拟地址空间的生命周期和主线程是一样的,与子线程无关。

目前的解决方案:让子线程执行完毕,主线程再退出,可以在主线程中添加挂起函数 sleep();

3. 线程退出

在编写多线程程序的时候,如果想要让线程退出,但是不会导致虚拟地址空间的释放(针对于主线程),我们就可以调用线程库中的线程退出函数,只要调用该函数当前线程就马上退出了,并且不会影响到其他线程的正常运行,不管是在子线程或者主线程中都可以使用。

#include <pthread.h>
void pthread_exit(void *retval);
  • 参数:线程退出的时候携带的数据,当前子线程的主线程会得到该数据。如果不需要使用,指定为 NULL

下面是线程退出的示例代码,可以在任意线程的需要的位置调用该函数:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>// 子线程的处理代码
void* working(void* arg)
{sleep(1);printf("我是子线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<9; ++i){if(i==6){pthread_exit(NULL); // 直接退出子线程} printf("child == i: = %d\n", i);}return NULL;
}int main()
{// 1. 创建一个子线程pthread_t tid;pthread_create(&tid, NULL, working, NULL);printf("子线程创建成功, 线程ID: %ld\n", tid);// 2. 子线程不会执行下边的代码, 主线程执行printf("我是主线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<3; ++i){printf("i = %d\n", i);}// 主线程调用退出函数退出, 地址空间不会被释放pthread_exit(NULL);return 0;
}

4. 线程回收

4.1 线程函数

线程和进程一样,子线程退出的时候其内核资源主要由主线程回收,线程库中提供的线程回收函叫做 pthread_join(),这个函数是一个阻塞函数,如果还有子线程在运行,调用该函数就会阻塞,子线程退出函数解除阻塞进行资源的回收,函数被调用一次,只能回收一个子线程,如果有多个子线程则需要循环进行回收。

另外通过线程回收函数还可以获取到子线程退出时传递出来的数据,函数原型如下:

#include <pthread.h>
// 这是一个阻塞函数, 子线程在运行这个函数就阻塞
// 子线程退出, 函数解除阻塞, 回收对应的子线程资源, 类似于回收进程使用的函数 wait()
int pthread_join(pthread_t thread, void **retval);

参数:

  • thread: 要被回收的子线程的线程 ID

  • retval: 二级指针,指向一级指针的地址,是一个传出参数,这个地址中存储了 pthread_exit () 传递出的数据,如果不需要这个参数,可以指定为 NULL

返回值:线程回收成功返回 0,回收失败返回错误号。

4.2 回收子线程数据

在子线程退出的时候可以使用 pthread_exit() 的参数将数据传出,在回收这个子线程的时候可以通过 phread_join() 的第二个参数来接收子线程传递出的数据。接收数据有很多种处理方式,下面来列举几种:

4.2.1 使用子线程栈通过函数 pthread_exit(void *retval); 可以得知,子线程退出的时候,需要将数据记录到一块内存中,通过参数传出的是存储数据的内存的地址,而不是具体数据,由因为参数是 void* 类型,所有这个万能指针可以指向任意类型的内存地址。先来看第一种方式,将子线程退出数据保存在子线程自己的栈区:

// pthread_join.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>// 定义结构
struct Persion
{int id;char name[36];int age;
};// 子线程的处理代码
void* working(void* arg)
{printf("我是子线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<9; ++i){printf("child == i: = %d\n", i);if(i == 6){struct Persion p;p.age  =12;strcpy(p.name, "tom");p.id = 100;// 该函数的参数将这个地址传递给了主线程的pthread_join()pthread_exit(&p);}}return NULL; // 代码执行不到这个位置就退出了
}int main()
{// 1. 创建一个子线程pthread_t tid;pthread_create(&tid, NULL, working, NULL);printf("子线程创建成功, 线程ID: %ld\n", tid);// 2. 子线程不会执行下边的代码, 主线程执行printf("我是主线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<3; ++i){printf("i = %d\n", i);}// 阻塞等待子线程退出void* ptr = NULL;// ptr是一个传出参数, 在函数内部让这个指针指向一块有效内存// 这个内存地址就是pthread_exit() 参数指向的内存pthread_join(tid, &ptr);// 打印信息struct Persion* pp = (struct Persion*)ptr;printf("子线程返回数据: name: %s, age: %d, id: %d\n", pp->name, pp->age, pp->id);printf("子线程资源被成功回收...\n");return 0;
}

编译并执行测试程序:

# 编译代码
$ gcc pthread_join.c -lpthread
# 执行程序
$ ./a.out 
子线程创建成功, 线程ID: 140652794640128
我是主线程, 线程ID: 140652803008256
i = 0
i = 1
i = 2
我是子线程, 线程ID: 140652794640128
child == i: = 0
child == i: = 1
child == i: = 2
child == i: = 3
child == i: = 4
child == i: = 5
child == i: = 6
子线程返回数据: name: , age: 0, id: 0
子线程资源被成功回收...

通过打印的日志可以发现,在主线程中没有没有得到子线程返回的数据信息,具体原因是这样的:

如果多个线程共用同一个虚拟地址空间,每个线程在栈区都有一块属于自己的内存,相当于栈区被这几个线程平分了,当线程退出,线程在栈区的内存也就被回收了,因此随着子线程的退出,写入到栈区的数据也就被释放了。

4.2.2 使用全局变量

位于同一虚拟地址空间中的线程,虽然不能共享栈区数据,但是可以共享全局数据区和堆区数据,因此在子线程退出的时候可以将传出数据存储到全局变量、静态变量或者堆内存中。在下面的例子中将数据存储到了全局变量中:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>// 定义结构
struct Persion
{int id;char name[36];int age;
};struct Persion p; // 定义全局变量// 子线程的处理代码
void* working(void* arg)
{printf("我是子线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<9; ++i){printf("child == i: = %d\n", i);if(i == 6){// 使用全局变量p.age  =12;strcpy(p.name, "tom");p.id = 100;// 该函数的参数将这个地址传递给了主线程的pthread_join()pthread_exit(&p);}}return NULL;
}int main()
{// 1. 创建一个子线程pthread_t tid;pthread_create(&tid, NULL, working, NULL);printf("子线程创建成功, 线程ID: %ld\n", tid);// 2. 子线程不会执行下边的代码, 主线程执行printf("我是主线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<3; ++i){printf("i = %d\n", i);}// 阻塞等待子线程退出void* ptr = NULL;// ptr是一个传出参数, 在函数内部让这个指针指向一块有效内存// 这个内存地址就是pthread_exit() 参数指向的内存pthread_join(tid, &ptr);// 打印信息struct Persion* pp = (struct Persion*)ptr;printf("name: %s, age: %d, id: %d\n", pp->name, pp->age, pp->id);printf("子线程资源被成功回收...\n");return 0;
}

4.2.3 使用主线程栈

虽然每个线程都有属于自己的栈区空间,但是位于同一个地址空间的多个线程是可以相互访问对方的栈空间上的数据的。由于很多情况下还需要在主线程中回收子线程资源,所以主线程一般都是最后退出,基于这个原因在下面的程序中将子线程返回的数据保存到了主线程的栈区内存中:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>// 定义结构
struct Persion
{int id;char name[36];int age;
};// 子线程的处理代码
void* working(void* arg)
{struct Persion* p = (struct Persion*)arg;printf("我是子线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<9; ++i){printf("child == i: = %d\n", i);if(i == 6){// 使用主线程的栈内存p->age  =12;strcpy(p->name, "tom");p->id = 100;// 该函数的参数将这个地址传递给了主线程的pthread_join()pthread_exit(p);}}return NULL;
}int main()
{// 1. 创建一个子线程pthread_t tid;struct Persion p;// 主线程的栈内存传递给子线程pthread_create(&tid, NULL, working, &p);printf("子线程创建成功, 线程ID: %ld\n", tid);// 2. 子线程不会执行下边的代码, 主线程执行printf("我是主线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<3; ++i){printf("i = %d\n", i);}// 阻塞等待子线程退出void* ptr = NULL;// ptr是一个传出参数, 在函数内部让这个指针指向一块有效内存// 这个内存地址就是pthread_exit() 参数指向的内存pthread_join(tid, &ptr);// 打印信息printf("name: %s, age: %d, id: %d\n", p.name, p.age, p.id);printf("子线程资源被成功回收...\n");return 0;
}

在上面的程序中,调用 pthread_create() 创建子线程,并将主线程中栈空间变量 p 的地址传递到了子线程中,在子线程中将要传递出的数据写入到了这块内存中。也就是说在程序的 main() 函数中,通过指针变量 ptr 或者通过结构体变量 p 都可以读出子线程传出的数据。

5. 线程分离

在某些情况下,程序中的主线程有属于自己的业务处理流程,如果让主线程负责子线程的资源回收,调用 pthread_join() 只要子线程不退出主线程就会一直被阻塞,主要线程的任务也就不能被执行了。

在线程库函数中为我们提供了线程分离函数 pthread_detach(),调用这个函数之后指定的子线程就可以和主线程分离,当子线程退出的时候,其占用的内核资源就被系统的其他进程接管并回收了。线程分离之后在主线程中使用 pthread_join() 就回收不到子线程资源了。

#include <pthread.h>
// 参数就子线程的线程ID, 主线程就可以和这个子线程分离了
int pthread_detach(pthread_t thread);

下面的代码中,在主线程中创建子线程,并调用线程分离函数,实现了主线程和子线程的分离:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>// 子线程的处理代码
void* working(void* arg)
{printf("我是子线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<9; ++i){printf("child == i: = %d\n", i);}return NULL;
}int main()
{// 1. 创建一个子线程pthread_t tid;pthread_create(&tid, NULL, working, NULL);printf("子线程创建成功, 线程ID: %ld\n", tid);// 2. 子线程不会执行下边的代码, 主线程执行printf("我是主线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<3; ++i){printf("i = %d\n", i);}// 设置子线程和主线程分离pthread_detach(tid);// 让主线程自己退出即可pthread_exit(NULL);return 0;
}

6. 其他线程函数

6.1 线程取消

线程取消的意思就是在某些特定情况下在一个线程中杀死另一个线程。使用这个函数杀死一个线程需要分两步:

  • 在线程 A 中调用线程取消函数 pthread_cancel,指定杀死线程 B,这时候线程 B 是死不了的

  • 在线程 B 中进程一次系统调用(从用户区切换到内核区),否则线程 B 可以一直运行。

这其实和七步断肠散、含笑半步癫的功效是一样的,吃了毒药不动或者不笑也没啥事儿

#include <pthread.h>
// 参数是子线程的线程ID
int pthread_cancel(pthread_t thread);
  • 参数:要杀死的线程的线程 ID

  • 返回值:函数调用成功返回 0,调用失败返回非 0 错误号。

在下面的示例代码中,主线程调用线程取消函数,只要在子线程中进行了系统调用,当子线程执行到这个位置就挂掉了。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>// 子线程的处理代码
void* working(void* arg)
{int j=0;for(int i=0; i<9; ++i){j++;}// 这个函数会调用系统函数, 因此这是个间接的系统调用printf("我是子线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<9; ++i){printf(" child i: %d\n", i);}return NULL;
}int main()
{// 1. 创建一个子线程pthread_t tid;pthread_create(&tid, NULL, working, NULL);printf("子线程创建成功, 线程ID: %ld\n", tid);// 2. 子线程不会执行下边的代码, 主线程执行printf("我是主线程, 线程ID: %ld\n", pthread_self());for(int i=0; i<3; ++i){printf("i = %d\n", i);}// 杀死子线程, 如果子线程中做系统调用, 子线程就结束了pthread_cancel(tid);// 让主线程自己退出即可pthread_exit(NULL);return 0;
}

关于系统调用有两种方式:

  1. 直接调用 Linux 系统函数

  2. 调用标准 C 库函数,为了实现某些功能,在 Linux 平台下标准 C 库函数会调用相关的系统函数

6.2 线程 ID 比较

在 Linux 中线程 ID 本质就是一个无符号长整形,因此可以直接使用比较操作符比较两个线程的 ID,但是线程库是可以跨平台使用的,在某些平台上 pthread_t 可能不是一个单纯的整形,这中情况下比较两个线程的 ID 必须要使用比较函数,函数原型如下:

#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
  • 参数:t1 和 t2 是要比较的线程的线程 ID

  • 返回值:如果两个线程 ID 相等返回非 0 值,如果不相等返回 0

*声明:本文于网络整理,版权归原作者所有,如来源信息有误或侵犯权益,请联系我们删除或授权事宜。

e66c257111ecb73ed62eeb36dcc384b7.png

442822b1c8f4a703f5faaba57452cdf3.gif

戳“阅读原文”我们一起进步

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

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

相关文章

alexeyab darknet 编译_【目标检测实战】Darknet—yolov3模型训练(VOC数据集)

原文发表在&#xff1a;语雀文档0.前言本文为Darknet框架下&#xff0c;利用官方VOC数据集的yolov3模型训练&#xff0c;训练环境为&#xff1a;Ubuntu18.04下的GPU训练&#xff0c;cuda版本10.0&#xff1b;cudnn版本7.6.5。经过一晚上的训练&#xff0c;模型20个类别的mAP达到…

html字符串转svg,【SVG】如何操作SVG Text

上周我们学习了如何使用元素创建SVG文本。在实例中我们设置了x和y坐标来定位文本&#xff0c;也尝试了给SVG文本中的每个字符定位。关于元素还有很多内容。在处理SVG文本时&#xff0c;不要局限于x和y属性。元素还有几个可以添加的属性&#xff0c;现在我们开始讨论吧。dx和dy属…

C++ 面试考点(三)

点击蓝字关注我们21、构造函数和析构函数可以调用虚函数吗&#xff0c;为什么在C中&#xff0c;提倡不在构造函数和析构函数中调用虚函数&#xff1b;在构造函数和析构函数调用的所有函数(包括虚函数)都是编译时确定的, 虚函数将运行该类中的版本.因为父类对象会在子类之前进行…

用终端访问路由器设置端口开发_serial for mac(终端管理软件)v2.0.3

原标题&#xff1a;serial for mac(终端管理软件)v2.0.3serial for mac是应用在Mac上的一款终端管理软件&#xff0c;可以帮助您连接和控制串行设备&#xff0c;如服务器&#xff0c;路由器或调制解调器等网络设备&#xff0c;PBX系统等。好消息是Serial为大多数串行设备提供了…

大神级的C++性能优化,你能看懂吗?

点击蓝字关注我们一、前言性能优化不管是从方法论还是从实践上都有很多东西&#xff0c;文章会从C语言本身入手&#xff0c;介绍一些性能优化的方法&#xff0c;希望能做到简洁实用。二、实例1在开始本文的内容之前&#xff0c;让我们看段小程序&#xff1a;// 获取一个整数对应…

钢笔墨水能否代替打印机墨水_LAMY钢笔应该如何选择墨水?

其实墨水世面上基本是有两种&#xff1a;碳素墨水和非碳素。碳素墨水相对比较堵笔&#xff0c;碳素墨水对钢笔本身腐蚀性不大&#xff0c;腐蚀性大的那是染料/颜料墨水。碳素墨水写字非常容易有笔锋&#xff0c;而非碳素墨水就显得略微柔和了。有人说&#xff0c;字写得好的人用…

html ctf查找,Web CTF 解题思路总结—南京邮电大学攻防平台writeup

1、直接查看源代码例&#xff1a;签到题(50)2、PHP的特性(1)MD5 碰撞例&#xff1a;md5 collision(50)md5碰撞&#xff1a;MD5摘要算法可以从多个字节组成的串中计算出由32个字节构成的“特征串”&#xff0c;对于超过32字节的串来说&#xff0c;MD5计算得出的值必然是其一个子…

java 1.8新增功能_睡觉时:新增的Java 8新增功能

java 1.8新增功能自Java 8推出以来&#xff0c;最有趣的功能是什么&#xff1f; Java 8最近庆祝了它的第一个生日&#xff0c;其主要版本已经在一年多以前了。 这当然值得庆祝。 自从最初的Java 8版本问世以来&#xff0c;已经发布了六个更新。 这些更新中的某些元素是次要的…

C++ 为什么不加入垃圾回收机制

点击蓝字关注我们Java的爱好者们经常批评C中没有提供与Java类似的垃圾回收(Gabage Collector)机制(这很正常&#xff0c;正如C的爱好者有时也攻击Java没有这个没有那个&#xff0c;或者这个不行那个不够好)&#xff0c;导致C中对动态存储的官吏称为程序员的噩梦&#xff0c;不是…

c++ 共享内存_Python3.8多进程之共享内存

最近发了个宏愿想写一个做企业金融研究的Python框架。拖出Python一看已经更新到了3.8&#xff0c;于是就发现了Python 3.8里新出现的模块&#xff1a;multiprocessing.shared_memory。随手写了个测试。生成一个240MB大小的pandas.DataFrame&#xff0c;然后转换成numpy.recarra…

计算机主机箱外部介绍图,电脑的主机结构是怎样的 电脑主机结构图【图文】...

在电脑已经普及的今天&#xff0c;基本上每家每户都有电脑了&#xff0c;大家用它来看电影&#xff0c;搜索资料啊&#xff0c;上网啊等等。在我们日常的娱乐方式中&#xff0c;电脑也是我们的娱乐项目之一&#xff0c;至少还是好多人用它看电影和追剧的。但是机器会有故障的时…

C 语言各数据类型的内存映像

点击蓝字关注我们C语言各种数据类型的内存映像&#xff08;32位平台&#xff09;&#xff1a;0 signed char#include <stdio.h> int main() {char min 1<<7;char max (1<<7)-1;for(int imin;i<max;i)if(i<0)printf("%.2X ",(unsigned char…

用java编写一个图书管理系统_手把手教你编写第一个java程序

安装完jdk后我们就可以试着编写第一个java程序了&#xff0c;让我们一起来试试吧&#xff01;第一步点击开始——所有程序——附件——记事本&#xff0c;新建记事本&#xff0c;输入以下代码&#xff1a;class HelloWorld { public static void main(String args[]) { System.…

go micro java_Java Micro Framework:您无法忽略的新趋势

go micro java什么是Java微框架&#xff0c;为什么要使用它们&#xff1f; 每种语言都有权衡。 对于Java&#xff0c;要成为一种安全&#xff0c;经过严格测试&#xff0c;向后兼容的语言&#xff0c;就要在敏捷性和简化方面做出一些牺牲。 无可否认&#xff0c;有些冗长和冗长…

C语言调用C++类成员函数讲解和实例

点击蓝字关注我们1、问题成因C语言与C调用问题原因主要在于C编译器和C编译器的不同。C是过程式语言&#xff0c;C编译器编译后&#xff0c;函数在符号库中就是函数名&#xff0c;没有其他任何附加信息。而C是对象式语言&#xff0c;支持函数重载&#xff0c;C编译器编译后&…

学生命科学要学计算机吗,现在学生物学出路真的有那么不济吗?

最近浏览知乎&#xff0c;看到大规模的生物劝退贴&#xff0c;这些帖子纷纷拿出多个例子&#xff0c;并现身说法&#xff0c;告诫学生物的同学尽早转行&#xff0c;并声称劝退一人胜发7篇CNS。作为网友盛传的天坑之首&#xff0c;生物专业到底有多坑&#xff1f;环球科学曾对北…

java笔试题_Java面试才到笔试就没有然后了?快来签收,高频笔试57题及解答

前言很多人面试之前&#xff0c;可能没有在互联网公司工作过或者说工作过但年头较短&#xff0c;不知道互联网公司技术面试都会问哪些问题&#xff1f; 再加上可能自己准备也不充分&#xff0c;去面试没几个回合就被面试官几个问题打蒙了&#xff0c;甚至笔试都过不了。最后以惨…

java开发错误_每个Java开发人员都必须避免的9个安全错误

java开发错误Checkmarx CxSAST是功能强大的源代码分析&#xff08;SCA&#xff09;解决方案&#xff0c;旨在从根本上识别&#xff0c;跟踪和修复技术和逻辑安全漏洞&#xff1a;源代码。 在这里查看 &#xff01; 自从1995年中期引入Java以来​​&#xff0c;它已经走了很长一…

C语言中常用的标准库函数有哪些?

点击蓝字关注我们标准头文件包括&#xff1a;<asset.h> <ctype.h> <errno.h> <float.h> <limits.h> <locale.h> <math.h> <setjmp.h> <signal.h> <stdarg.h> <…

计算机考试一级考试基础知识,全国计算机等级考试一级msoffice基础知识

全国计算机等级考试一级msoffice基础知识导语&#xff1a;在日常生活中&#xff0c;媒体(Medium &#xff0c;复数形式为Media )是指文字、声音、图像、动画和视频等内容。多媒体(Multimedia )是指能够同时对两种或两种以上媒体进行采集、操作、编辑、存储等综合处理的技术。多…