1:函数:
2:函数定义
在程序中测试一下;
C语言执行是从上而下执行。是由函数组成的可执行文件。
C是以函数为基础,而java是以类为基础,类作为一个对象,方法可以写在类中。
而C是函数组成的,当我们设计一个程序,会产生很高的耦合,而头文件就相当于一个接口,每一个接口,每一个类都有一个接口,如果我想调用这个类,可以直接把这个接口,即.h文件引入进来就可以了,不用引入.cpp文件。
实际上,接口只是对类进行规范,在编译的时候,并不会被编译成具体的对象;
同样的,.h头像并不会参与编译,真正的是头文件所实现的c文件。是为了解决程序的耦合问题
3:指针函数
指针函数的写法,和我们平时的写法是一致的。
至于指针类型是什么,我们不需要担心,可以是int类型,也可以是Object类型,也可以是void类型
所以当我们&(取地址),这里的void类比于object类型
4,函数指针
定义:函数指针是指向函数的指针变量,即本质是一个指针变量。
如:
int(*f)(int x); /*声明一个函数指针**/
//前面代表函数的返回值类型
func; //将func函数的首地址赋值给指针f/
void(*funcp)();
void point_func() {
printf("函数指针\n");
}
int main()
{
int a = 2;
int_add_func(&a);
funcp = point_func;
funcp();
}
运行正常打印。
java面向对象,对基本数据类型可以修改,通过指针可以修改
5:Linux内存布局原理
基础知识 目录框架~
1:C和C++基础
2:MakeFile与Cmake详解
3:Opencv图像识别
4:网易音视频编解码
5:阿里Sophix实战==热修复
6:网易课堂直播核心技术(Ffmpeg,Rtmp)
7:网易直播通话实战(Webrtc)
Android瓶颈:
Ndk还能继续深入研究
只学习java是不够的~
C语言最难的地方
内存 指针
本节课学习内容
1:数据类型
2:数组
3:内存布局
4:物理内存
5:虚拟内存
1:内存定义
硬件角度:内存是计算机必不可少的一个组成部分,是于CPU沟通的桥梁,计算机中所有的程序都是运行在内存汇中的。
譬如:是内存条
逻辑角度:内存是一块具备随机访问能力,支持读,写操作,用来存放程序以及程序运行中产生的数据区域。
譬如:我们可以把建筑物当做一个内存,每一个内存都对应一个房间
,整个内存条就对应这栋大楼,每个内存地址对应房间号,内存中储存的内容就是房间里面住的人,我们可以根据内存的单元地址,我们可以根据地址找到存储内容
关于内存还有很多概念:
比如内存单位,内存编制,内存地址,内存组成,基础函数。
内存单位:内存都是有大小的
类型:1:位 bit 是电子计算机中最小的数据单位,每一位的状态只能是0 或者1
2:字节: 1Byte = 8bit 是内存基本的计量单位
3:KB: 1KB = 1024Byte 1KB = 1024 * 8 = 8192 bit
4 MB 1MB =1024 KB 类似的还有GB,TB
内存编制:计算机中的内存按字节编址,每个地址中的存储单元可以存放一个字节(8bit)的数据,CPU通过内存地址获取指令和数据,并不关心这个地址所代表的空间具体再什么位置,怎么分布,因为硬件的设计保证一个地址对应一个固定的空间,所以说:内存地址和地址指向的空间共同构成了一个内存单元。
譬如:一个建筑立面有地板砖
内存地址:内存地址通常用十六进制的数据表示,指向内存中的某一块区域。
java中不需要我们来管理内存地址,但是C语言就需要我们自己来管理了。
内存地址分配规则:
内存分配规则是连续的,一个挨着一个的。
例如村子里面的房子都是一个挨着一个的,当有新的对象产生的时候,会给分配一个新的地址。
当对象申请内存的时候,先给对象分配一个编码,这个编码就是内存地址。
比如上面的0x0ffc2 就是一个地址,至于这个地址存放在哪,不确定,但是可以通过这个地址找到具体的对象。
内存对象:
指针指向的内存区域能存储不同的类型。
基本数据类型:
内存组成;
我们先来看android的内存组成:
而C语言的内存组成:
c语言的执行不依赖于任何虚拟机,直接编译成汇编语言直接执行。
BSS端:存放成员变量,在编译的时候无法确定大小
数据端:存放的是数据
代码端:把这个函数转化成汇编语言。
基于汇编语言形成的过程语言。
下面学习:数组和指针,数组指针,指针数组
1:数组:是数据集合,在内存中是连续的
通过指针操作数组
int main()
{
int arry[] = { 100,200,300 };
for (int i = 0; i < 3; i++) {
printf("数组 %d\n" , arry[i]);
}
int* p = arry;
*p = 400;
for (int i = 0; i < 3; i++) {
printf("数组 %d\n", arry[i]);
}
return 0;
}
打印的值是
发现指针修改的是数组的第一个元素进行了修改;
那么如何指向第二个元素呢?
int main()
{
int arry[] = { 100,200,300 };
for (int i = 0; i < 3; i++) {
printf("数组 %d\n" , arry[i]);
}
int* p = arry;
*(p+1) = 400;
for (int i = 0; i < 3; i++) {
printf("数组 %d\n", arry[i]);
}
return 0;
}
第二个数组元素变成了400;
+1的操作,
当前的指针类型是int类型,那么+1就是+了4个字节
指针意义:
通过指针可以操作数组的能力
2:指针数组
这个数组中的每一个元素都是一个指针,指针变量分别指向不同的变量abc;
如图:
int main()
{
int arry[] = { 100,200,300 };
int *pa[3];//指针数组
for (int i = 0; i < 3; i++) {
pa[i] = &arry[i];
;
}
for (int i = 0; i < 3; i++) {
printf("数组 %d\n", pa[i]);
printf("数组 %d\n", *pa[i]);
}
return 0;
}
3:指针优先级
定义:() > [] > *
意思就是定义指针数组的时候
int *pa[3];//指针数组
先定义pa[3]为数组,后面才取* 为指针,所以为指针数组。
4:数组指针(也称为行指针)
定义 int (*p)[n]
优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n.也可以说是p的步长。
也就是说执行p+1时,p要跨过n整个整型数据的长度。
int a[3][4];
int (*p)[4]//该语句定义一个数组指针,指向含4个元素的一维数组
p=a;//将该二维数组的首地址赋值给p,也就是a[0]或&a[0][0]
p++;//该语句执行过后,也就是p= p +1 ,p跨过行a[0]指向了行a[1]
然后我们一起来看一下指针数组 和数组指针的区别
写法上就是多了一个括号
指针数组,他是一个数组,这个数组里面存放的是指针 int *p[n]
而数组指针,只是一个变量,这个变量存放一个指针 int (*p)[n]
但是他的步长发生了变化,他的步长指向的是二维数组的首地址,如果你对当前指针进行
+1操作,那就会指向第二行的首地址
5:结构体
基础知识:
结构体和共用体是一致的。
struct关键字类比于java中的class
student ,a 是成员变量
例如:
struct Student {
int i;
short j;
}s1, s2;
int main()
{
struct Student student;
student.i = 10;
student.j = 20;
printf("使用结构体打印变量 %d \n", sizeof(student));
s1.i = 8;
s2.j = 6;
printf("使用结构体打印变量 %d \n", sizeof(student));
return 0;
}
运行结果
结构体打印是8 ,为什么是8呢 我们来学习一下c的内存对齐
6:内存对齐:
定义:对齐跟数据再内存中的位置有关系。如果一个变量的内存地址正好位于它长度的整数倍,他就被称作自然对齐。
比如在32位cpu下,假设一个整型变量的地址为0x0000004,那么他就是自然对齐的。32位就是4位
当结构体需要内存过大,使用动态内存申请。结构体占用字节数和结构体内字段有关系。指针占用内存就是4/8字节,因此传 指针比传值效率更高。
结构体存储原则:
1 ,结构体变量中的成员的偏移量必须是成员大小的整数倍(0被让认为是任何数的整数倍)
2,结构体大小必须是所有成员大小的整数倍,也即所有成员大小的公数倍。
例如:刚才定义了 short类型,他是2个字节,在32位cpu下,他最小的单位步长是4个字节,2个字节小于4个字节,所以他会给一个short类型的成员变量分配4个字节,只不过后面2个自己个是不起作用的。
为什么要内存对齐呢?
例如:小张想找隔壁老忘和老宋一起帮忙照看下媳妇
由于他们住在不同的楼层,所以小张必须要跑两次。
这个是没有内存对齐的情况下,小张必须跑两次,那么小张可以不可以跑一次呢/
那就只能让老张和老王同时在一个房间。 (放到一起就是结构体。。。。)
char也是占1个字节
对齐前,找到一个变量,需要两次寻址,比如int i 在储存过程中换行了
对齐后是12个自己 前面c1是1个字节,后面是4个字节,c2也是1个字节 但是对齐后 就占了12个字节
7:共用体
共用体和结构体类似
也是一个数据类型的结合。
定义:共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。
您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。
共用体提供了一种使用相同的内存位置的有效方式。
例如:
我有100个成员变量,但是在某一时刻只有一个变量有值,那么此时用共用体就非常省内存。
使用:
注意:共用体占用的内存应足够存储共用体中最大的成员。例如,上面的实例中,Data占用了20个字节的内存空间,因为再各个成员中,字符串所占用的空间是最大的。下面的实例将显示上面的共用体占用的内存大小。
取决于最长的内存大小
union MyUnion
{
int i;
float j;
};
int main()
{
union MyUnion mystudent;
mystudent.i = 10;
mystudent.j = 11;
printf("i的地址 %#x\n",&mystudent.i);
printf("j的地址 %#x\n", &mystudent.j);
return 0;
}
运行结果:
union MyUnion
{
int i;
int k;
float j;
};
int main()
{
union MyUnion mystudent;
mystudent.i = 10;
printf("i的地址 %d\n", mystudent.i);
mystudent.j = 11;
mystudent.k = 11;
printf("i的地址%d\n",mystudent.i);
printf("j的地址 %d\n", mystudent.j);
return 0;
}
运行结果:
将i值的内存位置重新赋值给k值。
8:so动态库与编译
库的概念:
windows平台和linux平台下都大量存在着库。android中也存在着库,库顾名思义,值的是一个容器文件,里面装的都是函数,
由于Windows和linux的平台不同(主要是编译器、汇编器和连接器的不同),一次二者的二进制库是不兼容的。
库存在的意义:
库是被人写好的现有的,成熟的,可以复用的代码,现实中每个程序都要依赖很多基础的底层库。UK额能每个人的代码都是从零开始的,因此库的存在意义非同寻常。
库的种类:
动态库和静态库
.so/.dll 和 .a/.lib
可以把库理解为jar包
他们的区别是发生在链接和加载过程中的,目的都是加载第三方的函数
如何编译一个动态库?
使用gcc进行预编译和编译。
//======================
//=====================
动态库和静态库区别;