六 数组
1. 数组基础
- 取数组地址:&cArray,与cArray相同,其为数组第一个元素地址
- 取数组对应索引元素的值:cArray[0] 或 *(cArray+0)(注意与 “*cArray+0” 的区别)
- 取数组对应索引元素地址:&cArray[0] 或 cArray+0
/*
数组:
1.数组的取值
2.数组的取地址(数组地址与元素地址)
3.数组是一块连续的内存空间
4.数组的设计
*/
main(){
char cArray[] = {'H','E','L','L','O'};
int iArray[] = {2,0,1,6,5};
//取数组的值
printf("cArray[0] = %c\n",cArray[0]); //cArray[0] = H
printf("iArray[0] = %d\n",iArray[0]); //iArray[0] = 1
//1.取地址
printf("&cArray = %#x\n",&cArray);//&cArray = 0x9ffe40 ,数组地址为首元素地址
//数组是一块连续的内存空间
printf("&cArray[0] = %#x\n",&cArray[0]);//&cArray[0] = 0x9ffe40,一个字母为1字节
printf("&cArray[1] = %#x\n",&cArray[1]);//&cArray[1] = 0x9ffe41
printf("&cArray[2] = %#x\n",&cArray[2]);//&cArray[2] = 0x9ffe42
printf("&cArray[3] = %#x\n",&cArray[3]);//&cArray[3] = 0x9ffe43
printf("cArray+0地址 = %#x\n",cArray+0); //cArray+0地址 = 0x9ffe40
printf("cArray+1地址 = %#x\n",cArray+1); //cArray+1地址 = 0x9ffe41
printf("cArray+2地址 = %#x\n",cArray+2); //cArray+2地址 = 0x9ffe42
printf("cArray+3地址 = %#x\n",cArray+3); //cArray+3地址 = 0x9ffe43
printf("&iArray = %#x\n",&iArray);//&iArray[0] = 0x9ffe20
printf("&iArray[0] = %#x\n",&iArray[0]);//&iArray[0] = 0x9ffe20,一个int为4字节
printf("&iArray[1] = %#x\n",&iArray[1]);//&iArray[1] = 0x9ffe24
printf("&iArray[2] = %#x\n",&iArray[2]);//&iArray[2] = 0x9ffe28
printf("&iArray[3] = %#x\n",&iArray[3]);//&iArray[3] = 0x9ffe2c
//2.取值
//此种取法为取下一个字母(ASCII码?)
printf("cArray = %c\n",*cArray); //cArray = H
printf("cArray[0] = %c\n",*cArray+0);//cArray[0] = H
printf("cArray[1] = %c\n",*cArray+1);//cArray[1] = I
printf("cArray[2] = %c\n",*cArray+2);//cArray[2] = J
printf("cArray[3] = %c\n",*cArray+3);//cArray[3] = K
//此种取法为向下一个元素移动
printf("cArray[0] = %c\n",*(cArray+0));//cArray[0] = H
printf("cArray[1] = %c\n",*(cArray+1));//cArray[1] = E
printf("cArray[2] = %c\n",*(cArray+2));//cArray[2] = L
printf("cArray[3] = %c\n",*(cArray+3));//cArray[3] = L
printf("iArray = %d\n",*iArray); //iArray = 2
printf("iArray[0] = %d\n",*iArray+0);//iArray[0] = 2
printf("iArray[1] = %d\n",*iArray+1);//iArray[1] = 3
printf("iArray[2] = %d\n",*iArray+2);//iArray[2] = 4
printf("iArray[3] = %d\n",*iArray+3);//iArray[3] = 5
printf("iArray[0] = %d\n",*(iArray+0));//iArray[0] = 2
printf("iArray[1] = %d\n",*(iArray+1));//iArray[1] = 0
printf("iArray[2] = %d\n",*(iArray+2));//iArray[2] = 1
printf("iArray[3] = %d\n",*(iArray+3));//iArray[3] = 6
system("pause");
}
2.1 例子 - 输入数组
main(){
//1.用户输入数组长度
printf("请输入数组的长度:\n");
int length;
scanf("%d",&length);
printf("输入的长度为:%d\n",length);
//2.根据长度创建数组
int aArray[length];
//3.用户输入数组的值
int i;
for(i=0;i<length;i++){
printf("请输入第%d个值:\n",i+1);
scanf("%d",&aArray[i]);
}
//4.把数组内容打印出来
for(i=0;i<length;i++){
printf("输出第%d个值:%d\n",i+1,aArray[i]);
}
system("pause");
}
2.2 例子 - 指针的长度
#include<stdio.h>
#include<stdlib.h>
main(){
int* iPoint;
char* cPoint;
printf("iPoint的长度 = %d\n",sizeof(iPoint));
printf("cPoint的长度 = %d\n",sizeof(cPoint));
system("pause");
}
3. 动态数组
/*
动态创建数组步骤:
1.获取一个长度
2.根据长度分配内存空间
3.数组元素依次赋值
4.接收扩展长度
5.根据长度重新分配内存空间
6.扩展的长度赋值
7.输出数组
结果:
请输入创建数组的长度:
3
输入数组的长度为:3
请输入第1个元素的值:
3
请输入第2个元素的值:
4
请输入第3个元素的值:
5
请输入扩展数组的长度:
2
扩展数组的长度为:2
请输入第4个元素的值:
6
请输入第5个元素的值:
7
第1个元素的值为:3
第2个元素的值为:4
第3个元素的值为:5
第4个元素的值为:6
第5个元素的值为:7
*/
main(){
printf("请输入创建数组的长度:\n") ;
//1获取一个长度
int length;
scanf("%d",&length);
printf("输入数组的长度为:%d\n",length) ;
//2据长度分配内存空间
int* iArray = malloc(length*4);
//3组元素依次赋值
int i;
for(i=0;i<length;i++){
printf("请输入第%d个元素的值:\n",i+1);
scanf("%d",iArray+i);
}
//4接收扩展长度
int length1;
printf("请输入扩展数组的长度:\n") ;
scanf("%d",&length1);
printf("扩展数组的长度为:%d\n",length1) ;
//5根据长度重新分配内存空间
iArray = realloc(iArray,(length+length1)*4);
//6根据长度重新分配内存空间
for(i=length;i<length+length1;i++){
printf("请输入第%d个元素的值:\n",i+1);
scanf("%d",iArray+i);
}
//7输出数组
for(i=0;i<length+length1;i++){
printf("第%d个元素的值为:%d\n",i+1,*(iArray+i));
}
system("pause");
}
七、内存分配
变量的分配
- 从静态存储区域分配
内存在程序编译的时候已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量 - 从栈上创建
执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动释放。栈内存分配效率高,但容量有限 - 从堆上分配
动态内存分配。程序在运行的湿乎乎用malloc或new申请内存,自己负责free或delete释放内存,使用灵活,但是容易造成问题。
堆和栈的区别
- 申请方式
- 栈:有系统自动分配,如声明一个局部变量int b,在调用函数时需要保持变量,如递归调用,要系统自动分配一个栈空间,后进先出。
- 堆:需要自己申请,并指明大小
- 申请后的系统响应
- 栈:只要神域空间大于所申请的空间,系统将为程序提供,否则报异常提示栈溢出。
- 堆:首选应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,
寻找第一个空间大于申请空间的堆节点,然后将该节点从空闲节点链表中删除,并将该节点的空间分配给程序。
另外,对于大多数系统,会在这块内存空间中的首地址记录本次分配的大小,这样,代码中的delete语句才能正确释放本内存空间。
另外,由于找到的堆节点大小不一定正好等于申请的大小,系统会自动将多余的那部分重新放入空闲链表中。
- 申请大小的限制
- 栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存区域。意思是栈顶的地址和栈的最大容量是系统预先规定好的。在WINDOWS下,栈的大小默认是2M。
- 堆:堆是向搞地质扩展的数据结构,是不连续的内存区域,空间比较灵活,也比较大。
- 申请效率
- 栈:系统自动分配,速度较快,单程序员无法控制。
- 堆:malloc/new分配的内存,一般速度比较慢,而且容易产生内存碎片,但是用起来方便。
- 存储内容
- 栈:在函数调用时,第一进栈的是主函数后的下一条指令的地址,然后是函数的个个参数,在大多数c编译器中,参数是由右往左入栈的,然后是函数的局部变量。注意静态变量不入栈。
- 堆:一般在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。
- 内存回收
1. 静态内存分配
1.1 说明
1.2 实例
/*
静态内存分配 :
静态内存是程序编译执行后系统自动分配,由系统自动释放
*/
void func(int** address){
int i=100;
//把i对应的地址赋值给iPoint
*address = &i;
}
main(){
int* iPoint;
func(&iPoint);//一级指针的地址为二级指针
//注意到i为定义到func函数中的变量
printf("*iPoint = %d\n",*iPoint); //*iPoint = 100
//执行完成后栈中func部分被销毁,i不存在,iPoint无法指向
printf("*iPoint = %d\n",*iPoint); //*iPoint = 0
printf("*iPoint = %d\n",*iPoint); //*iPoint = 0
system("pause");
}
2. 动态内存分配
2.1 说明
- malloc()//分配
- ralloc()//重新分配,使用见动态数组
- free()//释放
2.2 实例
#include<stdio.h>
#include<stdlib.h>
void func(int** address){
int i = 100;
int* temp;
temp = malloc(sizeof(int));
*temp = i;
*address = temp;
}
main(){
int* iPoint;
func(&iPoint);
printf("*iPoint = %d\n",*iPoint);
printf("*iPoint = %d\n",*iPoint);
printf("*iPoint = %d\n",*iPoint);
system("pause");
}
八、函数指针
1. 说明
2.实例
#include<stdio.h>
#include<stdlib.h>
/**
函数指针
*/
Log: result = 20
int add( x,int y){
return x + y;
}
int main(void){
int (*quantai)(int x,int y);
quantai = add;
int result = quantai(12,13);
printf("result = %d",result);
return 0;
}