指针的奇技淫巧一__认识指针

指针是一个C语言中比较重要的概念,C语言对内容管理的技巧也逃不掉指针的概念。这边我结合《深入理解C指针》这本书做了一些笔记。

为什么要精通指针

  • 写出快速高效的代码
  • 为解决很多类问题提供方便的途径
  • 支持动态分配内存
  • 使表达式更加的紧凑和简洁
  • 提供用指针传递数据结构的能力而不会带来庞大的开销
  • 保护作为参数传递给函数的数据

声明指针

通过在数据后面跟星号,再加上指针变量名字就可以声明一个指针

1
2
int num;
int *pi

星号两边的空白符无关紧要,下面的声明等价

1
2
3
4
int* pi
int * pi
int *pi
int*pi

*将变量声明为指针,这是一个重载过的符号,它也用在乘法和解引指针上

如何阅读声明

阅读一个指针推荐的阅读方式是重后往前读。

理解 声明
pci是一个变量 const int *pci
pci是一个指针变量 const int *pci
pci是一个指向整数的指针变量 const int *pci
pci是一个指向整数常量的指针变量 const int *pci

地址操作符

地址操作符&会返回操作数的地址。我们可以用这个操作符来初始化pi指针

1
2
3
num = 0;
pi = #

可以在声明变量pi的同时把它初始化为num地址

1
2
int num;
int *pi = #

尽快的初始化指针是一个好习惯

1
int *pi = null;

打印指针的值

1
2
3
4
int num = 0;
int *pi = #
printf("Address of num :%d value:%d \n",&num,num);
printf("Address of pi :%d value:%d \n",&pi,num);

输出

这里输出的真实地址用:地址A,地址B表示

1
2
Address of num :地址A value :0
Address of pi :地址B value :地址A

用间接引用操作符解引指针

(*)一般称为解引指针,我们可以把解引操作符的结果用作左值,术语左值是指赋值操作符左边的操作数所以有左值都必须可以修改,因为它会被赋值

总结

  1. 指针的值是指针指向的地址
  2. &pi,是指针的地址
  3. *pi,是指针指向变量的内容,俗称解引

PHP源码解析_mac源码编译PHP7.1 VSCode 断点调试配置

不管出于什么目的,做PHP的想往精深走是绕不开读读源码这条路的。这篇文字记录了本地源码编译PHP7.1到配置VSCode的debug调试的过程。现在2018年11月25日01:54:13,刚快写好的笔记因为一个错误操作丢失掉了,真的很MMP,以后要加个定时记录的功能了,哎。

编译安装PHP

mac 下编译PHP7会遇见几个问题点,autoconf未安装,bison版本问题。

获取

在PHP官网上直接下载下载包或者,去GitHub选择你中意的分支clone,这边我们选择php7.1的最后一个版本php7.1.25
解压压缩包,cd进去

生成configure安装文件

1
./buildconf

autoconf未安装

1
2
3
buildconf: autoconf not found
You need autoconf version 2.59 or newer installed
to build php from Git

如果出现这个报错说明缺少autoconf包管理工具

我们使用brew简单暴力

1
brew install autoconf

autoconf安装成功

再次执行

1
./buildconf

成功生成configure文件

成功生成configure文件

编译安装

后续计划研究PHP标准库中的源码实现,为了节省时间所以关掉了不必要的扩展。

参数 作用
disable-all 关闭所有扩展
enable-debug 打开编译debug
prefix 安装目录
1
2
./configure --disable-all --enable-debug --prefix=/Users/zhangjunjie/Desktop/php_c

MAC默认安装2.3版本bison不出意外会报如下错误

bison版本错误

1
2
3
configure: WARNING: You will need re2c 0 13.4 or later if you want to regenerate PHP parsers

configure: error: bison is required to build PHP/Zend when building a GIT checkout!

bison安装及版本切换

如果未出现上环节的错误可以跳跃这部分

安装bison:brew install bison
切换bison:brew switch bison

安装完毕后还需要 bison -V检测下bison是否升级切换成功,如果还是2.3的版本那么我们只有开大招,配置环境变量了

