指针的概念
指针顾名思义就是起指向作用的,通常用来指向需要查找的某个地址,一般在计算机中存储内容都需要开辟一块内存,有了内存就引出一个叫做地址的名词,就像我们有家庭住址一样,内存就是房子,地址就是房子在的位置。
指针变量
在c语言中,数据由常量和变量组成,代表指针的变量就叫做指针变量,像其他变量一样,指针变量也分为整型和字符型等,例如整形指针变量的表示方法为int*,,int是指针的类型,*表示该变量是一个指针。还有void*,可以理解为无具体类型的指针,例如一个函数的形参是指针,但又不知道具体的类型,就可以利用void*来接收。
解引用操作符
要注意的是,“*”不仅仅可以表示上述的作用,还是一个解引用符,用于解开一个指针变量,获取到其中存储的内容,,就相当于一把钥匙,打开了指针变量。但是不同类型的指针变量解引用之后获取的权限是不一样的,字符型指针char*只能访问一个字节,整形指针int*可以访问四个字节。
const变量
我们知道,使用const修饰可以使变量变为常量,不可被修改,可以起到保护的作用。那么,在指针变量这里是否也可用呢?
void test1()
{
int m=10;
int n=10;
int* p=&m;
*p=20;
p=&n;
}
void test2()
{
int m=10;
int n=10;
const int* p=&m;
*p=20;
p=&n;
}
void test3()
{
int m=10;
int n=10;
int* const p=&m;
*p=20;
p=&n;
}
int main()
{
test1();
test2();
test3();
return 0;
}
利用上面的代码,我们可以知道当const在int*前修饰时,修饰的是指针的内容,即上面代码中p的值依然是10,但是不修饰指针变量,所以p=&m变为&n。
相反的,const修饰int*后面时,修饰的是指针变量,但是值依旧可以改变。
野指针
野指针是指未初始化,或者指针指向的内容越界访问了,,又或者指针指向的内容被释放了,都会产生野指针,当我们不知道指针指向的内容时,我们可以使其等于NULL,即空指针。当然,我们也可以使用assert断言。
数组
数组也是指针的一种形式,其中数组名就是地址,通常是指向数组的第一位元素,当然也有例外,当sizeof(数组名)或者&数组名时,数组名表示的是整个数组的地址。
那么怎么利用指针来访问数组中的元素呢?*(arr+i)就等价于arr[i],可以进行打印等操作。
一维数组传参的本质
上面我们了解到了数组名通常情况下是表示数组首元素的地址,而当一个函数的参数为数组时,传递的是数组首元素的地址,而不是整个数组的地址,所以无法确定出数组中含有的元素个数。
二维数组传参的本质
二维数组传参的本质也是传递地址,可以将二维数组看做i个一维数组组成,二维数组传参就是将一维数组的首位元素的地址,所以可以用指针代替,arr[i][j]等价于*((*p+n)+m),m,n是常数。
指针数组
顾名思义,指针数组其实是一个数组,但是数组包含的内容是指针,就是说一个数组内部的元素是指针,而我们知道,指针可以指向一片区域,所以我们可以利用这一点模拟出二维数组。
函数指针数组
形式:int (*parr[])(int x,int y).
类型指针变量
字符指针变量
char* p="hello world"
上述的字符指针指向的是整个字符串还是“h” 呢?答案是首元素“h”
数组指针变量
例如int(*p)[10]是一个整型的数组指针变量,因为[]的优先级比*更高,所以需要使用()
初始化:
int arr[10]={ 0 };
int (*p)[10]=&arr;
函数指针变量
函数指针变量是用来存放函数地址的,以后调用函数就可以直接解引用地址。
形式为:int (*fun) (int x,int y);
strlen和sizeof的对比
sizeof是计算变量所占内存空间的大小,单位是字节,也可以计算类型的大小。是一个操作符。不关注内存中存放什么数据。
strlen是求字符串长度的库函数,统计的是\0之前字符串中字符的个数,所以使用需要包含头文件<string.h>