C语言 指针

0719

第七章 指针

一、指针的定义

指针就是地址编号。

二、指针变量

基本格式:type* 指针变量名;(type:基类型。egintchar。基类型:指针变量指向的变量的类型)

32位机每个指针变量在内存中占4字节;64位机每个指针变量在内存中占8字节。

例:1) int a=10;int* p;p=&a;指针变量p“指向变量a。(&a=int*

2) int a;char c;char* q;q=&c;指针变量q”指向变量c。(&c=char*

3) int* * p;(二级指针变量:指向一级指针变量的指针变量)(int*就是基类型。p表示指针变量名。)

int* q;(一级指针变量)

int a=0;

p=&q;q=&a;

三、使用指针变量

int a=10;int* p=&a;//指针变量p”指向变量a

printf(“%d,%d\n”,a,*p);//a *p *:解地址操作符。

a=20;

*p=30;//*:获得指针变量指向的变量。

*p<=>*(&a)<=>a//but:*p不代表变量名。

四、空指针,野指针

空指针:值为NULL的指针变量

野指针:指向不明确的指针变量

空指针:int* p;p=NULL;//不能对空指针解地址。如果对空指针解地址一般会是系统崩溃。

不能对空指针解地址的原因:因为权限问题,没有读写的权限。

野指针:int* q;*q=10;//如果对野指针结地址一般会出现乱码。


0720

五、指针变量的特点:值可以修改(指向可以变化);指针变量需要指向特定类型的数据;多个指针变量的指向可以相同。

//通过指针实现两个变量交换

void exchange1(int *x,int *y){

    *x=*x+*y;

    *y=*x-*y;

    *x=*x-*y;

    printf("%d,%d\n",*x,*y);

}

局部标识符:形参 函数体 

全局标识符:不属于任何函数体的标识符(全局变量名 全局函数名)

局部变量分配的存储空间在函数结束时回收。

全局变量分配的存储空间在main()函数结束时回收。


int *smaller(int x,int y)

{

    return x<y?,&x,&y;//返回的是野指针。

}

函数返回指针时:不能返回被调用函数中(普通)局部变量的指针(指针->野指针)。


static静态局部变量:只有在程序执行结束时才被回收释放。

计算机的内存分为五个分区:栈区(手动分配和回收),堆区(手动分配和回收),静态/全局数据区(只有在程序执行结束时才会回收),文字常量区(用双引号包围起来的字符串),代码区。


例:

int a=5,*p,**q;

p=&a;

q=&p;

//*p<=>a; *q<=>p<=>&a; **q<=>*p<=>a;

printf(“%d,%d,%d\n”,a,*p,**q);

六、指针和数组:

数组名:保存数组的首地址,即数组首元素地址。index:数组下标

a+index<=>&a[index]

*(a+index)<=>a[index]

例:int a[]={1,2,3,4};int *p;

   printf(“%p,%p\n”,a,&a[0]);//输出结果一样,都是数组的首地址。

   p = a;//*p=1;

   p = a+1;//*p=2;

   p++;//相当于:p=p+1;指针变量p“指向了下一个元素。

//a[0]<=>*(a+0);a[1]<=>*(a+1);

//指针变量加n,表示指针变量向下移动n个元素;指针变量减n,表示指针变量向上移动n个元素。

图解:

指针变量之间也可以做减法。得到的是两个指针变量相差的元素个数。(两个指针不可以做减法)

例:int a[5]={1,2,3,4,5};

int *p,*q;

p=a;

q=a+3;

printf(“%d\n”,q-p);//输出3

图解:

0725

七、内存分配

静态内存分配:在编译时有系统完成。

动态内存分配:在运行时完成,动态内存的分配与释放需要占用CPU资源,有程序员决定何时分配以及分配的空间大小。不需要预先分配存储空间

内存分为5个分区:堆、栈、全局/静态存储区、常量存储区和代码区。

常量区:在程序结束时回收

全局/静态存储区:在程序结束时回收

  C语言中:堆:有程序员手动申请、手动回收。

malloc函数。需包含stdlib.h。

作用:申请堆区空间。

函数原型:void * malloc(size_t size);

返回值:申请堆空间成功,返回堆空间的首地址;申请堆空间失败,返回NULL

参数:申请的堆空间大小。(以字节为单位)

堆区空间都是动态分配的,没有静态分配的堆。

堆区空间没有名字,通过指针操作堆区空间。

free函数。

作用:回收堆区空间

函数原型:void free(void *);

参数:即将被回收的堆区空间首地址。

malloc>free:内存泄漏。//内存不足

malloc<free:二次删除。//导致程序崩溃

int *p;

p=(int *)malloc(20);//分配存储空间20字节。

for (int i=0; i<5; i++) {

    *(p+i)=10;

    //p[i]=20;

}

free(p);//并不是释放p,而是释放指针p“指向”的堆区空间。

    C++:new:申请堆空间

1)int *p = new int;//new 类型;

2)int *p = new int(5);//new  类型(值);值:用于初始化堆空间

3)int *p = new int[10];//new 类型[元素个数];申请数组堆空间

int a[10];//int[10]

C++:delete:回收堆空间。delete 堆空间的首地址;

1)delete p;//回收指针变量p“指向”的堆空间;p变成野指针。回收堆空间后一般会初始化指针p=NULL;

2)delete p;//同上

3)delete  []p;//回收指针变量p“指向”的数组堆空间。

C++工程:#include<iostream>//输入输出流

using namespace std;//使用命名空间std

char* p = new char;