1
2
echo 'export PATH="/usr/local/Cellar/bison/3.2.1/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc

然后我们再 bison -V确认下版本

bison版本

继续编译安装

重复执行编译命令

1
2
./configure --disable-all --enable-debug --prefix=/Users/zhangjunjie/Desktop/php_c
make install

如果安装成功/Users/zhangjunjie/Desktop/php_c/bin/php目录下会有PHP可执行文件,我们来验证下

1
/Users/zhangjunjie/Desktop/php_c/bin/php -v

PHP安装成功

好的大吉大利晚上吃鸡

#VSCode

下载

VS官网

下载安装这一环节就不赘述了VScode自带gdb

配置

  1. 打开我们下载的PHP源码

2.选择 调试>添加配置>Launch
添加配置

Launch

3.配置项目
配置项目

1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "(lldb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "/Users/zhangjunjie/Desktop/php_c/bin/php",//php执行文件路径
"args": ["/Users/zhangjunjie/Desktop/1.php"],//运行参数
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": true,
"MIMode": "lldb"
}

断点

创建一个array操作的PHP脚本

1
echo "<?php \n \$array = [1,2,3,4,2,1,2,3]; \n \$count = array_sum(\$array); \n print_r(\$array); \n print_r(\$count);" > /Users/zhangjunjie/Desktop/1.php

我们给 /Users/zhangjunjie/Desktop/php-src-PHP-7.1.25/ext/standard/array.c 5138 行 array_sum打上断点

断点

运行

运行断点

现在已经定位到了我们打的断点上,可以F10一行一行的去追代码如何执行的吧~

近2个月计划

更新 2018年11月27日01:05:57

有点飘,PHP7内核解析差点基础知识

重新整理书单

书目 优先级 预计时间
C Primer Plus 1 12月20号前
C语言指针 1 12月20号前月内
C计算法 1 1月22日前

近期有点迷茫,周末2天过得都是恍恍惚惚。只有采用这种任务似的方法给自己定目标然后充实下下班时间。

书单

番茄工作法,45分钟休息10分钟。

工作日下班时间 45x2 1.5小时
周末 45X6 4.5小时

书目 优先级 预计时间
方与圆 1 12月5日前
PHP7内核解析 1 12月5日前
C语言指针 2 19年1月前
C计算法 2 19年1月前

目标

  • 快速识人能力,包括业务,稳定,技术
  • php7内核有一定认知
  • C基础积累

目的

  • 能看懂PHP源码
  • 能编写PHP扩展

整理文档

近期招聘较多,以前面试方法耗时极大,人员入职内推为优,面试入职人员水平未达到期望预期。需提升自我面试能力,归纳总结。

elasticsearch中文文档精解三_基础_轻量查找

这篇文字讲下es最基本的查找分页等方法,这里要明白es为提供方便查找提供了2套查找方案,一是使用get请求带参数的形式轻量查找,一是把查找语句放入请求体中实现更多高级一点的特性

空查询

1
2
curl -X GET "localhost:9200/_search"

