- 指针
-
指针变量:
- 指针的实质就是变量。跟普通的变量没有任何本质区别。
- 指针变量存储的是另外一个变量的地址。
- 指针是一个值为内存地址的变量(数据对象),正如char类型变量的值是字符,int类型变量的值是正整数,指针变量(p)的值是地址,而对应的*p表示指针变量所对应地址内存储的变量x(具体值的数据类型由定义决定)。
- printf中%p的实质即为将所指向的数据地址以十六进制的形式输出。
- 指针的标准使用方式:
- ①定义指针变量;int a=89; int *p;
- ②给指针变量赋值(绑定指针),让指针指向一个可以访问的变量; P=&a;
- ③解引用。 *p=x 将x放入p指向的变量之中
- 示例代码:
- int a=23;
- int *p; //指针的定义
- printf("p=%d.\n",*p );
- //输出p所绑定的地址(用16进制数表示),且随机,每次输出均不同
- p=&a; //p表示a的地址
- printf("p=%d.\n",*p ); //输出23 指针的解引用
- p=&a;
- *p=555;
- printf("p=%d.\n",*p ); //输出555
- return 0; //常规做法:定义指针,将指针和变量a绑定,
- //解引用指针,并给指针赋一个值。
- int *p代表定义一个指针类型的变量p(即int * 为p的变量类型)
- 指针的初始化:int a=32; int *p =&a;
- 与指针先定义后赋值:int a=32; int *p; p=&a;
- 以上的两种定义方法效果相同。
- 左值与右值:
- 当一个变量做左值时,编译器会认为这个变量符号的真实含义是这个变量所对应的内存空间,
- 当一个变量做右值时,编译器会认为这个变量符号的真实内容是这个变量所对应的内存空间内存储的数值,
- 野指针:
- 指针定义之后没有初始化,也没有赋值,即指针没有明确指向一个可用的内存空间,指针指向的位置是未知的。
- 野指针分为三种情况:指向一个不可访问(例如内核空间);
- 指向一个可用的,但是没有什么特别意义的空间(例如使用过的栈空间);
- 指向了一个可用的空间,而且这个空间在程序中正在被使用,那么指针的解引用就会刚好修改原本变量的值,导致程序内的值莫名被改变。
- 解决方法:在指针解引用之前一定要保证指针指向一个绝对可用的空间。
- 防治方法:第一点:在定义指针时,同时初始化为NULL;
- 第二点:在解引用之前判断其是不是NULL;
- 第三点:使用完将其赋值为NULL;
- 即:不用的时候就将其赋值为NULL.
- NULL 的定义:#ifdef _cplusplus
- #define NULL 0 //在c++中NULL就是0
- #else
- #define NULL(void *0); //在c中NULL是强制类型转换为void *的0
- #endif
- NULL 的实质就是0。0地址作为一个特殊地址,指向他的指针没有被初始化,且0地址在一般的操作系统中是不可被访问的。
- const关键字与指针
- const 用来修饰变量,表示这个变量为常量
- 1. const int *p //表示*p为是const的
- 2. int const *p //表示*p为是const的
- 3. int * const p //表示p本身为const 而p指向的变量(*p)不是const
- 4. const int * const p //p本身是const的,而p指向的变量也是const的
- 数组 int a[10]
- 1.a做右值时代表数组的首元素的首地址,等同于&a[0],都属于元素指针类型。
- &a做右值时表示整个数组的首地址,属于数组指针类型
- 2. a做左值时代表整个数组的所有空间(4*10=40字节),所以a不能做左值
- &a是一个常量,不能做左值
- 3. a和&a[0]做右值时意义和数值完全相同,可以互相替换。
- 数据类型:C语言数据类型的本质就是决定了这个数在内存之中是怎么储存的问题,也决定了这个数怎么转 换成二进制的问题。
- 指针的数据类型:指针涉及两个变量,一个是指针变量自己本身,一个是指针变量指向的那个变量。
- sizeof是一个运算符而非一个函数,作用是用来返回()内的变量或者是数据类型占用的内存字节数。
- (在不同的系统平台内,各种数据类型所占据的内存字节数不尽相同)
- strlen()是C语言内一个库函数,用来返回一个字符串的长度(不计算末尾的\0)
- ******************************************************************************
- 函数传参,形参是可以用数组的, 函数形参是数组时,实际传递的不是整个数组,而是数组的首元素的首地址,也就是函数传参用数组来传,实际上相当于传递的是指针(指针指向数组的首元素的首地址)。
- 当形参为数组时:可以将数组的地址和数组内字节数都作为形参传递,例如int a[10];
- void func(int *a,int num) 则*a为数组的首元素的首地址,num为10
- sizeof(a)/sizeof(a[0])可以用来算出数组内的元素个数。
- 指针与函数参数:
- 形参是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数
- 实参是调用时传递给函数的参数。
- 数组名做形参传递的时候,仅仅调用的是数组首元素的首地址,
- 数组作为函数形参的时候,[]里面的数字可有可无,因为数组名做形参实际上只是一个指针,根本没有数组长度这个信息。
- 传值调用:变量作为实参时,自己真身并没有进入函数内部,而只是拷贝了自己的副本进入函数, 但是在函数外部的变量自己本身没有发生任何变化。
- 传址调用:将变量以其指针的形式作为实参传进函数的内部,这时通过改变不同变量的地址,从根 本改变变量的值。操作完成后函数外部变量的值就会发生变化。
- 所谓的传值和传址都是传值,只是在传地址时传递的是变量的指针即地址而不是变量自身的值。