*p = 'a';

int* q = new int(5);//即*q=5

delete p;//p:野指针

delete q;//q:野指针

p = NULL;

q = NULL;

int* pp = new int[10];

for (int i=0; i<10; i++) {

    //*(pp+i) = i;

    pp[i] = i;

}

delete [] pp;

pp = NULL;

八、常量-指针(const修饰谁,谁就不可以被修改)

1)int* const p;指针常量//const修饰p:表示p不能被修改。

2)const int* p;指向常量的指针,即常量指针//const修饰 *p:表示*p不能被修改,不能通过指针间接的修改“指向”的数据。

3)int const *p;//同上

例:int a=10;

    int b=9;

    const int *p=&a;

    //*p=10;//error

    p=&b;

    int* const q=&a;

    *q=10;

    //q=*b;//error  

    int const *pp=&a;

    //*pp=10;

    pp=&b;

九、引用:(就是别名)& (本质:不占用内存空间)

int b = 10;

int & a=b;//&:引用符

引用:只能在声明的时候初始化。


0726

十、变量存储方式

左值=右值

变量作用域:

文件域:在所有方法外声明的全局标识符,作用范围是整个文件

函数域:在一个函数原型中声明的标识符,作用范围是整个函数

语句块域:在一个程序结构块声明的标识符,作用范围是该结构块

局部变量和全局变量同名

(在C++源文件支持)全局域操作符::(只有在调用全局变量是才可以使用“::”)

当在块内部声明一个与外部标识符(变量名)同名的标识符时,外部标识符可以被临时隐藏。

如果有同名的局部变量被访问时:就近原则。

extern:定义在一个源文件中的全局变量或全局函数可以被另一个源文件引用,需要使用extern关键字。(首先包含另一个源文件的头文件)如:hello.h中有void fun();则在main.c文件中#include”hello.h”(回车)extern void fun();

存储类型:

局部变量和函数形参坐在内存空间:栈。

栈上的变量称为自动变量。

自动变量的内存空间的开辟和回收:自动变量定义时,由系统开辟内存空间;自动变量离开作用域时,系统自动回收内存空间。

静态变量:

静态局部变量:局部变量声明时前加static关键字。

1)函数返回后,静态局部变量的内存空间不会被回收;

2)当下一次调用该函数时,静态局部变量依然保持上一次调用退出时的值;

3)静态局部变量的作用域不变,依然是在函数内部;

4)静态局部变量在内存中,保存在静态存储区;

5)静态存储区里的变量生命周期,是整个程序运行期间。

注意:相对于普通局部变量,静态局部变量的作用域相同,生命周期不同。

静态全局变量:全局变量声明时前加static关键字。

1)生命周期不变,依然是整个程序运行期间;

2)静态全局变量在内存中,保存在静态存储区;

3)作用域缩小:只限于当前的源文件,不能被其他源文件引用,不能加extern关键字。

注意:相对于普通全局变量,静态全局变量生命周期不变,作用域缩小


十一、参数传递和返回值

参数传递的三种方式:

1)传值:形参是实参的一份拷贝,单向传递,形参的改变不会影响实参;

2)传指针:通过形参简介改变实参所指向的变量的值;

3)传引用:形参就是实参,改变形参就是改变实参;

函数返回值:

一个函数可以返回一个值、一个指针或一个引用

如果传值返回,编译器会创建一个临时变量来接受返回值

注意:不能返回指向局部变量的指针或引用。

不能返回引用的原因:(引用就是别名,不单独占用内存空间)(&表示:不用给返回值分配存储空间了)

当返回引用:被调函数调用结束时,普通局部变量的空间会被回收

例:

1)int test(){static int temp = 20;return temp;}//返回temp的值给一个临时变量来存储。

2)int& test(){static int temp = 20;return temp;}//返回temp的值,并不需要临时变量。直接用temp返回。(temp是静态变量)

在主函数中调用test();

int main(){

1)test()=10//error;原因:test()表示临时变量,“=”左值必须是变量。

2)test()=12//正确;test()就表示静态变量temp。temp是变量,所以test()=12即为temp=12

return 0;}

十二、函数指针

函数指针:指向函数的指针变量。(”函数指针”本身就是指针变量,只不过该指针变量指向函数)

函数指针的声明方法:返回值类型(*指针变量名)([参数列表]);例:int (*f)(int x);//声明一个函数指针

函数指针的使用:

int (*pointer)(int,int);//函数指针要和他指向的函数的参数类型、返回值类型一致

int add(int,int);

int sub(int,int);

int main()

{

pointer = add;//函数名本身就是指针(函数在内存里的地址)。就像数组名本身就是指针。

printf(“%d\n”,pointer(4,5));//等效于add(4,5)

pointer=sub;//改变指向

printf(“%d\n”,pointer(6,2));//等效于sub(6,2)

return 0;

}

int add(int a,int b){

return(a+b);

}

int sub(int a,int b){

return(a-b);

}

十三、void* 指针

void* 指针即空类型指针,指针指向的是void类型,空类型指针可以转型为其他类型的指针。注意:不能对void*类型指针解引用。

例:

void f(void* v){

//int *p=(int*)v;//(int*)强制类型转换

//printf(“*p = %d\n”,*p);

printf(“*p = %p\n”,v);//返回的是v的地址

}

int main()

{

int a=10;

float b=2.4;

char c=‘a’;

f(&a);

f(&b);

f(&c);

return 0;

}

补充:与void*相似,通用类型:c++:auto c#:var oc:id

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值