返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
{
"took": 5, #耗时单位微妙
"timed_out": false,
"_shards": { #分片情况
"total": 13,
"successful": 13,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 20105,
"max_score": 1,
"hits": [ #默认返回10条
{
"_index": "bt",
"_type": "text",
"_id": "4930340",
"_score": 1,
"_source": {
"name": "Volver (2006) BDRip-Avc UkrEsp.mkv",
"keywords": "Volver,(2006),BDRip-Avc",
"url": "981D242E410C5B380FC7EC5B67FC3125ECDC268F"
}
},
{
"_index": "bt",
"_type": "text",
"_id": "4930342",
"_score": 1,
"_source": {
"name": "Song of the Sea 2014 1080p BRRip x264 AAC-KiNGDOM",
"keywords": "1080p,BRRip,AAC-KiNGDOM",
"url": "813AB10C05E374DDDE5986710D6BA6F35CAD1E8C"
}
},
{
"_index": "bt",
"_type": "text",
"_id": "4930343",
"_score": 1,
"_source": {
"name": "Sing - Quem Canta Seus Males Espanta 2017 (720p) DUBLADO",
"keywords": "Canta,Males,Espanta,(720p),DUBLADO",
"url": "14C5E021537A92CC026A9CD49D237691E0C1B2E3"
}
},
{
"_index": "bt",
"_type": "text",
"_id": "4930344",
"_score": 1,
"_source": {
"name": "Idealnyi_muzhchyna.2015.BDRip.720p_Sborka_grab777.mkv",
"keywords": "",
"url": "E11FFF8B0E53E9F041FA0F7D59BC0D0E2B056FCD"
}
},
{
"_index": "bt",
"_type": "text",
"_id": "4930350",
"_score": 1,
"_source": {
"name": "Raziskovalka.Dora.V.Čudežni.Deželi.2014.ENG.DVDRip.x264",
"keywords": "",
"url": "422141BBB8048A3844D8D5510C7F25E08CE65A6D"
}
},
{
"_index": "bt",
"_type": "text",
"_id": "4930356",
"_score": 1,
"_source": {
"name": "Ранго",
"keywords": "Ранго",
"url": "949B4F8C8C4771BF96850403ED6B903FF22E86BC"
}
},
{
"_index": "bt",
"_type": "text",
"_id": "4930357",
"_score": 1,
"_source": {
"name": "A.trois.on.y.va_2015.WEB-DLRip.avi",
"keywords": "",
"url": "022E86258986B31A50DF710F33F2FCA6CA4B3DAB"
}
},
{
"_index": "bt",
"_type": "text",
"_id": "4930362",
"_score": 1,
"_source": {
"name": "Suzumiya_Haruhi_no_Shoushitsu_1080p_Hi10p_Ukr_UAMAX.mkv",
"keywords": "",
"url": "B41C345EDD0845EBEEB0B22FACAFEF6EBECEF13D"
}
},
{
"_index": "bt",
"_type": "text",
"_id": "4930363",
"_score": 1,
"_source": {
"name": "X-Men - Days of Future Past [BDRip-720p] (www.kinokopilka.tv)",
"keywords": "X-Men,Future,[BDRip-720p]",
"url": "C031DA00FE0C3A77C66CAE6BDC88DBDED843984D"
}
},
{
"_index": "bt",
"_type": "text",
"_id": "4930364",
"_score": 1,
"_source": {
"name": "Hatufim.S02.720p.WEBRip.AAC2.0.H.264-HRiP",
"keywords": "",
"url": "327C175612A471708D76C0BC609ADC049F2FB40A"
}
}
]
}
}

可选参数

一 . timenout

GET /_search?timeout=18ms

注:timenout并不会在过期后终止查询,查询在内部还在进行,只是es会返回收集到当前时间节点的数据

二 . 查询区间

参数 介绍
/_search 在所有的索引中搜索所有的类型
/gb/_search 在 gb 索引中搜索所有的类型
/gb,us/_search 在 gb 和 us 索引中搜索所有的文档
/g*,u*/_search 在任何以 g 或者 u 开头的索引中搜索所有的类型
/gb/user/_search 在 gb 索引中搜索 user 类型
/gb,us/user,tweet/_search 在 gb 和 us 索引中搜索 user 和 tweet 类型
/_all/user,tweet/_search 在所有的索引中搜索 user 和 tweet 类型

分页

1
curl -X GET "localhost:9200/_search?size=5"
GET /_search ?size=5 &from=10
请求方式 搜索参数 每页条数 第几页

深度分页

一个比较有意思的概念,假如我们有5个主分片。需要查询前10条数据详情,ES内部会在5个分片上分别查询前10个然后对比提取出前10个。也就是5*10-10的浪费率。任何web查询搜索引擎都要超过1000个性能损耗极大。

轻量搜索

有两种查询方法,一种是轻量级通过get参数的形式查询,一种是放在结构体中的查询。

查询bt索引的type等于text,name包含es的数据

1
2
curl -X GET "localhost:9200/bt/text/_search?q=name:es

