左值:放在等号左边的值,代表其表示的存储空间;
(指针可以作为左值是因为它是一个变量)
右值:放在等号右边的值,变量放在右边代表其所代表的内容;
数组
数据比较基础,这里不详细介绍,这里只单独拿出来强调数组作为函数的参数时的变化:
1.一维数组作为函数传递的参数时,它会降级成为一个指向一维数组元素的指针;
2.二维数组作为函数传递的参数时,它会降级成为一个指向一维数组的指针;
3.三位数组作为函数传递的参数时,它会降级成为一个指向二维数组的指针;
c语言中数组的排布在内存中永远都是线性的,如二维数组,三维数组;
数组名在做右值时,代表第一个元素的首地址(数组名不能做左值)
数组的首地址代表数组的地址;
eg:
1.一维数组的数组名做右值代表第一个元素的首地址;
2.二维数组的数组名做右值代表第一行的首地址;
指针
1.指针也是变量,也有存储空间和存储内容;
2.指针本身就是一个对象,可以赋值和拷贝,无需在定义的时候赋初始值,它的指向可以改变;
eg:
int * p = a ; //将a的值放在p所代表的存储空间中;
itn * q = p;//将p所代表的内容放在q所代表的存储空间中;
3.指针+1 的实际大小与其指向的对象的类型有关,比如数组指针和指针数组;
4.如果两个指针想要进行加减运算,那么这两个指针必须在同一个数组中;
1》两个指针相减的结果是这两个指针之间元素的个数;
2》如果两个指针指向不同数组,则它们之间的加减结果是未定义的;
数组指针:指向数组的指针,这是一个指针;
int (*p) [10]; -----> p + 1 ==》 实际上加了整个数组;
指针数组:存放指针的数组,这是一个数组;
int * p[10]; ------> p + 1 ==》 实际上指向第二个元素指针;
函数指针:指向函数的指针;
char (*fuc) (char *p1,char *p2)
{
}
指针函数:返回值是一个指针的函数;
char* fuc (char *p1,char *p2)
{
}
指针和数组的关系
//常量字符串没有名字(在内存中),只能使用指针来访问
char * ptr = "abcdef";
//常量字符串有名字arr(在内存中),可以直接通过arr来访问字符
char arr[7] = "abcedf";
//1.如果一个指针指向一维数组时,访问其中的某个值的访问是“匿名访问”, *(ptr+i)
//2.如果一个数组指向一维数组时,访问其中的某个值的访问是“具名+匿名 访问”, arr[i]
(1)二者没有关系;
(2)二者都是可使用 “匿名访问”,“具名 + 匿名 访问”;
(因为 arr[i] —> 编译器会自动将其翻译成 *(arr + i))
(3)数组名是一个 “指针常量”,指针也是一个 “指针常量”;
这里有一篇关于指针的详解文章:指针
动态内存分配
堆适合处理大型数据,栈适合处理小型数据;
内存值出现是为了解决性能问题,如果动态申请的内存在使用完成后,没有使用free释放掉的话就会造成内存泄漏问题;
内存申请:
malloc
malloc 申请的内存实际上会比申请时大一些;
malloc 申请的值比较大;
malloc 申请时会先向内存池要,如果没有再向操作系统要;
malloc 申请返回值为void* 类型,返回值必须检查;
void* malloc( unsigned int size );
calloc
也用于内存分配,但是其返回地址之前会把它每个字节初始化为0;
void* calloc( 元素个数, 大小)
realloc
主要用于修改一个原先已经分配好的内存块的大小,主要用于扩增容量,扩增容量后原指针会被删除;(主要用于动态调整已经开辟好的空间)
void* realloc (老指针,新的大小)
备注:每次动态申请内存空间时,并不是一定会成功,所以申请完后要检查;
内存释放
主要对于用户动态申请的堆空间在不适用后的动态回收,防止内存泄漏;
free
1.free 不可以对一段动态空间多次释放;
2.free 不可以对一段动态空间只释放一部分;
动态分配的问题:
动态分配内存可能引发的错误:
1.操作内存时超出了分配内存的边界;
2.内存泄漏,分配后的内存没有释放(free)造成的后果;
这里有篇文章有详细介绍:c语言动态分配内存