
yarco
@yarco
Best posts made by yarco
Latest posts made by yarco
-
企业信息化概论
注: 本文为原创,转载请说明出处
企业/公司的信息化本质上其实是一种“再创业”,在另一个维度上. 和一般的公司成立初期多少有些相似之处。所以这就意味着,企业的信息化建设需要有和“创业”相当的勇气与资源(当然并不完全等同)。
假使一个传统企业,它的产品是A. 那么企业当前的核心自然是为了产品A服务的(这点毫无置疑). 假使这个企业开始信息化转型/建设, 那么在初始阶段,显然是没有完整人力与信息化资源的 -- 这就相当于需要建立一支有别于当前文化与核心的东西. 因为虽然信息化最终的目标是让企业获利,但它自身的发展显然和"为产品A服务"无关, 比如“AI与智能化”,它本身其实是通用技术,怎么去用其实是一种特殊化. 这事相当于是说:
- (1阶) 传统企业当前的所有部门 => 目标是 产品A (如何生产/制造/销售)
- (2阶) 信息/软件部门 => 目标是信息/软件本身能力的提升 => 特别的: 为产品A服务
所以信息化部门,在一个企业/公司内多少会有点格格不入 -- 如果融合的好,老板的角度, 看起来也能用(但不知道怎么用, 特别是老板来自传统企业对计算机技术毫无所知的情况下);融合不好,就等于这个企业信息化失败。
其实如果把这件事当成是再一次创业,一切都会简单很多(至少理解上)。
在公司规模尚不算很大的情况下,信息化建设的早期通常以购买乙方的软件设施来实现,可以把这想象成为企业初始创立阶段,通过copy&paste, 代工来实现产品 -- 你是没有核心东西的,所以你需要更专业化的来帮助你实现这些东西,然后你通过运维来体现这些东西的价值。当然与最初的创业相比,显然运转良好的企业有足够的资金去做这件事 -- 但这同样需要管理层的魄力, 毕竟这是把1阶的利润拿来去发展2阶的东西。当企业开始步入高级的阶段, 软件与信息化的作用开始体现,比如你只能用人工的东西,但别的企业却能实现各种自动化节省人力物力。显然,在高级的阶段,一个企业的"信息/软件能力"本身就变成了核心竞争力,在这个节点上,很明确的事情是,企业为能确保这种优势,需要将这种"信息/软件能力"变成自有的工具,各种信息化数据,技术成为该企业核心的需要保护的内容,类似前面的分类:
- (1阶) 传统企业当前的所有部门 => 产品A => 核心竞争力是对产品A的, 比如 设计/销售网络 等等
- (2阶) 信息/软件部门 => 信息/软件能力 => 核心竞争力是围绕产品A发展起来的 数据收集/技术手段.
(当然信息化建设还有基于公司的规模还有更高级的阶段,此处不再阐述,可以参考各个大公司的开源行为)
如何把控和实现,将是对公司的管理层(以及信息部负责人)的再次创业挑战 -- 而且这一次,是企业付出成本,去养大一个性格迥异的孩子。
-
PHP FFI详解 - 一种全新的PHP扩展方式
From: Laruence
随着PHP7.4而来的有一个我认为非常有用的一个扩展:PHP FFI(Foreign Function interface), 引用一段PHP FFI RFC中的一段描述:
For PHP, FFI opens a way to write PHP extensions and bindings to C libraries in pure PHP.
是的,FFI提供了高级语言直接的互相调用,而对于PHP来说,FFI让我们可以方便的调用C语言写的各种库。
其实现有大量的PHP扩展是对一些已有的C库的包装,比如常用的mysqli, curl, gettext等,PECL中也有大量的类似扩展。
传统的方式,当我们需要用一些已有的C语言的库的能力的时候,我们需要用C语言写wrapper,把他们包装成扩展,这个过程中就需要大家去学习PHP的扩展怎么写,当然现在也有一些方便的方式,比如Zephir. 但总还是有一些学习成本的,而有了FFI以后,我们就可以直接在PHP脚本中调用C语言写的库中的函数了。
而C语言几十年的历史中,积累了大量的优秀的库,FFI直接让我们可以方便的享受这个庞大的资源了。
言归正传,今天我用一个例子来介绍,我们如何使用PHP来调用libcurl,来抓取一个网页的内容,为什么要用libcurl呢? PHP不是已经有了curl扩展了么? 嗯,首先因为libcurl的api我比较熟,其次呢,正是因为有了,才好对比,传统扩展方式和FFI方式直接的易用性不是?
首先,比如我们就拿当前你看的这篇文章为例,我现在需要写一段代码来抓取它的内容,如果用传统的PHP的curl扩展,我们大概会这么写:
<?php $url = "https://www.laruence.com/2020/03/11/5475.html"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); curl_exec($ch); curl_close($ch);
(因为我的网站是https的,所以会多一个设置SSL_VERIFYPEER的操作)那如果是用FFI呢?
首先要启用PHP7.4的ext/ffi,需要注意的是PHP-FFI要求libffi-3以上。
然后,我们需要告诉PHP FFI我们要调用的函数原型是咋样的,这个我们可以使用FFI::cdef, 它的原型是:
FFI::cdef([string $cdef = "" [, string $lib = null]]): FFI
在string $cdef中,我们可以写C语言函数式申明,FFI会parse它,了解到我们要在string $lib这个库中调用的函数的签名是啥样的,在这个例子中,我们用到三个libcurl的函数,它们的申明我们都可以在libcurl的文档里找到,比如对于curl_easy_init.
具体到这个例子,我们写一个curl.php, 包含所有要申明的东西,代码如下:
$libcurl = FFI::cdef(<<<CTYPE void *curl_easy_init(); int curl_easy_setopt(void *curl, int option, ...); int curl_easy_perform(void *curl); void curl_easy_cleanup(void *handle); CTYPE , "libcurl.so" );
这里有个地方是,文档中写的是返回值是CURL *,但事实上因为我们的例子中不会解引用它,只是传递,那就避免麻烦就用void *代替。
然而还有个麻烦的事情是,PHP预定义好了CURLOPT_等option的值,但现在我们需要自己定义,简单的办法就是查看curl的头文件,找到对应的值,然后我们把值给加进去:
<?php const CURLOPT_URL = 10002; const CURLOPT_SSL_VERIFYPEER = 64; $libcurl = FFI::cdef(<<<CTYPE void *curl_easy_init(); int curl_easy_setopt(void *curl, int option, ...); int curl_easy_perform(void *curl); void curl_easy_cleanup(void *handle); CTYPE , "libcurl.so" );
好了,定义部分就算完成了,现在我们完成实际逻辑部分,整个下来的代码会是:
<?php require "curl.php"; $url = "https://www.laruence.com/2020/03/11/5475.html"; $ch = $libcurl->curl_easy_init(); $libcurl->curl_easy_setopt($ch, CURLOPT_URL, $url); $libcurl->curl_easy_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); $libcurl->curl_easy_perform($ch); $libcurl->curl_easy_cleanup($ch);
怎么样,相比使用curl扩展的方式, 是不是一样简练呢?
接下来,我们稍微弄的复杂一点,也即使,如果我们不想要结果直接输出,而是返回成一个字符串呢, 对于PHP的curl扩展来说,我们只需要调用curl_setop 把CURLOPT_RETURNTRANSFER为1,但在libcurl中其实并没有直接返回字符串的能力,而是提供了一个WRITEFUNCTION的回调函数,在有数据返回的时候,libcurl会调用这个函数, 事实上PHP curl扩展也是这么做的.
目前我们并不能直接把一个PHP函数作为回调函数通过FFI传递给libcurl, 那我们会有俩种方式来做:
- 采用WRITEDATA, 默认的libcurl会调用fwrite作为回调函数,而我们可以通过WRITEDATA给libcurl一个fd,让它不要写入stdout,而是写入到这个fd
- 我们自己编写一个C的简单函数,通过FFI引入进来,传递给libcurl.
我们先用第一种方式,首先我们需要使用fopen,这次我们通过定义个C的头文件来申明原型(file.h):
void *fopen(char *filename, char *mode); void fclose(void * fp);
像file.h一样,我们把所有的libcurl的函数申明也放到curl.h中去
#define FFI_LIB "libcurl.so" void *curl_easy_init(); int curl_easy_setopt(void *curl, int option, ...); int curl_easy_perform(void *curl); void curl_easy_cleanup(CURL *handle);
然后我们就可以使用FFI::load来加载.h文件:
static function load(string $filename): FFI;
但是怎么告诉FFI加载那个对应的库呢?如上面,我们通过定义了一个FFI_LIB的宏,来告诉FFI这些函数来自libcurl.so, 当我们用FFI::load加载这个h文件的时候,PHP FFI就会自动载入libcurl.so
那为什么fopen不需要指定加载库呢,那是因为FFI也会在全局符号表中查找符号,而fopen是一个标准库函数,它早就存在了。
好,现在整个代码会是:
<?php const CURLOPT_URL = 10002; const CURLOPT_SSL_VERIFYPEER = 64; const CURLOPT_WRITEDATA = 10001; $libc = FFI::load("file.h"); $libcurl = FFI::load("curl.h"); $url = "https://www.laruence.com/2020/03/11/5475.html"; $tmpfile = "/tmp/tmpfile.out"; $ch = $libcurl->curl_easy_init(); $fp = $libc->fopen($tmpfile, "a"); $libcurl->curl_easy_setopt($ch, CURLOPT_URL, $url); $libcurl->curl_easy_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); $libcurl->curl_easy_setopt($ch, CURLOPT_WRITEDATA, $fp); $libcurl->curl_easy_perform($ch); $libcurl->curl_easy_cleanup($ch); $libc->fclose($fp); $ret = file_get_contents($tmpfile); @unlink($tmpfile);
但这种方式呢就是需要一个临时的中转文件,还是不够优雅, 现在我们用第二种方式,要用第二种方式,我们需要自己用C写一个回调函数传递给libcurl:
#include <stdlib.h> #include <string.h> #include "write.h" size_t own_writefunc(void *ptr, size_t size, size_t nmember, void *data) { own_write_data *d = (own_write_data*)data; size_t total = size * nmember; if (d->buf == NULL) { d->buf = malloc(total); if (d->buf == NULL) { return 0; } d->size = total; memcpy(d->buf, ptr, total); } else { d->buf = realloc(d->buf, d->size + total); if (d->buf == NULL) { return 0; } memcpy(d->buf + d->size, ptr, total); d->size += total; } return total; } void * init() { return &own_writefunc; }
注意此处的init函数,因为在PHP FFI中,就目前的版本(2020-03-11)我们没有办法直接获得一个函数指针,所以我们定义了这个函数,返回own_writefunc的地址。
最后我们定义上面用到的头文件write.h:
#define FFI_LIB "write.so" typedef struct _writedata { void *buf; size_t size; } own_write_data; void *init();
注意到我们在头文件中也定义了FFI_LIB, 这样这个头文件就可以同时被write.c和接下来我们的PHP FFI共同使用了。
然后我们编译write函数为一个动态库:
gcc -O2 -fPIC -shared -g write.c -o write.so
好了, 现在整个的代码会变成:
<?php const CURLOPT_URL = 10002; const CURLOPT_SSL_VERIFYPEER = 64; const CURLOPT_WRITEDATA = 10001; const CURLOPT_WRITEFUNCTION = 20011; $libcurl = FFI::load("curl.h"); $write = FFI::load("write.h"); $url = "https://www.laruence.com/2020/03/11/5475.html"; $data = $write->new("own_write_data"); $ch = $libcurl->curl_easy_init(); $libcurl->curl_easy_setopt($ch, CURLOPT_URL, $url); $libcurl->curl_easy_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); $libcurl->curl_easy_setopt($ch, CURLOPT_WRITEDATA, FFI::addr($data)); $libcurl->curl_easy_setopt($ch, CURLOPT_WRITEFUNCTION, $write->init()); $libcurl->curl_easy_perform($ch); $libcurl->curl_easy_cleanup($ch); ret = FFI::string($data->buf, $data->size);
此处, 我们使用FFI::new ($write->new)来分配了一个struct _write_data的内存:
function FFI::new(mixed $type [, bool $own = true [, bool $persistent = false]]): FFI\CData
$own表示这个内存管理是否采用PHP的内存管理,默认的情况下,我们申请的内存会经过PHP的生命周期管理,不需要主动释放,但是有的时候你也可能希望自己管理,那么可以设置$own为flase,那么在适当的时候,你需要调用FFI::free去主动释放。
然后我们把$data作为WRITEDATA传递给libcurl, 此处我们使用了FFI::addr来获取$data的实际内存地址:
static function addr(FFI\CData $cdata): FFI\CData;
然后我们把own_write_func作为WRITEFUNCTION传递给了libcurl,这样再有返回的时候,libcurl就会调用我们的own_write_func来处理返回,同时会把write_data作为自定义参数传给我们的回调函数。
最后我们使用了FFI::string来把一段内存转换成PHP的string:
static function FFI::string(FFI\CData $src [, int $size]): string
当不提供$size的时候,FFI::string会在遇到Null-byte的时候停止。
好了,跑一下吧?
然而毕竟直接在PHP中每次请求都加载so的话,会是一个很大的性能问题,所以我们也可以采用preload的方式,这种模式下, 我们通过opcache.preload来在PHP启动的时候就加载好:
ffi.enable=1 opcache.preload=ffi_preload.inc
ffi_preload.inc:
<?php FFI::load("curl.h"); FFI::load("write.h");
但我们引用载入的FFI呢? 为此我们需要修改一下这俩个.h头文件,加入FFI_SCOPE, 比如curl.h:
#define FFI_LIB "libcurl.so" #define FFI_SCOPE "libcurl" void *curl_easy_init(); int curl_easy_setopt(void *curl, int option, ...); int curl_easy_perform(void *curl); void curl_easy_cleanup(void *handle);
对应的我们给write.h也加入FFI_SCOPE为"write", 然后我们的脚本现在看起来应该是这样:
<?php const CURLOPT_URL = 10002; const CURLOPT_SSL_VERIFYPEER = 64; const CURLOPT_WRITEDATA = 10001; const CURLOPT_WRITEFUNCTION = 20011; $libcurl = FFI::scope("libcurl"); $write = FFI::scope("write"); $url = "https://www.laruence.com/2020/03/11/5475.html"; $data = $write->new("own_write_data"); $ch = $libcurl->curl_easy_init(); $libcurl->curl_easy_setopt($ch, CURLOPT_URL, $url); $libcurl->curl_easy_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); $libcurl->curl_easy_setopt($ch, CURLOPT_WRITEDATA, FFI::addr($data)); $libcurl->curl_easy_setopt($ch, CURLOPT_WRITEFUNCTION, $write->init()); $libcurl->curl_easy_perform($ch); $libcurl->curl_easy_cleanup($ch); ret = FFI::string($data->buf, $data->size);
也就是,我们现在使用FFI::scope来代替FFI::load,引用对应的函数。
static function scope(string $name): FFI;
然后还有另外一个问题,FFI虽然给了我们很大的灵活性,但是毕竟直接调用C库函数,还是非常具有风险性的,我们应该只容许用户调用我们确认过的函数,于是,ffi.enable=preload就该上场了,当我们设置ffi.enable=preload的话,那就只有在opcache.preload的脚本中的函数才能调用FFI,而用户写的函数是没有办法直接调用的。
我们稍微修改下ffi_preload.inc变成ffi_safe_preload.inc
<?php class CURLOPT { const URL = 10002; const SSL_VERIFYHOST = 81; const SSL_VERIFYPEER = 64; const WRITEDATA = 10001; const WRITEFUNCTION = 20011; } FFI::load("curl.h"); FFI::load("write.h"); function get_libcurl() : FFI { return FFI::scope("libcurl"); } function get_write_data($write) : FFI\CData { return $write->new("own_write_data"); } function get_write() : FFI { return FFI::scope("write"); } function get_data_addr($data) : FFI\CData { return FFI::addr($data); } function paser_libcurl_ret($data) :string{ return FFI::string($data->buf, $data->size); }
也就是,我们把所有会调用FFI API的函数都定义在preload脚本中,然后我们的例子会变成(ffi_safe.php):
<?php $libcurl = get_libcurl(); $write = get_write(); $data = get_write_data($write); $url = "https://www.laruence.com/2020/03/11/5475.html"; $ch = $libcurl->curl_easy_init(); $libcurl->curl_easy_setopt($ch, CURLOPT::URL, $url); $libcurl->curl_easy_setopt($ch, CURLOPT::SSL_VERIFYPEER, 0); $libcurl->curl_easy_setopt($ch, CURLOPT::WRITEDATA, get_data_addr($data)); $libcurl->curl_easy_setopt($ch, CURLOPT::WRITEFUNCTION, $write->init()); $libcurl->curl_easy_perform($ch); $libcurl->curl_easy_cleanup($ch); $ret = paser_libcurl_ret($data);
这样一来通过ffi.enable=preload, 我们就可以限制,所有的FFI API只能被我们可控制的preload脚本调用,用户不能直接调用。从而我们可以在这些函数内做好尽可能的安全保证工作,从而保证一定的安全性。
好了,经过这个例子,大家应该对FFI有了一个比较深入的理解了,详细的PHP API说明,大家可以参考:PHP-FFI Manual, 有兴趣的话,就去找一个C库,试试吧?
本文的例子,你可以在我的github上下载到:FFI example
最后还是多说一句,例子只是为了演示功能,所以省掉了很多错误分支的判断捕获,大家自己写的时候还是要加入。毕竟使用FFI的话,会让你会有1000种方式让PHP segfault crash,所以be careful
-
RBAC vs. ABAC: What’s the Difference?
From: https://www.dnsstuff.com/rbac-vs-abac-access-control
In any company, network users must be both authenticated and authorized before they can access parts of the system capable of leading to security breaches. The process of gaining authorization is called access control. In this guide, I discuss the two main methods for managing access control for your systems—role-based access control (RBAC) and attribute-based access control (ABAC)—their differences, and the importance of using an access rights management tool. I also review SolarWinds
Access Rights Manager, which is my top choice for a comprehensive solution to help teams more easily monitor access control across their organization.
Authentication and Authorization
The two fundamental aspects of security are authentication and authorization. After you enter your credentials to log in to your computer or sign in to an app or software, the device or application undertakes authentication to determine your level of authorization. Authorization may include what accounts you can use, what resources you have access to, and what functions you are permitted to carry out.
Role-Based Access Control (RBAC) vs. Attribute-Based Access Control (ABAC)
Role-based access control (RBAC) and attribute-based access control (ABAC) are two ways of controlling the authentication process and authorizing users. The primary difference between RBAC and ABAC is RBAC provides access to resources or information based on user roles, while ABAC provides access rights based on user, environment, or resource attributes. Essentially, when considering RBAC vs. ABAC, RBAC controls broad access across an organization, while ABAC takes a fine-grain approach.
What Is RBAC?
RBAC is role-based, so depending on your role in the organization, you will have different access permissions. This is determined by an administrator, who sets the parameters of what access a role should have, along with which users are assigned which roles. For instance, some users may be assigned to a role where they can write and edit particular files, whereas other users may be in a role restricted to reading files but not editing them.
It’s possible for one user to be assigned multiple roles, giving them access to numerous different files or abilities. Say there’s a team of people working on a large project. The project manager will have access to all the files and can edit and change things within the project. However, the development team might only be allowed access to the programming files and won’t be able to see or edit the financial information or employee details for the project. On the flip side, the human resources or management team might have access to all the employee and financial information but has no use for the programming files.
An organization might use RBAC for projects like this because with RBAC, the policies don’t need to be changed every time a person leaves the organization or changes jobs: they can simply be removed from the role group or allocated to a new role. This also means new employees can be granted access relatively quickly, depending on the organizational role they fulfill.
What Is ABAC?
Attribute-based access control draws on a set of characteristics called “attributes.” This includes user attributes, environmental attributes, and resource attributes.
- User attributes include things like the user’s name, role, organization, ID, and security clearance.
- Environmental attributes include the time of access, location of the data, and current organizational threat levels.
- Resource attributes include things like creation date, resource owner, file name, and data sensitivity.
Essentially, ABAC has a much greater number of possible control variables than RBAC. ABAC is implemented to reduce risks due to unauthorized access, as it can control security and access on a more fine-grained basis. For example, instead of people in the HR role always being able to access employee and payroll information, ABAC can place further limits on their access, such as only allowing it during certain times or for certain branch offices relevant to the employee in question. This can reduce security issues and can also help with auditing processes later.
RBAC vs. ABAC
Generally, if RBAC will suffice, you should use it before setting up ABAC access control. Both these access control processes are filters with ABAC being the more complex of the two, requiring more processing power and time. There’s no point in using this more powerful filter—and incurring the accompanying resource cost—if you don’t need it.
Either way, it’s important to use the minimum number of RBAC and ABAC filters to structure your access and security landscape. It can help to carefully plan out your directory data and access approaches to make sure you aren’t using unnecessary filters or making things overly complex. In many cases, RBAC and ABAC can be used together hierarchically, with broad access enforced by RBAC protocols and more complex access managed by ABAC. This means the system would first use RBAC to determine who has access to a resource, followed by ABAC to determine what they can do with the resource and when they can access it.
Best Access Management Tools
Whether you use RBAC or ABAC, or a combination of the two, I strongly suggest using an access rights management tool. A good tool can streamline the setup and cut down on the administrative overhead involved in setting and managing filters. My pick is Access Rights Manager, a high-quality tool built to manage and audit access rights across your IT infrastructure.
Access Rights Manager includes a user management system to monitor, analyze, and report on Active Directory and Group Policy, and can show you what changes have been made, by whom, and when, which helps you to minimize the likelihood of an insider threat. It includes templates designed to help you enforce role-specific security and the provisioning and deprovisioning of user accounts. You can smoothly delegate access to files, folders, or resources in a simple process to reduce administrative overhead.
How to Choose an Access Control Solution
When it comes to security, it’s crucial to plan and monitor your access control processes carefully. Use a robust access management tool to help you set up your access control, and regularly review your setup to make sure it still fits your organizational needs. Whether you invest in Access Rights Manager from SolarWinds or go another route, make sure the tool you choose can set up a protocol and mechanism to ensure users have the correct access to what they need to do their jobs, and nothing more.
-
(leetcode) 3. 无重复字符的最长子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb" 输出: 1 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
示例 4:
输入: s = "" 输出: 0
提示:
- 0 <= s.length <= 5 * 104
- s 由英文字母、数字、符号和空格组成
-
RE: (leetcode) 1. 两数之和
其实题目讲的不是很清楚, 比如数据规模, 以及整数范围. 以及"数组中同一个元素不能使用两遍", 我误以为不可重复. 以下解法是当成不可重复的来实现的, 好在我们关注的练习, 而不是非得解题。
A. 假如数据规模不是很大, 并且是正整数的话, 那么可以用数组来存储位置, 比如示例中,
num = [2,7,11,15]
就可以转化成num[2]=1, num[7]=2,num[11]=3,num[15]=4
我们假定偏移从1开始, 如果是0, 则表示不存在对应值.步骤如下:
- 从数组num中找出最大值
- 根据最大值分配数组T
- 循环数组num,设置T对应位置的偏移(注: 从1开始, 因为0被用来表示不存在)
- 再循环数组num, 得到 [target - 当前元素] 对应的偏移位置, 便是了...
大致是3次遍历num, 即, O(3n)
// // PHP代码如下 // 我感觉PHP做为一种宏语言来描述算法也不错 // function twoSum($nums, $target) { $maps = array_flip($nums); $rests = array_map(function($item) use($target) { return $target - $item; }, $nums); // leetcode 里php版本不是最新的 不然 fn($item) => $target - $item // 自然是最好的 $ret = []; foreach($rests as $k => $v) { if (isset($maps[$v]) && ($v<<1 !== $target)) { $ret[$k] = true; } } return array_keys($ret); }
-
(leetcode) 1. 两数之和
来自: https://leetcode-cn.com/problems/two-sum/
两数之和
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
示例:
给定 nums = [2, 7, 11, 15], target = 9 因为 nums[0] + nums[1] = 2 + 7 = 9 所以返回 [0, 1]
-
收集的c/c++练习项目
https://zhuanlan.zhihu.com/p/23047091
- 简单计算器
- 通讯录
- epoll实现高并发聊天室
- 万年历
- 2048
- flappy bird
- 扫雷
- 五子棋
- 简易PHP web服务器
- ping
- linux嗅探
- 文件类型统计程序
- 多线程排序
- 太阳系行星系统
- 银行排队服务模拟
- 线程池
- Markdown解析
https://www.zhihu.com/question/67158058/answer/250574321
https://zhuanlan.zhihu.com/p/146951957
- 自己的编程语言
- 打字练习软件
- 贪吃蛇
https://zhuanlan.zhihu.com/p/59934714
四、用C语言实现Linux命令
最后,再介绍如何用C语言实现Linux命令,通过学习可以了解Linux操作系统。
14、C语言实现Linuxtouch命令
C语言实现Linuxtouch命令项目,学习基于LINUX环境的系统编程技术,尤其Linux文件IO操作相关技术。
15、C语言实现Linuxcp命令
C语言实现Linuxcp命令项目的学习,可以掌握Linux操作系统中的文件IO相关的系统函数和目录相关操作的系统函数,比如open,write,opendir,readir。深入了解Linux环境系统编程。
16、C语言实现Linuxls命令
使用C语言实现Linuxls命令,学习linux目录与文件属性。
17、C语言实现Linuxwho命令
通过C语言实现Linuxwho命令项目的学习,可以掌握Linux操作系统为上层提供的访问系统数据文件的接口。更好的理解Linux操作系统工作的原理。 -
【摘录】2020年度最佳的23个的机器学习项目
摘自: https://zhuanlan.zhihu.com/p/181089803
面向初学者的机器学习项目
- 鸢尾花分类项目
数据集 - Emojify –使用Python创建自己的表情符号
介绍 - 使用机器学习进行贷款预测
在kaggle.com - 住房价格预测项目
数据集 - MNIST数字分类机器学习项目
数据集 - 使用机器学习预测股价
在kaggle.com - 泰坦尼克号生存计划
在kaggle.com - 葡萄酒质量检测项目
数据集 - 假新闻检测项目
介绍
中级机器学习项目
- 音乐流派分类机器学习项目
介绍 - 比特币价格预测器项目
在kaggle.com - Uber数据分析项目
介绍 - 人格预测项目
在kaggle.com - Xbox游戏预测项目
在kaggle.com - 信用卡欺诈检测项目
介绍 - 使用机器学习进行客户细分
介绍
高级机器学习项目
- 鸢尾花分类项目