20180905
Unsigned char 是一个字节
C语言隐式转换:
(摘自CSDN):C语言规定,不同类型的数据需要转换成同一类型后才可进行计算。
相同类型之间:
1.字符必须先转换为整数
2.short型转换成int型
3.float转换成double,以提高运算精度
4.赋值时,一律右部值转换成左部类型
不同类型之间:
1.当整型数据和双精度数据进行运算时,则先将整型数据转换成双精度类型,再进行运算,结果为双精度型数据。
2.当字符型数据和实型数据进行运算时,则先将字符型数据转换成实型数据,再进行运算,结果为实型数据。
C语言规定的转换规则是由低级向高级转换
char,short —> int —> unsigned —>long —>double <—float
低级 ——————————————————->高级
Unsigned char t = 0x01;
Unsigned long int u = 0;
U = t;//u = 0x00000001;
结构小的转化成大的,系统自动转换,编译器会将结构大的区域多出的部分进行填充。若改为t = u;编译器会报worning,因为是大的转为小的,此时便需要使用强制类型转换。int型数值赋给char型变量时,只保留其最低8位,高位部分舍弃。如u=0x12345678,强转后变为0x78。
指针大小永远和CPU字长保持一致,不管是什么类型的指针(char、int等类型)。
万能指针 void *
void *和unsigned char *都是万能指针。因为大小相同,一切皆大小。Void *是约定的万能指针
如果形参为void *x,则*x不能在函数中直接引用。因为不知道指向的是大小多少的空间,需要强转成其他类型。若写成unsigned char *x,则可以直接引用*x,长度为1字节。
(摘自CSDN):void *是一种指针类型,常用在函数参数、函数返回值中需要兼容不同指针类型的地方。我们可以将别的类型的指针无需强制类型转换的赋值给void *类型。也可以将void *强制类型转换成任何别的指针类型,至于强转的类型是否合理,就需要程序员自己控制了。
C语言隐式转换:https://blog.csdn.net/qq_29169813/article/details/51181628
20180907
数组名称是指针常量。(不是常量指针,二者注意区分)
两个const连用(自己了解,面试题类型):
Const stu_t ay[2];
Const struct stu *const (*xp) = NULL;
关键字:typeof
Unsigned long int f[4];
f[1]===f[1*sizeof(unsigned long int)]
(数组计算方法)
函数指针:指向的是指令区域。
Int func(int a)
{
Int x=a;
Return (x);
}
Int func2(int a)
{
Int x=a;
Return (x);
}
Int main(int argc,const char *Argv[]){
Int ( *fp ) ( int ) = func;//函数指针,指向函数。
Func2(1);
Fp = func;
Fp(1);//这里fp即为func,若没有上一行fp=func2,则fp为func2;
Return 0;
}
函数指针与其指向的函数簇(只要符合那个函数的形式,函数指针均可指向)。例如上方代码的func和func2,就是函数簇。函数形式相同,入参个数和类型相同,即可看为相同类型的函数,函数簇。
int* f(int a, int b); // 指针函数,重点强调返回值类型是一个地址变量
int (*f)(int a, int b); // 函数指针,强调一种函数类型
20180908
指针函数:
指针函数,简单的来说,就是一个返回指针的函数,其本质是一个函数,而该函数的返回值是一个指针。
声明格式为:类型标识符 *函数名(参数表)
函数指针:
函数指针,其本质是一个指针变量,该指针指向这个函数。总结来说,函数指针就是指向函数的指针。
声明格式:类型说明符 (*函数名) (参数)
区别:
指针函数本质是一个函数,其返回值为指针。
函数指针本质是一个指针,其指向一个函数。
写法:
指针函数:int* fun(int x,int y);
函数指针:int (*fun)(int x,int y);
可以简单粗暴的理解为,指针函数的*是属于数据类型的,而函数指针的星号是属于函数名的。
再简单一点,可以这样辨别两者:函数名带括号的就是函数指针,否则就是指针函数。
malloc的全称是memory allocation,中文叫动态内存分配,用于申请一块连续的指定大小的内存块区域以void*类型返回分配的内存区域地址,当无法知道内存具体位置的时候,想要绑定真正的内存空间,就需要用到动态的分配内存。
malloc用法:
指针名=(数据类型)malloc(长度),(数据类型)表示指针.
举例1:int* buffer = (int*)malloc(8 * sizeof(int));
20180909
单向链表:链式的存储结构,在逻辑上是连续的,每次通过一个指针来指向下一个节点将其链接起来。
单向循环链表:与单向链表的区别就是,单向链表的最后一个节点指针是指向NULL的,单向循环链表最后一个节点的指针是指向头节点head的。
双向链表:包含两个指针,一个(prior)指向前一个节点,一个(next)指向后一个节点。
双向循环链表:最后一个节点的next指向head,而head的prior指向最后一个节点,构成一个环。
20180910
声明和定义:
Int func(int a){
}
Int main(){
Int func(int);//声明,声明入参类型,告诉编译器,这个符号存在过
Func(0x02);//调用
}
extern 关键字:如果声明里没有extern,表示当前符号一定在当前本模块(一个.c文件)内出现
如果加了extern,表示这个符号原则上应该在其他文件的其他模块中出现过,是共享的。链接器会找到,若找不到,则报错。
关于extern关键字:
1.声明可以多次,定义只能一次。
2.关键字extern用于扩展变量和函数的可见性。
3.由于函数默认存在extern,不需要再定义和声明的时候使用extern。
4.当变量使用extern时,它只是声明没有定义。
5.当变量用extern声明并且有初始化时,和变量的定义一样。
Int main(){
Extern int func(int);//声明,声明入参类型
Func(0x02);//调用
}
不管什么类型的指针,指针都占4个字节。
Char *p=((void *) 0);//相当于char *p=NULL;赋初值,但是不是实例化。实例化:给指针指向一段有意义的空间。
P=(char *)malloc(sizeof(char));//运行到这里,向一段预定的区域,动态分配一块可用的内存。若到这里内存不够,则返回为空。
Heap:堆空间(专门用于动态内存分配,必须自己操作,编译器不会管)(可以认为是一段“永久空间”,生命周期随着main函数的结束而结束)
Stack:栈空间(由编译器自己操作)(可以认为是临时空间)
堆内存大,栈内存小。
对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列。
堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。
栈的效率比堆的效率高。
Malloc分配的空间,如果不手动释放,则一直占用空间。空间分配在堆里。
静态分配的内存是有作用域的。
Malloc是空类型,需要进行强制类型转换。
Int main()
{
#define NULL((void *)(0))
Char a = 0x01;//静态分配的内存,空间在栈里。当函数结束,这段空间将被抹掉,将来可能会在其他函数中被复用。若程序结束后还想使用,可用static。但是static为不可重入函数,在多线程中则无法使用。
Char *p=NULL;
P=&a;
P=(char *)malloc(sizeof(char));
Func(0x02);
Retuen 0;
}
C语言中static的三种用法(摘自CSDN):
1.全局变量:
在全局变量之前加上关键字static,全局变量就被定义成为一个全局静态变量。
1)内存中的位置:静态存储区(静态存储区在整个程序运行期间都存在)
2)初始化:未经初始化的全局静态变量会被程序自动初始化为0(自动对象的值是任意的,除非他被显示初始化)
3)作用域:全局静态变量在声明他的文件之外是不可见的。准确地讲从定义之处开始到文件结尾。
好处:
定义全局静态变量的好处:
<1>不会被其他文件所访问,修改
<2>其他文件中可以使用相同名字的变量,不会发生冲突。
2.局部静态变量
在局部变量之前加上关键字static,局部变量就被定义成为一个局部静态变量。
(1)内存中的位置:静态存储区
(2)初始化:未经初始化的全局静态变量会被程序自动初始化为0(自动对象的值是任意的,除非他被显示初始化)
(3)作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域随之结束。
3. 静态函数
在函数的返回类型前加上关键字static,函数就被定义成为静态函数。
函数的定义和声明默认情况下是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。
定义静态函数的好处:
<1> 其他文件中可以定义相同名字的函数,不会发生冲突
<2> 静态函数不能被其他文件所用。 存储说明符auto,register,extern,static,对应两种存储期:自动存储期和静态存储期。 auto和register对应自动存储期。具有自动存储期的变量在进入声明该变量的程序块时被建立,它在该程序块活动时存在,退出该程序块时撤销。
关键字extern和static用来说明具有静态存储期的变量和函数。用static声明的局部变量具有静态存储持续期(static storage duration),或静态范围(static extent)。虽然他的值在函数调用之间保持有效,但是其名字的可视性仍限制在其局部域内。静态局部对象在程序执行到该对象的声明处时被首次初始化。
由于static变量的以上特性,可实现一些特定功能。
可重入函数和线程安全函数(摘自CSDN):
可重入函数是由于一个任务并发使用,而不用担心数据的错误,相反,不可重入函数不能被一个任务所共享,除非能确保函数的互斥(使用信号量或禁用中断)。可重入函数可以在任意时刻被中断,稍后再运行,数据不会产生问题。不可重入函数要么使用局部变量,要么使用全局变量时保护自己的数据(加锁等方式)。
使用了静态变量(全局&局部)或是静态函数的,为不可重入函数。
线程安全函数和可重入函数的关系:
(1)可重入函数一定是线程安全函数
(2)线程安全函数不一定是可重入函数(有可能通过互斥机制实现线程安全不安全函数—>线程安全函数的转换等)
(3)可重入性要强于线程安全性(相当于函数产生相同结果时可重入性的条件要苛刻)
C程序一直由下列部分组成(摘自CSDN):
1)正文段——CPU执行的机器指令部分;一个程序只有一个副本;只读,防止程序由于意外事故而修改自身指令;
2)初始化数据段(数据段)——在程序中所有赋了初值的全局变量,存放在这里。
3)非初始化数据段(bss段)——在程序中没有初始化的全局变量;内核将此段初始化为0。
4)栈——增长方向:自顶向下增长;自动变量以及每次函数调用时所需要保存的信息(返回地址;环境信息)。
5)堆——动态存储分。是向高地址扩展的数据类型,是自下向上的扩展方式
变量的定义和声明:
定义只能一次,声明可以多次。
定义也是声明(定义变量时声明了类型和名字)。
定义会分配存储空间,声明不会。
如果声明有初始化式,就被当作定义,即使前面加了extern。
可重入函数和线程安全函数:https://blog.csdn.net/skyroben/article/details/72900748
变量的声明和定义:https://blog.csdn.net/gatieme/article/details/50640424