C语言
C提供三种预处理方式:宏定义#define,文件包含#include,条件编译#ifdef
所有的预处理指令都必须以#开头
#define Init (int*)
Init a,b; a为int*,b为int。 宏定义只是文本替换,而不是让Init 是int这个类型
typedef (int) Init
Init a,b;
基本语句
逻辑 && (重要)
A && B A B同时为真 整个表达式结果为真。 A B只要有一个为假 整个表达式为假
A && B 如果A为假 系统不会执行B 这就是&&的短路特性
逻辑||
A || B 只要A B任意一个为真 整个表达式结果为真,A B同时为假的时候 结果才为假。
逻辑|| 也有短路特性:只要A为真 编译器不会判断B的真假。
^:按位异或
语法:相同为0 不同为1
1010 1010
^ 0000 1111
1010 0101
特点:和0异或 保持不变 和1异或 取反。
应用场景:将固定的为 发生高低电频 翻转。
右移运算符:
1010 1100 >> 2
右移分类:逻辑右移 算术右移
逻辑右移:右边丢弃 左边补0.
算术右移:
无符号数:右边丢弃 左边补0。
有符号数:
正数:右边丢弃 左边补0。
负数:右边丢弃 左边补1。
【?:】
表达式1 ? 值1:值2
语法:如果表达式1 为真 整个表达式的值为“值1” 否则为“值2”
++i 或 --i 先加、减 后使用。
i++ 或 i-- 先 使用 后 加、减
注意:i++ 或 ++i 作为单独的指令 没有区别
总结:for while 我们如何选择呢?
如果循环的次数是确定的 建议选择for。
如果循环的次数不确定,知道退出的条件 建议选择while.
char str1[]={‘h’,‘e’,‘h’,‘e’};//逐个字符初始化 系统不会添加’\0’ char str2[]=“hehe”;//以字符串初始化 系统会给字符串添加’\0’
scanf和%s使用的时候 有个缺点 遇到 空格会结束输入
gets 缺点: 获取键盘输入的时候 不会管 buf的大小 容易造成 内存 污染
fgets函数 既可以获取 带空格的字符串 也可以保证 buf的不越界
fgets(buf, sizeof(buf), stdin);//推荐
排序算法
排序的算法有很多,对空间的要求及其时间效率也不尽相同。下面列出了一些常见的 排序算法。这里面 插入排序和 冒泡排序又被称作 简单排序,他们对空间的要求不高,但是时间效率却不稳定;而后面三种排序相对于简单排序对空间的要求稍高一点,但时间效率却能稳定在很高的水平。 基数排序是针对 关键字在一个较小范围内的排序算法。
插入排序
冒泡排序
选择排序
快速排序
堆排序
归并排序
基数排序
希尔排序
深拷贝与浅拷贝到底是什么
复制构造函数也叫拷贝构造函数;
浅复制也叫浅拷贝或位拷贝;
深复制也叫深拷贝;
浅拷贝和深拷贝
拷贝就是复制,创建副本。假设有对象A,A有属性t1,t2。那么,我通过拷贝A,得到B,B应该也有属性t1,t2,且A、B两个对象的每个属性,都应该是相同的。对于基本类型的属性t1,拷贝是没有疑义的。简单将值复制一份,就达到了拷贝的效果。而对于引用类型的属性t2来说,拷贝就有了两层含义。第一层是,我只是将t2引用的地址复制一份给B的t2,确实达到了属性相同的效果,可以理解为实现了拷贝,但是事实上,两个对象中的属性t2对应的是同一个对象。在B对象上对t2所指向的对象进行操作,就会影响到A对象中的t2的值。第二层是,我将A的t2所指向的对象,假设为o1,完整复制一份,假设为o2,将新的o2的地址给B的t2。也达到了复制的效果,且对B的t2所指向的o2进行操作,不会影响到A的t2所指向的o1。拷贝的两层含义,对应了浅拷贝和深拷贝的概念,做了第一层,就是浅拷贝,做到第二层,就是深拷贝。
基于以上内容,很容易可以想到,浅拷贝比深拷贝要更快,但是,从拷贝的意义上来看,浅拷贝相较于深拷贝,要欠缺一点。
浅复制仅复制对象本身(其中包括是指针的成员),这样不同被复制对象的成员中的对应非空指针会指向同一对象,被成员指针引用的对象成为共享的,无法直接通过指针成员安全地删除(因为若直接删除,另外对象中的指针就会无效,形成所谓的野指针,而访问无效指针是危险的;除非这些指针有引用计数或者其它手段确保被指对象的所有权);而深复制在浅复制的基础上,连同指针指向的对象也一起复制,代价比较高,但是相对容易管理。
指针函数作为参数
如果想在函数内部修改外部一维数组的值,需要作为形参传出去。
指针类型也可以作为函数参数的类型,这时视为把变量的地址传入函数。如果在函数中对这个地址中的元素进行改变,原先的数据也就会确实地被改变。
在代码中,是将int型的指针变量p赋值为a的地址,然后通过change函数将指针变量p作为参数传入函数。此时传入的其实是a的地址。在change函数中,使用p修改地址中存在的数据,也就是改变了a本身,所以最后输出a时其实已经改变了值。这种传递方式被叫做地址传递。这里需要区别一般的参数传递,一般形参与实参的传递,在main函数外部的函数改变,并不改变实际值,叫做值传递。需要区别两种参数的传递,本质上是看是否对传入参数指定地址的内容作出改变,值传递时在外部函数实际上是新建了一个变量将实参的值赋值给新地址的变量,因此不改变主函数传入变量的值,而地址传递是所有参数都在同一个地址上的变量做修改。因此会改变传入参数的值。
指针变量存放的是地址,使用指针变量作为参数时传进来的也是地址。只有在获取地址的情况下对元素进行操作,才能够真正的修改变量。
//负数 以补码 存储
//正数 以原码 存储
//十六进制 以 原码存储
//八进制 以 原码存储
//每3位二进制 代表 一位八进制
//如果数据越界 以原码 存储
//取 %x %u %o 都是输出内存的原样数据
//每4位二进制 代表 一位十六进制(记住)
1、void 不能定义变量
void num;//错误的 系统不知道 num的大小
2、void *可以定义变量
void *p;//p的类型为void , 而void 指针类型,32为平台4字节,系统知道给p开辟4字节
3、不要对 没有初始化的 指针变量 取
4、不要对 初始化为NULL的指针变量 取
5、不要给 指针变量 赋普通的数值。
6、指针变量不要操作越界的空间。
一、栈区
栈区之中的数据在栈区之中以栈的形式进行存储。
栈区的特点:数据是先进后出,放在栈区里面存放的是局部变量。(例如定义在函数内部的变量)
栈区之中的数据(局部变量)的作用范围过了之后,系统就会回收自动管理栈区的内存(分配内存,回收内存),不需要开发人员来手动管理。
二、堆区
堆区可以高效的使用内存,但是如果数据不用时需要及时释放,否则可能会造成内存泄漏。(内存一直被占用 得不到释放)
这里的内存可以由程序员自己手动管理 高效的使用内存 。例如: 申请内存、释放内存、优化内存 ARC。
一般常用的申请内存的函数有 malloc() 、realloc()、 calloc()这三个函数。释放内存的函数有free()。(在C++编译器中还有new关键字可用于申请内存空间,delete关键字用于释放内存)
三、静态区
静态区存放的是: 全局变量(定义在函数外部的变量,例如在主函数上面 定义的变量) 和静态变量。
static 类型的变量的特点:(只初始化一次(初始化是在定义的时候赋值),不初始化的话默认为零)
静态区内存是由系统管理的,一旦静态区的内存被分配,静态区的内存直到程序全部结束之后才会被释放。
四、常量区
常量区存放: 常量(程序在运行的期间不能够被改变的量 例如: 10 ,‘a‘ ,1.12 ,“ios”,数组的名字)
常量区的内容通常只能读的不能被修改的。
五、代码区
内存还是由系统控制的
代码区的存放 :程序中的函数编译后cpu指令
代码区的地址:函数的地址,程序的入口地址,程序的名字。
程序被操作系统加载到内存的时候,所有的可执行代码(程序代码指令、常量字符串等)都加载到代码区,这块内存在程序运行期间是不变的。代码区是平行的,里面装的就是一堆指令,在程序运行期间是不能改变的。函数也是代码的一部分,故函数都被放在代码区,包括main函数。
内核(kernel)利用文件描述符(file descriptor)来访问文件。文件描述符是非负整数。打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。
函数内部修改外部变量的值 请传外部变量的地址.
外部变量为0级指针 函数的形参为1级指针
外部变量为1级指针 函数的形参为2级指针
外部变量为2级指针 函数的形参为3级指针
。。。。。。
外部变量为n-1级指针 函数的形参为n级指针
堆区使用注意事项:
1、指向堆区空间的指针变量 不要随意的更改指向(如果更改了指向,没有对先前申请的空间的指向了,所以没有办法释放先前的空间,导致内存泄漏)
2、不要操作已经释放的空间
3、不要对同一份空间重复释放
只要在释放空间时判断指向是否为空,再进行释放就可以了。
数据库
tips:
查找表sensor中address为aaaa::11:22ff:fe33:4461的最后一条记录
select * from sensor where address = ‘aaaa::11:22ff:fe33:4461’ order by id desc limit 0,1;
其中order by id desc 是按照id降序排列;limit 0,1中0是指从偏移量为0(也就是从第1条记录)开始,1是指需要查询的记录数,这里只查询1条记录
我们可以看到进程直接调用内核暴露出来的的接口的方式称为系统调用;而调用将内核暴露出来的借口封装好的函数的方法为库函数的调用,有的库函数不需要封装内核暴露出来的接口。
系统调用函数和库函数的区别:
从程序完成的功能来看:函数库提供的函数通常是不需要操作系统的服务. 函数是在用户空间内执行的,除非函数涉及到I/O操作等,一般是不会切到内核态的。而系统调用是要求操作系统为用户提供某种服务,通常是涉及系统的硬件资源和一些敏感的软件资源等。
从程序执行效率来看:当处理的数据量比较小时,函数库的函数执行效率比较好,因为函数库的函数的作法是将要处理的数据先存入缓冲区内,等到缓冲区装满了,再将数据一次写入或者读出。这种方式处理小量数据时效率比较高。但是在进行系统调用时,因为用户进程从用户模式进入系统内核模式,中间涉及了许多额外的任务的切换工作,这些操作称为上下文切换,此类的额外工作会影响系统的执行效率。那么为什么还要使用系统调用呢?这是因为,读写文件通常是大量的数据(这种大量是相对于底层驱动的系统调用所实现的数据操作单位而言),使用库函数调用可以大大减少系统调用的次数。这是因为缓冲区技术。在用户空间和内核空间,对文件操作都使用了缓冲区,当内核缓冲区写满之后或写结束之后才将内核缓冲区内容写到文件对应的硬件媒介中。
从程序的可移植性的角度来看:对于不同的系统,系统函数的实现有很大的差别。相对于系统调用,一些库函数,如C语言的标准函数库具备较高的可移植性,在不同的系统环境下,只要做很少的修改,通常情况是不需要修改的。
从运行层面上看:库函数是高层的,完全运行在用户空间, 为程序员提供调用真正的在幕后完成实际事务的系统调用的更方便的接口。系统调用在内核态运行并且由内核自己提供。
C++
能被赋值的就是左值 不能被赋值的就是右值
c语言三目运算表达式返回值为数据值,为右值,不能赋值
c++语言三目运算表达式返回值为变量本身(引用),为左值,可以赋值