输入输出函数
%d - int
%ld – long int
%hd – 短整型
%c -char
%f - float
%lf – double
%u – 无符号数
%x – 十六进制输出 int 或者long int 或者short int
%o - 八进制输出
%s – 字符串
Int len;
Scanf(“%d”,&len);
什么是指针
Int main(void)
{
int* p; //p 是变量的名字,int * 是一个类型
//这个变量存放的是int类型变量的地址。
inti =3;
p=&i;
system(“pause”);
return 0;
}
1.介绍内存,内存编号就是地址,内存分很多单元,每个单元对应一个编号. 介绍地址,内存单元的编号,
为什么4G内存只显示3G
从0开始的非负整数.
0000~FFFF
XP操作系统 为神马只能显示3G内存。
常用的XP系统都是32位的系统,就是说在所有程序(包括)系统本身运行的时候,最多能使用2的32次方的地址,大家可以自己算一下2的32次方就是4G,但问题是,系统里面除了内存还有其它设备啊,显卡硬盘之类的都是需要地址的,所以,留给内存使用的地址只有3G多一点,剩下的要保留给其它设备。
指针就是地址. 热身小程序介绍指针
int * p;//p是变量的名字, int * 表示p变量的数据类型是存放int类型的地址的数据类型
//int * p; 不表示定义了一个名字叫做*p的变量
// int * p;应该这样理解: p是变量名, int *是数据类型,p变量的数据类型是int*
//所谓int * 类型 实际就是存放int变量地址的类型
int i = 3;
p = &i;
/*1. p保存了i的地址, 因此p指向i
2. p不是i,i也不是p,修改p的值不影响i的值,修改i的值也不会影响p的值
3. 如果一个指针变量指向了某个普通变量, 则 *指针变量 就完全等同于 普通变量
例子:
如果p是个指针变量,并且p存放了普通变量i的地址
则p指向了普通变量i
*p 就完全等同于 i
在所有出现*p的地方都可以替换成i
在所有出现i的地方都可以替换成*p
*p 就是以p的内容为地址的变量*/
j =*p; //等价于 j = i;
printf("i= %d, j = %d\n", i, j);
指针和指针变量的关系
l 指针就是地址,地址就是指针
l 地址就是内存单元的编号
l 指针变量是存放地址的变量
l 指针和指针变量是两个不同的概念
l 但是要注意: 通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样
为什么使用指针
指针的重要性
直接访问硬件 (opengl显卡绘图)
快速传递数据(指针表示地址)
返回一个以上的值(返回一个数组或者结构体的指针)
表示复杂的数据结构(结构体)
方便处理字符串
指针有助于理解面向对象
如何用基本类型的指针
Int * p
Double * p
指针常见错误
# include<stdio.h>
intmain(void)
{
int * p;
int i = 5;
*p = i; //应该是p=&i
printf("%d\n", *p);
return 0;
}
例子2
int main(void)
{
inti = 5;
int* p;
int* q;
p= &i;
//*q= p; //error 语法编译会出错
//*q= *p; //error
p= q; //q是垃圾值,q赋给p, p也变成垃圾值
printf("%d\n",*q); //13行
/*
q的空间是属于本程序的,所以本程序可以读写q的内容,
但是如果q内部是垃圾值,则本程序不能读写*q的内容
因为此时*q所代表的内存单元的控制权限并没有分配给本程序
所以本程序运行到13行时就会立即出错
*/
return0;
}
介绍一下野指针. 蓝屏
错误3:
int main(void)
{
int* p;//等价于int *p;int* p;
inti=5;
charch = A;
//错误一 char ch ='A'
//错误二 p=&ch;
return0;
}
互换两个数字
检测实参形参是不是同一个参数
经典指针程序交换两个数组
#include <stdio.h>
int main(void)
{
inta =3;
intb =5;
swap(&a,&b);
printf("a=%d,b=%d",a,b);
return0;
}
void swap(int a, int b){ //数值传递,没有变
intt;
t= a;
a= b;
b= t;
}
void swap(int * p ,int * q){ //引用传递,改变了
intt;
t=*p;
*p= *q;
*q= t;
}
//C++中用&表示引用传递,效果和指针传递一样,还更简洁。 void swap(int &x, int &y) { int temp; temp = x; /* 保存地址 x 的值 */ x = y; /* 把 y 赋值给 x */ y = temp; /* 把 x 赋值给 y */ return; }
*号的三种含义
1.乘法3*5
2.定义指针变量 int * p;//定义了一个名字叫p的变量,能够存放int数据类型的地址
3.指针运算符,
//如果p是一个已经定义好的指针变量
则*p表示以p的内容为地址的变量
函数返回一个以上的值
void f(int *p ,int *q)
{ *p =1; //指针重新指到1;
*q=2;
}
Int main(void)
{ int a =3,b=5;
f(&a,&b);
Printf(“%d %d \n”,a,b); // 结果a=1,b=2
Return 0;
}
通过被掉函数修改主调函数普通变量的值
1.实参必须是普通变量的地址
2.形参必须是指针变量
3.被掉函数中通过修改 *形参名的方式修改主调函数相关变量的值
指针和数组的关系
数组名,下标和指针的关系,指针变量的运算
数组名
int a[5] //a是数组名,5是数组的大小,元素个数
int a[3][4] // 3行4列 a[0][0]就是数组的第一个元素
Int b[5]
A=b ;//错误
一维数组名是个指针常量,它存放的是一维数组第一个元素的地址
int a[5];
inta[3][4];
printf("%#X\n",&a[0]);
printf("%#X\n",&a);
下标和指针的关系
如果p是个指针变量则
p[i]等价于 *(p+i)
确定一个一维数组
确定数组需要两个参数及其原因
# include <stdio.h>
//f函数可以输出任何一个一维数组的内容
void f(int * pArr, int len)
{
inti;
for(i=0; i<len; ++i)
printf("%d ", *(pArr+i) ); //*pArr *(pArr+1) *(pArr+2)
printf("\n");
}
int main(void)
{
inta[5] = {1,2,3,4,5};
intb[6] = {-1,-2,-3,4,5,-6};
intc[100] = {1, 99, 22, 33};
f(a,5); //a是 int *
f(b,6);
f(c,100);
return0;
}
/* 一定要明白 pArr[3]和a[3] 是同一个变量
*/
# include <stdio.h>
void f(int * pArr, int len)
{
pArr[3]= 88;
}
int main(void)
{
int a[6] = {1,2,3,4,5,6};
printf("%d\n", a[3]);
f(a, 6);
printf("%d\n", a[3]);
return0;
}
指针的运算
# include<stdio.h>
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;
}
指针占用几个字节
# include<stdio.h>
int main(void)
{
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));
return 0;
}
动态内存分配问题
传统数组的缺点
1.数组长度必须实现指定,
并且只能是常整数.
inta[5];
intlen;
inta[len];//error
2.传统形式的数组,程序员没法手动释放空间
数组一旦定义,系统为该数组分配的空间一直存在
函数运行完毕,数组的空间就被释放
3.数组的长度不能在函数运行中动态增加或者缩小
4.A函数定义的数组只有在A没执行完毕前被使用,a函数运行完毕后,a的数组就无法被其他函数使用
#include<stdio.h>
void g(int * arr){
arr[2]=2;
}
void f(){
int a[] ={1,2,3,4,5};
g(a);
printf("%d",a[2]);
}
int main(){
f();
}
创建动态数组
Malloc 函数
realloc re- allocate
# include<stdio.h>
# include<malloc.h> //不能省 malloc是 memory(内存) allocate(分配)的缩写
int main(void)
{
int i = 5; //分配了4个字节 静态分配 11行
int * p = (int *)malloc(4); //12行
/*
1. 要使用malloc函数,必须添加malloc.h这个头文件
2. malloc函数只有一个形参,并且形参是整型
3. 4表示请求系统为本程序分配4个字节
4. malloc函数只能返回第一个字节的地址
5. 12行分配了8个字节, p变量占4个字节, p所指向的内存也占4个字节
6. p本身所占的内存是静态分配的, p所指向的内存是动态分配的
*/
*p = 5; //*p 代表的就是一个int变量, 只不过*p这个整型变量的内存分配方式和11行的i变量的分配方式不同
free(p); //freep(p)表示把p所指向的内存给释放掉 p本身的内存是静态的,不能由程序员手动释放,p本身的内存只能在p变量所在的函数运行终止时由系统自动释放
printf("大家好!\n");
return 0;
}
用主函数外部方法修改主函数申请的一块内存中的值
补充写void f() 函数
#include<stdio.h>
#include <malloc.h>
void f(int * p)
{ *p = 20;
// free(p); //free(p) 会对第二次打印*p产生什么影响
}
int main(void)
{ int i =5;
int * p = (int *) malloc(sizeof(int));
*p = 10;
printf("%d\n",*p);
f(p);
printf("%d\n",*p);
system("pause");
}
问 free(q) 能不能在 f()里面写
静态内存和动态内存的比较
动态创建一个动态大小的数组,赋值打印,释放空间
#include<stdio.h>
#include<malloc.h>
int main(void)
{
int * pArr;
printf("请输入你要存放的元素的个数:");
int len;
scanf ("%d",&len);
pArr=(int*)malloc(sizeof(int)*len);
for(int i= 0; i<len;i++)
{ scanf("%d",&pArr[i];
}
for(int i= 0; i<len;i++)
{
printf("%d\n",*(pArr+i));
}
system("pause");
return 0;
}
静态内存和动态内存
静态内存是系统是程序编译执行后系统自动分配,由系统自动释放,静态内存是栈分配的.动态内存是堆分配的.
(1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
(2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多.
堆和栈的区别:
1.申请方式
栈:
由系统自动分配.例如,声明一个局部变量int b; 系统自动在栈中为b开辟空间.例如当在调用涵数时,需要保存的变量,最明显的是在递归调用时,要系统自动分配一个栈的空间,后进先出的,而后又由系统释放这个空间.
堆:
需要程序员自己申请,并指明大小,在c中用malloc函数
如char* p1 = (char*) malloc(10); //14byte
但是注意p1本身是在栈中的.
2 申请后系统的响应
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时, 会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
3.申请大小的限制
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(vc编译选项中可以设置,其实就是一个STACK参数,缺省2M),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
4.申请效率的比较:
栈:由系统自动分配,速度较快。但程序员是无法控制的。
堆:由malloc/new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
5.堆和栈中的存储内容
栈:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。
6.内存的回收
栈上分配的内存,编译器会自动收回;堆上分配的内存,要通过free来显式地收回,否则会造成内存泄漏。
堆和栈的区别可以用如下的比喻来看出:
使用栈就像我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就像是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。
静态变量不能跨函数使用
# include<stdio.h>
void f(int ** q)//q是个指针变量,无论q是什么类型的指针变量,都只占4个字节
{
int i = 5;
//*q等价于p q和**q都不等价于p
//*q = i; //error 因为*q = i; 等价于 p = i; 这样写是错误的
*q = &i; // p = &i;
}
int main(void)
{
int *p; //13行
f(&p);
printf("%d\n", *p); //16行 本语句语法没有问题,但逻辑上有问题
return 0;
}
多级指针
用来存指针变量的地址
int main(void)
{
int i=10;
int * p=&i;
int ** q= &p;
int *** r = &q;
把i打印出来
return 0;
}
函数的指针
1.定义int (*pf)(int x, int y);
2.赋值 pf = add;
3.引用 pf(3,5);
#include<stdio.h>
#include<stdlib.h>
int max(int a, intb)
{
return a>b ? a:b;
}
main(){
int a=10;
int b=11;
int(*pfun)(int x,int y);
pfun=max;
printf("%d\n", pfun(a,b));
system("pause");
}
结构体
#include<stdio.h>
#include<stdlib.h>
void study(){
printf("do something");
}
//定义一个结构体
struct student{
int age;
int height;
char sex;
//结构体中不能定义函数,但可以定义函数指针;
void (*studyP)();
}
main(){
//定义结构体的变量
struct student st = {20, 180, 'm', study};
printf("%d\n", st.age);
printf("结构体的长度%d\n", sizeof(st));
//调用函数指针的三种写法
st.studyP();//变量调用
struct student* stp = &st;
(*stp).studyP();//指针调用
stp->studyP();//指针调用
system("pause");
}
第一种
struct Student{
int age;
float score;
}
第二种
struct Student2{
body
} st2;
第三种
struc{
body
} st3
联合体
共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。
#include<stdio.h>
main( )
{
struct date { intyear, month, day; }today;
union { long i;int k; char ii; } mix;
printf("date:%d\n",sizeof(structdate));
printf("mix:%d\n",sizeof(mix));
}
mix.I mix .kmix.ii共用相同的地址
#include<stdio.h>
main()
{
struct date { int year, month, day;}today;
union { long i; int k; char ii; double d;} mix;
printf("date:%d\n",sizeof(struct date));
printf("mix:%d\n",sizeof(mix));
mix.i = 33;
mix.ii = 'a';
printf("i=%d\n",mix.i);
//110100000101
system("pause");
}