查询 name包含test并且keywords包含Hoepli的数据

1
2
3
4
表达式为 name:test +keywords:Hoepli
UrlEncode 为 name%3atest+%2bkeywords%3aHoepli

curl -X GET "localhost:9200/bt/text/_search?q=name%3atest+%2bkeywords%3aHoepli

不指定字段查找

es 内部会把所有字段拼接成一段话提供查找

1
2
curl -X GET "localhost:9200/_search?q=mary"

加深下难度

  1. name 字段中包含 mary 或者 john
  2. date 值大于 2014-09-10
  3. _all 字段包含 aggregations 或者 geo

get请求都需要url编码

1
2
+name:(mary john) +date:>2014-09-10 +(aggregations geo)

elasticsearch中文文档精解二_基础_增删查改批量操作

取回一个文档

restful api 分为 post delete get put ,对应了增删查改。这边整理了下es中基础的restful api增删查改的操作。这个是es比较简单也是比较重要的环节。我个人习惯在postman中操作,但是也给出了CURL的操作dome

1
2
3
4
5
6
7
8
9
10
11
12
13
14
curl -X GET "localhost:9200/phpzjj/article/123?pretty"

{
"_index": "phpzjj",
"_type": "article",
"_id": "1",
"_version": 3,
"found": true,
"_source": {
"title": "es文档写入",
"text": "演示下文档是怎么写入的",
"date": "2018/11/19"
}
}

解读

curl -X GET localhost:9200 /phpzjj /article /123 ?pretty
请求方式 域名:端口 _index _type _id pretty-print功能,美化json

参数列表

名称 作用
pretty 美化json
_source=title,text 返回一部分字段
_source 只返回source内容

postman列子

取回一个文档

文档更新

1
2
3
4
5
6
7
8
curl -X PUT "localhost:9200/phpzjj/article/1" -H 'Content-Type: application/json' -d'
{
"title": "My first blog entry",
"text": "I am starting to get the hang of this...",
"date": "2014/01/02"
}
'

返回

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"_index": "phpzjj",
"_type": "article",
"_id": "1",
"_version": 5, #版本号
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": false
}

解读

curl -X PUT “localhost:9200 /phpzjj /article /1” -H
请求方式 域名:端口 _index _type _id

更新一条数据的过程

  • 从旧文档构建 JSON
  • 更改该 JSON
  • 删除旧文档
  • 索引一个新文档

postman栗子

文档更新

创建文档保证不覆盖

  1. 使用post方式由es自动生成ID
  2. URL末端加上 _create,重复会抛出错误状态码409

postman栗子

创建文档保证不覆盖

删除文档

1
2
curl -X DELETE "localhost:9200/phpzjj/article/101"

返回 状态码200

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"found": true,
"_index": "phpzjj",
"_type": "article",
"_id": "101",
"_version": 2,
"result": "deleted",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}

如果文档不存在返回状态码 404

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"found": false,
"_index": "phpzjj",
"_type": "article",
"_id": "101",
"_version": 1,
"result": "not_found",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}

文档部分更新

_update语句会跟新部分字段,虽然如此但是它在内部实现的时候依旧是删除原有文档再重新建立的过程

1
2
3
4
5
6
7
8
9
curl -X POST "localhost:9200/phpzjj/article/2/_update" -H 'Content-Type: application/json' -d'
{
"doc" : {
"tags" : [ "testing" ],
"views": 0
}
}
'

常用参数

一 .文档中的_source在内部脚本中称为ctx._source

ctx._source.tags+=new_tag

二 .失败后执行次数
retry_on_conflict=5

1
2
3
4
5
6
7
8
9
curl -X POST "localhost:9200/website/pageviews/1/_update?retry_on_conflict=5" -H 'Content-Type: application/json' -d'
{
"script" : "ctx._source.views+=1",
"upsert": {
"views": 0
}
}
'

合并请求,批量取回文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
curl -X GET "localhost:9200/_mget" -H 'Content-Type: application/json' -d'
{
"docs" : [
{
"_index" : "phpzjj",
"_type" : "article",
"_id" : 2
},
{
"_index" : "phpzjj",
"_type" : "article",
"_id" : 1,
"_source": "title"
}
]
}
'

