2020-4-11ali晚面

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树的每一次查找
    都是稳定的。

参考链接

添加链接描述

发布了558 篇原创文章 · 获赞 295 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/zhoutianzi12/article/details/105461250