文章目录
strlen函数与sizeof的区别
- 表面上都可求字符串的长度,
- 但二者却存在着许多不同之处及本质区别。
- strlen 是一个函数,计算指定字符串str长度,不包括结束字符
size_t strlen(char const* str);
- 所以需要进行一次函数调用,调用示例如下面的代码所示:
char sArr[] = "ILOVEC";
/*用strlen()求长度*/
printf("sArr的长度=%d\n", strlen(sArr));
- 运行结果为 6(因为不包括结束字符 null)。
- 注意的是,函数 strlen 返回的是一个类型为 size_t 的值,从而有可能让程序导致意想不到的结果,如下面的示例代码所示:
/*判断一*/
if(strlen(x)>= strlen(y))
{
}
/*判断二*/
if(strlen(x)- strlen(y)>= 0)
{
}
- 判断表达式一没什么问题,程序也能够完全按照预想的那样工作;但判断表达式二的结果就不一样了,它将永远是真,为什么?
- 因为函数 strlen 的返回结果是 size_t (即无符号整型),size_t 类型绝不可能是负的。因此,语句“if(strlen(x)-strlen(y)>=0)”将永远为真。
- 就算表达式中同时包含了有符号整数和无符号整数,还是有可能产生意想不到的结果,如下面的代码:
/*判断一*/
if(strlen(x)>= 5)
{
}
/*判断二*/
if(strlen(x)- 5>=0)
{
}
- 显然,判断表达式二的结果还是永远是真,其原因与上面相同。
- sizeof 是一个单目运算符,不是一个函数。
- 参数可以是数组、指针、类型、对象、函数等
char sArr[] = "ILOVEC";
/*用sizeof求长度*/
printf("sArr的长度=%d\n", sizeof(sArr));
- 运行结果为 7(因为它包括结束字符 null)。
- 对 sizeof 而言,因为缓冲区已经用已知字符串进行初始化,其长度固定,所以sizeof 在编译时计算缓冲区的长度。
- 由于在编译时计算,sizeof 不能用来返回动态分配的内存空间的大小。
参考链接
- http://c.biancheng.net/view/342.html
C语言中sizeof和strlen的区别与联系
- strlen:统计字符串中字符的个数
- sizeof:统计对象所占的单元(字节)的个数,
- 一般以8位二进制作为一个存储单元,
- 所以字节数一般等于存储单元的个数。
- 返回值
- 整数
- 整数
- 参数
- 类型、数组、指针‘函数
- 数组
- 是否包含“\0”
- 包含
- 不包含(以“\0结束”)
- 本质
- 运算符
- 函数
- 计算时间
- 编译
- 运行
- 一般用途
- 统计存储单元个数
- 统计字符串中字符的个数,包括空格
- sizeof用法注意:
- 用于测定类型所占存储单元时,类型必须用sizeof(类型)
- 用于数组时,表示数组所占的存储空间的大小,可以不用(),
- sizeof(name)=sizeof name,name为数组
#include <stdio.h>
#include <string.h>
#define PRAISE "What a super marvelous name!"
int main(void)
{
char name[40];
printf("What's your name?\n");
scanf("%s",name);
printf("Hello,%s.%s\n",name,PRAISE);
printf("Your name of %d letters occupies %d memory cells.\n",strlen(name),sizeof(name));
printf("The phrase of praise has %d letters",strlen(PRAISE));
printf("and occupies %d cells.\n",sizeof(PRAISE));
return 0;
}
int sum(int ar[], int n)
{
int i;
int total = 0;
for(i=0;i<n;i++)
{
total += ar[i];
}
printf("The size of ar is %zd bytes.\n",sizeof ar);
return total;
}
- sizeof后面的对象如果是实参数组名,
- 结果为该数组的存储空间,
- 但sizeof如果为一个指向数组首元素的指针,
- 则对于4字节地址的计算机系统,指针的大小为4字节
- 如以上的sum函数,ar是一个指向数组的首元素的指针,
- 所以该函数输出的结果为4。
悲观锁与乐观锁
- 悲观锁
- 每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。
- 关系型数据库里边就用到了很多这种锁机制,
- 如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
- 它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。
- 悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
- 乐观锁
- 每次去拿数据的时候都认为别人不会修改,所以不会上锁,但在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。
- 适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
- 两种锁各有优缺点,
- 乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。
- 但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。
参考链接
- https://blog.csdn.net/coderDogg/article/details/85093741
最通俗易懂的乐观锁与悲观锁原理及实现
- 乐观锁 总认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据修改,因此不上锁,
- 但在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现。
- version方式:一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。
- 当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。
update table set x=x+1, version=version+1 where id=#{id} and version=#{version};
- CAS操作方式:即compare and swap 或者 compare and set,涉及到三个操作数,数据所在的内存值,预期值,新值。
- 当需要更新时,判断当前内存值与之前取到的值是否相等,若相等,则用新值更新,若失败则重试,一般情况下是一个自旋操作,即不断的重试。
- 悲观锁
- 每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等),
- 当其他线程想要访问数据时,都需要阻塞挂起。
- 可以依靠数据库实现,如行锁、读锁和写锁等,都是在操作之前加锁,
- Java中,synchronized的思想也是悲观锁。
参考链接
- https://blog.csdn.net/L_BestCoder/article/details/79298417
B树
- 从算法逻辑上来讲,二叉查找树的查找速度和比较次数都是最小的。
- 但我们不得不考虑一个现实问题磁盘IO
- 数据库索引是存储在磁盘上的,当数据量较大的时候,
- 索引的大小可能有几个G
- 当利用索引查询的时候,能把整个索引全部加载到内存吗?
- 不可能。
- 只有逐一加载每一个磁盘页,这里的磁盘页对应着索引树的节点。
- 如果用二又查找树作为索引结构
- 设树高4,查找的值是10,那么流程如下
-
磁盘I0的次数是4次,索引树的高度也是4。
-
最坏情况下,磁盘I0数等于树高
-
为了减少磁盘I0次数,需把原本“瘦高”的树结构变得“矮胖”。
-
这就是B-树的特征之ー。
-
B树是一种多路平衡查找树,每一个节点最多包含k个孩子,k
被称为B树的阶。 -
k取决于磁盘页的大小。
- 3阶B-树为例,看看B树具体结构。
- 树中的具体元素和刚才的二叉查找树是一样的。
- 查5吧!
-
B树在查询中的比较次数其实不比二又查找树少,尤其当单一节点中的元素数量很多时。
-
可是相比磁盘I0的速度,内存中的比较耗时几乎可以略。
-
所以只要树的高度足够低,I0次数足够少,就可
以提升查找性能。 -
相比之下节点内部元素多一些也没有关系,仅仅是多了几内存交互,只
要不超过磁盘页的大小即可。 -
这就是B-树的优势之ー。
这儿还有点没写
- B-树主要应用于文件系统及部分数据库索引,如著名的非关系
型数据库 Mongodb。 - 大部分关系型数据库,如Mysq1用B+树作为索引。
参考链接
B+树
- B+树是B-树的一种変体,
- 有着比B-树更高的查询性能。
- 每个父节点的元素都出现在子节点中,是子节点的最大(或最小)
元素。
- 注意,根节点的最大元素(15),也就等同于整个B+树的最大元素。
- 以后无论插入删除多少元素,始终要保持最大
元素在根节点当中。
- 至于叶子节点,由于父节点的元素都出现在子节点,因此所有叶子节点包含了全量元素信息。
- 每一个叶子节点都带有指向下ー个节点的指针,形成了一个
有序链表。
- 卫星数据,指的是索引元素所指向的数据记录,比如数据库中的某一行。
- B树中,无论中间节点还是叶子节点都带有卫星数据。
- B+树当中,只有叶子节点带卫星数据,其余中间节点仅仅是索
引,没有任何数据关联。
- 数据库的聚集索引中,叶子节点直接包含卫星数据。
- 非聚集索引中,叶子节点带有指向卫星数据的指针。
- B+树的好处主要体现在查询性能上。
- 通过单行查询和范围查询来分析。
- 单元素查询的时候,B+树会自顶向下逐层查找节点,最终找到匹配
的叶子节点。 - 比如要查找的是元素3
-
两点不同。
-
首先,B树的中间节点没有卫星数据,所以同样大小的
磁盘页可以容纳更多的节点元素。 -
意味着,数据量相同的情况下,B+树比B树更“矮胖”
因此查询时10次数也更少。
- B+树的查询必须最终查找到叶子,B树只要找到匹配元素
,无论匹配元素处于中间节点还
是叶子节点。 - B树查找性能并不稳定(最
好情况是只查根节点,最坏情况是查
到叶子节点)。而B树的每一次查找
都是稳定的。