postman无法get内带body curl跑一波

_mget

批量操作(需要使用时对照文档完形填空)

1
2
3
4
5
6
7
8
9
curl -X POST "localhost:9200/_bulk" -H 'Content-Type: application/json' -d'
{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "create": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title": "My first blog post" }
{ "index": { "_index": "website", "_type": "blog" }}
{ "title": "My second blog post" }
{ "update": { "_index": "website", "_type": "blog", "_id": "123", "_retry_on_conflict" : 3} }
{ "doc" : {"title" : "My updated blog post"} }
'

elasticsearch中文文档精解一_基础_索引与文档,es类比mysql的记忆方法

前记

这一系列elasticsearch都是基于官方文档的一些联系和总结,在博客里我把它放在PHP分类,因为这种服务小工具应该也属于PHP周边吧

介绍

因为英文不是很好我比较喜欢叫elasticsearch为es,它的使用领域可以说相当广泛了,小到我这种博客的搜索大到分布式数据计算。现在这个项目准备用它实现一套颜色相关的查询功能,它是Java写的ELK分析系统中的一环,并且它有一套RESTFUL API的对外接口。


集群健康

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
curl -X GET "localhost:9200/_cluster/health"

返回

{
"cluster_name": "elasticsearch",
"status": "yellow",
"timed_out": false,
"number_of_nodes": 1,
"number_of_data_nodes": 1,
"active_primary_shards": 10,
"active_shards": 10,
"relocating_shards": 0,
"initializing_shards": 0,
"unassigned_shards": 10,
"delayed_unassigned_shards": 0,
"number_of_pending_tasks": 0,
"number_of_in_flight_fetch": 0,
"task_max_waiting_in_queue_millis": 0,
"active_shards_percent_as_number": 50
}

这里推荐使用一个小工具postman

最重要的是status,它有如下3种颜色

颜色 状态
green 所有主副分片都运行正常
yellow 所有主分片正常,但不是所有副分片也正常
red 有主分片没有正常运行

索引

类比数据库es里的索引应该叫库,默认分配5个主分片。但是也可以手动配置

1
2
3
4
5
6
7
curl -X PUT "localhost:9200/blogs" -H 'Content-Type: application/json' -d
'{
"settings" : {
"number_of_shards" : 3,
"number_of_replicas" : 1
}
}'

es索引建立

单机版索引

上图可以看见默认es分配了5个主分区与5个副本分区,健康状况为yellow,因为我们这里使用的是单机版的配置,不会创建副分区。因为同在一台服务器上主分区GG了就意味着数据的丢失

索引文档

类比数据库,文档相当于数据库中的数据。里面有三个必须的元数据元素,_index,_type,_id

名称 用途
_index 等同有mysql中的概念,把有共同特性的数据放到相同集合
_type 等同有mysql中的概念
_id 与_index和_type组成了文档里数据的唯一标识

索引自定义ID的文档

1
2
3
4
5
6
7
8
9
curl -X PUT "localhost:9200/phpzjj/article/1" -H 'Content-Type: application/json' -d'
{
"title": "es文档写入",
"text": "演示下文档是怎么写入的",
"date": "2018/11/19"
}
'


localhost: 9200 /phpzjj/ article/ 1
域名 端口 _index _type _id

索引自定义ID的文档

索引系统分配ID文档

和上面不同的是这里采用 POST ,在RESTFUL API里PUT表示更新POST表示创建,这里可以理解为自定义ID是先写入成功数据后更改了ID的值

1
2
3
4
5
6
7
8
curl -X POST "localhost:9200/phpzjj/article" -H 'Content-Type: application/json' -d'
{
"title": "es文档写入",
"text": "演示下文档是怎么写入的",
"date": "2018/11/19"
}
'

执行结果

文档写入情况1
文档写入情况2

《腾讯传》

这本书因为不属于什么工具书籍所以可以说是在上下班骑小电瓶车的时候听完的

