数组做参数的引申:
数组做函数参数会退化为一个指针,将数组的内存首地址和数组的长度传递给被调用的函数。
类型是数组的形参和实参在函数中的数据类型不一样,前者是指针,后者是数组。
写在形参位置的数组,C++编译器仍然当作一个指针,只会分配4个字节的内存。
C语言的优势就是可以在主调函数和被调函数之间通过指针来操作内存。
形参写在括号内和函数内第一行是等价的。形参写在括号中表明形参具有对外的属性而已。
数据类型和内存的关系,数据类型的本质是固定内存大小的别名。站在编译器的角度来进行理解。
数组变量名称表示数组首元素的地址,&变量名,同样表示该变量的在内存中的地址。
a = {1,2,3,4}
在C语言中a 等于&a,而a+1不等于&a+1,这是因为a和&a所代表的数据类型不一样。
a数组首元素的地址,&a表示整个数组的地址。
数据类型的本质,是固定大小内存块的别名。
数组数据类型:
如何定义数组数类型
如何定义数组类型指针
数组类型和数组指针类型的关系。
sizeof()数据类型的大小,sizeof是操作符不是函数,sizeof测量实体的大小在编译期间就已经确定。
数据类型起别名。
用typedef来自定了数据类型
用结构体struct来包装数据类型,在没有使用typedef的情况下,在定义改数据类型的变量的时候,需要使用完整定义格式,就是struct 自定义类型 变量名,这样子。
如果使用了typedef,并且在最后重新定义了变量类型,这个时候就可以直接使用变量类型来定义变量。
typedef 对简单变量进行重命名,在后面都可以使用该重新命名的类型来定义变量。
数据类型的封装:
void是无类型,void*是无类型的指针,意思是void*可以指向任意类型的地址。
void修饰函数返回值或者是函数参数,仅仅表无,如果没有返回值,那么返回类型应该为void,如果函数没有参数,应该将其声明成为void。
void指针的含义,C语言规定,只有相同类型的指针才可以相互赋值,void*作为左值,可以接受任意类型的指针,void*作为右值赋值给其他指针的时候,需要强制类型转换。
C语言中,不存在void类型的变量。
变量3要素,名称,大小,作用域,函数也有3要素,名称,参数,返回值。
变量的本质,变量是既能读又能写的内存对象,成为变量,一旦初始化之后,只能读,不能写的成为常量,
变量的本质,程序通过申请和命名内存空间,通过变量名来访问内存空间,是一段连续内存空间的别名,是一个门牌号,通过变量来操作内存,对内存可读可写,通过变量往内存中读写数据,而不是往变量中读写数据。变量仅仅是内存地址的一个编号而已,那么变量跑那里去了?这就牵扯到了内存四区的问题。
通过变量修改内存有两种方法,一种是直接修改,另外一种是间接修改。
通过变量地址,可以直接修改变量内容,这就是通过指针修改内存内容。
变量是内存空间的别名,那么对内存空间可以再起别名?在C++中的引用可以做到。
也就是说,可以通过typedef对数据类型起别名,也可以对内存空间起别名。
变量的3要素,名称,大小,作用域。变量的声明周期。
内存4区模型和函数调用模型。
编译器如何管理定义的变量?
程序的内存四区模型。
首先,操作系统将代码load到内存中,第二,将C语言代码分成了4个区
堆区,(程序员分配内存,动态申请于释放,如果不释放,程序结束的时候可能有操作系统释放)
栈区(程序的局部变量区,编译器自动分配,存放函数的参数值,局部变量的值等),
全局区,(全局区和常量区统一管理)
常量区,就是字符串和其他常量存在的位置,程序结束后,有操作系统进行释放。
代码区,
第三,操作系统找到main函数的入口之后,就开始执行代码。
从静态区开始。
栈,堆,全局,代码区
代码区是有操作系统将代码加载到代码区中,基本上对于程序员来讲是透明的。
指针类型在32位系统下是4个字节,在64位系统下是8个字节。
指针类型,是一种只能存放变量地址的数据类型,剩下的和起其他数据类型没有太大区别
字符串常量分配的内存空间在全局区,是由操作系统来进行析构的,因此主函数没有结束之前,是可以使用全局区中的字符串常量的。
指针指向谁,就把谁的地址赋给指针。
指针变量和它所指向的内存空间变量是两个不同的概念。
栈区中函数调用在函数运行结束之后,就会被析构掉。
C++编译器会做代码优化,会将相同的字符串按照一个字符串地址进行处理。
堆区中的内存,需要程序员进行析构。
局部变量是不能够将变量的内容进行返回
理解指针的关键是看内存,没有内存块,哪来的内存首地址,没有内存首地址,哪来的指针
C语言可以在栈上分配内存,也可以在堆上分配内存
堆栈的属性。
堆栈的开口放下是开口向上的还是开口向上的?一般情况下,开口方向是向下的。
不管栈开口向上还是向下,BUFF+1永远是向上的,也就是说BUFF的生长方向永远是向上的
函数调用模型,函数的返回地址入栈,函数的参数入栈,函数的运行状态入栈
main函数可以在栈上分配内存,也可以在堆上分配内存,可以在全局区分配内存,都可以将内存地址进行子函数的传递
指针说明
指针也是一种数据类型,占用内存空间,用来保存内存地址,32位系统指针变量占4个字节,64位系统指针占8个字节
*p是操作内存的意思,在指针声明时,*表示所声明的变量为指针变量,在指针使用的时候,*表示操作指针做指向的内存空间的值。*p相当于是通过地址(p变量的值)找到一块内存,然后操作改内存。
*p放在左边,相当于是给内存赋值,*p放在右边相当于是从内存中取值。*就像一把钥匙,找到一个地址空间,修改内存空间的值
指针变量和它所指向的内存块是两个不同的概念
给p赋值p=0x1111,只会改变指针变量的值,不会改变指针所指向的内容。
给*p赋值p=a,不会改变指针变量的值,只会改变所值内存块的值。
左边p表示给内存赋值,右边p表示取值,含义不同,
不断的给指针赋值,相当于不停的改变指针的指向。
修改指针所指想的内存空间,一定要保证这个内存空间是可以修改的才行。不能修改常量区中的数据
指针也是一种数据类型,是指它所指向的内存空间的数据类型
数组做函数参数,形参有多个指针的时候,站在编译器的角度,只需要分配4个字节的内存,当我们使用指针的内存空间的时候,才关系指针指向的是一维的还是二维的。
指针前面的*,到底应该怎么理解,指针的数据类型,值得是它指向内存空间的数据类型。不同的数据类型步长不一样,也就是说+1的效果是不同的。即使是地址相同。指针的数据类型决定的指针的步长。
内存四区,指的就是堆区,栈区,全局区,代码区。
数据类型的本质就是固定大小内存块的别名,变量是一个门牌号,是一个编号,函数调用模型。
指针是一种复杂的数据类型。
在将指针释放掉的同时,需要将指针变量赋值为null这样才不会产生野指针