子函数:(为什么用指针,可以返回一个以上的值(返回一个数组或者结构体的指针))
如何让子函数更改主函数里面的数据;如何让子函数返回一个以上的值;
1:子函数的形参为主函数中要修改的变量的地址;
2:调用子函数的时候把要修改的变量的地址传递给子函数;
3:在子函数里面修改这个地址里面存在的变量的内容;
4:主函数使用这个变量的时候里面的值就发生了变化;
通过被掉函数修改主调函数普通变量的值
1.实参必须是普通变量的地址
2.形参必须是指针变量
3.被掉函数中通过修改 *形参名的方式修改主调函数相关变量的值
如:
Void f(int *p ,int *q)
{ *p =1;
*q=2;
}
Int main(void)
{ int a =3,b=5;
F(&a,&b);
Printf(“%d %d \n”,a,b);
Return 0;
}
指针的运算
int main(void)
{
int i = 5;
int j = 10;
int * p = &i;
int * q = &j;
int a[5];
p = &a[1];
q = &a[4];
printf("p和q所指向的单元相隔%d个单元\n", q-p);
//p - q 没有实际意义
return 0;
}
指针占用几个字节:
在32位的操作系统上因为程序最大能使用的内存空间的地址就是2的32次方;指针只需要4位就可以表示出来所有的内存空间;
而64并且编译支持64为 8位;
{
char ch = ‘A‘;
int i = 99;
double x = 66.6;
char * p = &ch;
int * q = &i;
double * r = &x;
printf("%d %d %d\n", sizeof(p), sizeof(q), sizeof(r)); //都是4;
return 0;
}
指针和数组的关系
数组名,下标和指针的关系,指针变量的运算
数组名
int a[5] //a是数组名,5是数组的大小,元素个数
int a[3][4] // 3行4列 a[0][0]就是数组的第一个元素
Int b[5]
A=b ;//错误
一维数组名是个指针常量,它存放的是一维数组第一个元素的地址
int a[5];
int a[3][4];
printf("%#X\n",&a[0]);
printf("%#X\n",&a);
下标和指针的关系
如果p是个指针变量 则
p[i] 等价于 *(p+i)
// arr是一个什么东西呢?
printf("arr = %#X\n",arr);
// 打印 数组的第一个元素的地址
printf("arr[0]地址 = %#X\n",&arr[0]);
// 打印数组中的第二个元素
printf("arr[1]=%d\n",arr[1]);
printf("arr[1]=%d\n", *(arr+1));
//问题: arr[i] *(arr+i) 代表的是同一个变量么?
// 代表的是同一块内存空间 指向的是同一个变量
//通过实验 : 数组名表示的 就是这个数组第一个元素 的首地址
如:
#include <stdio.h>
#include <stdlib.h>
#define pi 3.1415
// 写一个子函数 打印数组里面的每一个元素
void printArr(int* arr, int len){ // arr是数组的首地址 len数组的长度
int i=0;
for(;i<len;i++){ // 在c99 的语法格式下 for循环的初始化条件 不能写在 for 循环的括号里面
// printf("arr[%d]=%d\n",i,arr[i]); // arr[i] 和 *(arr+i) 代表的含义相同
printf("arr[%d]=%d\n",i, *(arr+i));
}
}
main()
{
// int arr[10]={1,2,3,4,5};
// printArr(&arr[0],10);
//1 .定义一个数组 缺陷 数组的长度 必须事先申请好
//int arr[1000]; // 这一句代码一旦执行 ,就立刻会在内存里面申请 5个内存空间 每个内存空间的大小可以存放一个int类型的数据
// 没有办法动态的增加这一块空间的大小, 也没办法减小这一块内存空间
//2. 循环赋值
//3. 打印数组里面的内容
int* arr = (int* )malloc(sizeof(int)*10);
arr = realloc(arr,sizeof(int)*8); //空间的长度为12了
int i =0;
for(;i<5;i++){
printf("请输入arr[%d]=的值\n",i);
scanf("%d",arr+i);
}
printArr(arr,5);
system("pause"); // 调用windows下系统的命令 让程序暂停执行 方便观察程序的执行结果
// 作业: 从键盘接受一个数组 数组的大小
// 动态的更改 数组的大小增加2
}
传统数组的缺点
1.数组长度必须实现指定,并且只能是常整数.int a[5];int len;int a[len];//error
2.传统形式的数组,程序员没法手动释放空间,数组一旦定义,系统为该数组分配的空间一直存在,函数运行完毕,数组的空间就被释放
3.数组的长度不能在函数运行中动态增加或者缩小
4.A函数定义的数组只有在A没执行完毕前被使用,a函数运行完毕后,a的数组就无法被其他函数使用
f(int** address){ //address 存放的是q的地址
// 动态的在堆内存里面申请一块空间
int* p ;
p = (int*)malloc(sizeof(int)*3);
*p = 3;
*(p+1) = 4;
*(p+2) = 5 ;
printf("子函数里面 地址%#X\n",p);
*address = p;
// 在子函数里面把p释放掉了
free(p);
}
main()
{
int* q ;
f(&q);
printf("主函数里面 地址%#X\n",q); //主函数和子函数打印的地址是一样的;
printf("*q = %d\n",*(q+0));
printf("*q = %d\n",*(q+1)); // 残留的内存映像 ,这样用是错误的;
printf("*q = %d\n",*(q+2));
//动态内存分配 程序员可以自己手工的决定一个变量的生命周期
//手工的释放调用内存空间
//不要使用已经回收掉的内存空间里面的数据
system("pause"); // 调用windows下系统的命令 让程序暂停执行 方便观察程序的执行结果
}
(1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
(2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多.
堆和栈的区别:
1.申请方式
栈:
由系统自动分配.例如,声明一个局部变量int b; 系统自动在栈中为b开辟空间.例如当在调用涵数时,需要保存的变量,最明显的是在递归调用时,要系统自动分配一个栈的空间,后进先出的,而后又由系统释放这个空间.
堆:
需要程序员自己申请,并指明大小,在c中用malloc函数
如char* p1 = (char *)malloc(10);
但是注意p1本身是在栈中的.
2. 申请后系统的响应
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时, 会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
3.申请大小的限制
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(vc编译选项中可以设置,其实就是一个STACK参数,缺省2M),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
4.申请效率的比较:
栈:由系统自动分配,速度较快。但程序员是无法控制的。
堆:由malloc/new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
5.堆和栈中的存储内容
栈:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。
6.内存的回收
栈上分配的内存,编译器会自动收回;堆上分配的内存,要通过free来显式地收回,否则会造成内存泄漏。
堆和栈的区别可以用如下的比喻来看出:
使用栈就像我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就像是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。
多级指针:
如:
int i = 5;
int* p = &i;
int** q = &p;
int*** r = &q;
printf("i=%d\n",***r); //i=5;
函数的指针和指针的函数:
/*1.定义int (*pf)(int x, int y);
2.赋值 pf = add;
3.引用 pf(3,5);
*/
#include <stdio.h>
#include <stdlib.h>
int add(int x , int y){
return x+y;
}
main()
{
int (*pf) (int x, int y); //定义一个函数的指针的声明 名字叫pf 返回值 int 接受参数两个int
pf = add;
printf("result=%d\n", pf(3,6));
system("pause"); // 调用windows下系统的命令 让程序暂停执行 方便观察程序的执行结果
}
结构体:
每个学生都有age score name....
int age;
int score;
char name[100]; // c语言里面的字符串一般都存放在字符数组中.
结构体定义格式:
第一种
struct Student{
int age;
float score;
char sex;
}
第二种
struct Student2{
int age;
float score;
char sex;
} st2;
第三种
struct{
int age;
float score;
char sex;
} st3
如:
struct Student
{
int age; //4
float score; // 4
long id; //4
char sex ; //2 vc 6.0 14
};
如:
main()
{
struct Student st={80,55.6f,100010 ,‘F‘};
printf("age = %d\n",st.age);
printf("score = %f\n",st.score);
printf("id = %ld\n",st.id);
printf("sex = %c\n",st.sex);
// 结构体的长度
printf("长度 = %d\n",sizeof(st)); //跟编译器的不一样;v6的是:14;而devC++的是:16(取最大的全部个数相加 4+4+4+4=4*4);
struct Student st2;
st2.age = 10;
st2.score = 88.8f;
st2.sex=‘F‘;
struct Student* pst;
pst = &st;
//printf("age = %d\n", (*pst).age); //*&="";&*两个综合运算后为变量;
printf("age = %d\n", pst->age); //pst->age 在计算机内部会被转换为 (*pst).age
system("pause"); // 调用windows下系统的命令 让程序暂停执行 方便观察程序的执行结果
}
联合体:
如:
main( ){
struct date { int year, month, day; } today;
// 联合体 是定义一块相同的内存空间 存放里面的数据 ;mix.I mix .k mix.ii共用相同的地址
union { long i; int k; char ii;double d; } mix;
// 联合体的作用就是用来表示一组数据类型 数据的数据类型为这一组中的某一种数据类型
//注意 : 联合体里面的数据内容会相互覆盖
printf("date:%d\n",sizeof(struct date)); //date:12 =4*3;
printf("mix:%d\n",sizeof(mix)); //mix:8
mix.ii = ‘A‘;
printf("k=%d\n",mix.k);//k=65
system("pause");
}
枚举:Typedef:声明自定义数据类型,配合各种原有数据类型来达到简化编程的目的的类型定义关键字。
如:
#include <stdio.h>
typedef int haha;
enum WeekDay
{
Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
};//0,1,2,3,4,5,6;
//{
//Monday=10,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
//};//10,11,12,13,14,15,16;
int main(void)
{