腾讯传

它讲述了腾讯的发展史,怎么从一个小小的创业团队做成现在的大厂。对于我们这代人来说可以这是一份童年,满满的全是发生在自己身边的故事。

感触比较深的是在那个时候腾讯手握大把流量却为变现发愁,如今可以说是反转了一下,做企业的有大把的变现手段流量确是靠买,这可能就是互联网创业流量变现在高速发展中的成熟的标志之一吧。

移动梦网在那个年代是手机端的流量入口,pc端的流量入口即是浏览器,智能机高速发展的几年各大厂商抓住市场红利疯狂竞争手机端的流量入口保证自己的市场占有率,微信和手Q在这场混战中能做到现在的装机必备讲真是一件相当了不起的事。比起3Q大战,这本书少了腾讯的QQ浏览器和360浏览器抢占PC端入口的战争史。

这本书是一本创业励志书籍,现在反过来看来腾讯的成功可能没那么困难,虽然吴晓波把腾讯面临的困境都描述得有惊无险,但我明白那是一个一步走错万劫不复的年代。我们现在看见的互联网都是这群人不断的摸索后的产物,当时大佬们都是盲人摸象,现在我们都是看着大象锦上添花。每个时代有每个时代的机会,我们需要的是学习书中解决问题的思维能力。

C语言自学系列五_函数指针的定义与应用

什么是函数指针

函数指针如字面表达一样,是指的指向函数的指针。它的用处可以说很广了,在我认知和学习到的有2种用法,一是直接调用 如 function(a[i]) 其实是 (*function)(a[i]),的缩写。第二种是 function(function2)类似于php中的依赖注入(本质差别很大,只是举个例子)。作为phper的我了解到这的时候可以说是耳目一新。。。感觉自己有点low

声明一个函数指针

1
char** (*function_name)(char*,int)
返回类型 指针变量 参数类型
char** (*function_name) (char*,int)

#使用

函数指针就这点东西,怎么说呢。使用出真像吧。在下面的演示中比较重要的几个知识点

  • 定义一个函数指针
  • 使用一个函数指针
  • 函数中 void指针(void*)的应用
  • scanf

使用一个 void指针

void* scores_a

int a = * (int*) scores_a;
定义数据类型 解引用 强制转换为数组指针 形参
注:函数指针数组后面另外写时间应该不够,明天新开文章单独举例。

题目一

创建一个list有各个女生的爱好,身材,年龄。我们根据男生的择偶标准匹配出最满足条件的女生

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**
* 一个人物结构体
*/
typedef struct girls
{
char *name;
char *like;
unsigned int old;
unsigned int height;
struct girls *next;
} girls;

/**
* 链表
* @param name 姓名
* @param like 爱好
* @param old 年龄
* @param height 身高
* @return
*/
girls* create(char *name, char *like, int old, int height)
{

girls *i = malloc(sizeof(girls));
i->name = strdup(name);
i->like = strdup(like);
i->old = old;
i->height = height;
i->next = NULL;
printf("【成功】录入%s个人信息\n",i->name);
return i;
}



/**
* 录入人员信息
* @return
*/
girls* entry()
{

char name[10],like[10];
int c,old,height,exit;

girls *start = NULL;//起始
girls *next = NULL;//下一个节点
girls *i = NULL;//上一个节点

printf("请按照格式录入人员信息: 姓名 爱好 年龄 身高 \n");
printf("--------------------------------- \n");

while (1){

i = next;

scanf("%s%s%d%d",name,like,&old,&height);


next = create(name,like,old,height);



printf("继续录入请输入【1】停止请出入【2】 \n");
scanf("%d",&exit);

if(start == NULL){
start = next;
}

if(i != NULL){
i->next = next;
}


if(exit == 2){
printf("录入完成 \n");
break;
}

}

return start;

}

/**
* 喜欢读书的小姐姐
* @param like
* @return
*/
int like_book(char *like)
{

if(strstr(like,"读书")){
printf("66666");
return 1;
}else{
return 0;
}
}

/**
* 递归链表
* @param start
*/
void display(girls *start, int (*match)(char*)){

girls *i = start;

for (;i!= NULL; i=i->next) {

if(match(i->like) == 1){
printf("姓名: %s 爱好:%s 年龄:%d she%d \n",i->name,i->like,i->old,i->height);
}

}

return;

}

int main()
{

girls *list = entry();
display(list,like_book);

return 0;
}

题目二

使用C标准函数库qsort实现数组的升序降序,长方形的面积排序

先介绍下 qsort

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <stdio.h>
#import <stdlib.h>

/**
* 降序
* @param scores_a
* @param scores_b
* @return
*/
int desc(const void* scores_a, const void* scores_b)
{
int a = *(int*)scores_a;//强转为整数指针
int b = *(int*)scores_b;
return b-a;
}

/**
* 升序
* @param scores_a
* @param scores_b
* @return
*/
int asc(const void* scores_a, const void* scores_b)
{
int a = *(int*)scores_a;//强转为整数指针
int b = *(int*)scores_b;
return a-b;
}

int main()
{
int scores[] = {10,2,4,5,6,7,8,9,0};
int cnt = sizeof(scores)/sizeof(scores[0]);

qsort(scores, cnt, sizeof(scores[0]), desc);

for (int i = 0; i < cnt; ++i) {
printf("%d \n",scores[i]);
}

printf("----------------------\n");
qsort(scores, cnt, sizeof(scores[0]), asc);

for (int i = 0; i < cnt; ++i) {
printf("%d \n",scores[i]);
}

return 0;
}

C语言自学系列四_链表与动态链表

#链表

什么是链表

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。

存储结构

链表中会有个指向下一节点的指针next

链表示例图

定义使用一个链表

首先这里创建一个小姐姐的结构体,包含姓名开始营业时间和结束营业时间。

1
2
3
4
5
6
7
8

typedef struct chick{
char *name;
char *opens;
char *closes;
struct chick *next;
} chick;

那么我们来定义几个小姐姐吧

1
2
3
4
5
6
7
chick xiaohong = {"小红红","23:00","01:00",NULL};
chick xiaohuang = {"小晃晃","23:00","01:00",NULL};
chick xiaolan = {"小蓝蓝","23:00","01:00",NULL};

xiaohong.next = &xiaohuang;//设置下一个节点
xiaohuang.next = &xiaolan;

展示出有过深度交流的小姐姐姓名

1
2
3
4
5
6
7

chick *i = start;//递归这个链表

for (;i!= NULL;i=i->next) {
printf("%s\n",i->name);
}

来一串完整版的输出打印

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**
* 定义一个小姐姐的结构体
*/
typedef struct chick{
char *name;
char *opens;
char *closes;
struct chick *next;
} chick;

/**
* 递归打印一个链表
* @param start
*/
void display(chick *start)
{
chick *i = start;//

for (;i!= NULL;i=i->next) {
printf("%s\n",i->name);
}

}


int main()
{
chick xiaohong = {"小红红","23:00","01:00",NULL};
chick xiaohuang = {"小晃晃","23:00","01:00",NULL};
chick xiaolan = {"小蓝蓝","23:00","01:00",NULL};

xiaohong.next = &xiaohuang;//设置下一个节点
xiaohuang.next = &xiaolan;

display(&xiaohong);

return 0;
}

结果

输出结果1

可以看到我们通过结构体声明了3个小姐姐的变量,并且用指针把她们关联了起来,然后用一个流程控制语句for打印出了这个链表里的信息

动态链表

在一些批量化的赋值中,并且想把数据存放到堆上,这样我们可以考虑下使用动态链表的形式。

创建一个小姐姐生成器,在这个生成器里面我们用malloc动态的分配了一个放chick变量的堆空间

1
2
3
4
5
chick *i = malloc(sizeof(chick));
i->name = name;
i->opens = "23:00";
i->closes = "01:00";
i->next = NULL;

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**
* 定义一个小姐姐的结构体
*/
typedef struct chick{
char *name;
char *opens;
char *closes;
struct chick *next;
} chick;

/**
* 递归打印一个链表
* @param start
*/
void display(chick *start)
{
chick *i = start;//

for (;i!= NULL;i=i->next) {
printf("%s\n",i->name);
}

}

chick* create(char *name)//返回一个chick类型指针
{

chick *i = malloc(sizeof(chick));
i->name = name;
i->opens = "23:00";
i->closes = "01:00";
i->next = NULL;
return i;
}

int main()
{
chick *xiaohong = create("小红红");//声明结构体指针
chick *xiaohuang = create("小晃晃");
chick *xiaolan = create("小蓝蓝");

xiaohong->next = xiaohuang;//设置下一个节点,这和前面略微不一样。放指针地址
xiaohuang->next = xiaolan;

display(xiaohong);

return 0;
}

用户手动输入创建链表结构

上面那个列子是人肉手工定义的链表的逻辑关系,我们可以让程序智能点,让用户自己输入小姐姐名单,并用链表的方式保存下来。这里strdup是一个新的知识点, 将串拷贝到新建的位置处用了记得free掉

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**
* 定义一个小姐姐的结构体
*/
typedef struct chick{
char *name;
char *opens;
char *closes;
struct chick *next;
} chick;

/**
* 递归打印一个链表
* @param start
*/
void display(chick *start)
{
chick *i = start;//

for (;i!= NULL;i=i->next) {
printf("%s\n",i->name);
}

}

chick* create(char *name)//返回一个chick类型指针
{

chick *i = malloc(sizeof(chick));
i->name = strdup(name);
i->opens = "23:00";
i->closes = "01:00";
i->next = NULL;
return i;
}

int main()
{
chick *i = NULL;//链表当前位置 注:这里定义的变量全是反自定义结构体的所以要chick声明
chick *next = NULL;//下一个节点
chick *start = NULL;//链表头

char name[80];

for (; fgets(name,80,stdin) != NULL; i = next) {//i保留上一个节点

next = create(name);//创建下一链

if(start == NULL){//找到链表头位置
start = next;
}

if(i != NULL){
i->next = next;//下一个节点赋值
}

}
display(start);

return 0;
}

运行结果

ctrl+d中断输入,可以看见正常输出,大吉大利~

运行结果

释放储存器

和打印方法一样,递归这个链表结构。需要注意的是必须先释放掉之前我们strdup声明的堆空间,否则后面就会再也无法释放掉它了。

1
2
3
4
5
6
7
8
9
10
11
12
void clear(chick *start)
{
chick *i = NULL;

i = start;
for (; i != NULL ; i = i->next) {

free(i->name);//用strdup新开堆空间,需要先释放掉否则就再也释放不了了
free(i);

}
}

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**
* 定义一个小姐姐的结构体
*/
typedef struct chick{
char *name;
char *opens;
char *closes;
struct chick *next;
} chick;

/**
* 递归打印一个链表
* @param start
*/
void display(chick *start)
{
chick *i = start;//

for (;i!= NULL;i=i->next) {
printf("%s\n",i->name);
}

}

chick* create(char *name)//返回一个chick类型指针
{

chick *i = malloc(sizeof(chick));
i->name = strdup(name);
i->opens = "23:00";
i->closes = "01:00";
i->next = NULL;
return i;
}

/**
* 释放堆空间
* @param start
*/
void clear(chick *start)
{
chick *i = NULL;

i = start;
for (; i != NULL ; i = i->next) {

free(i->name);//用strdup新开堆空间,需要先释放掉否则就再也释放不了了
free(i);

}
}

int main()
{
chick *i = NULL;//链表当前位置 注:这里定义的变量全是反自定义结构体的所以要chick声明
chick *next = NULL;//下一个节点
chick *start = NULL;//链表头

char name[80];

for (; fgets(name,80,stdin) != NULL; i = next) {//i保留上一个节点

next = create(name);//创建下一链

if(start == NULL){//找到链表头位置
start = next;
}

if(i != NULL){
i->next = next;//下一个节点赋值
}

}
display(start);
clear(start);
return 0;
}

请我喝杯咖啡吧~