时间:2022-12-27 10:18:56 | 栏目:C代码 | 点击:次
接着上次的函数的基本知识,今天我们来讲一讲指针
所谓指针,也就是内存的地址;所谓指针变量,也就是保存了内存地址的变量。
总结:指针就是变量,用指针存放地址(口语中说的指针通常指的是指针变量)
指针变量就是一个变量,它存储的内容是一个指针。在我们定义一个变量的时候,要确定它的类型。在定义指针变量时也是一样的,必须确定指针类型。int 变量的指针需要用 int 类型的指针存储,float 变量的指针需要用 float 类型的指针存储。
指针类型决定了:指针解引用的权限有多大与指针走一步走多大字节(步长)
整型指针+1跳过一个整型。字符指针+1跳过一个字符
(1)指针变量的定义
指针变量的定义形式如:数据类型 *指针名;例如:
int* x;//整型指针变量 float* f;//浮点型指针变量 char* ch;//字符型指针变量
指针的变量名分别是:x;f和ch。而int*;float*与char*分别是他们中存储的数据的类型。
(2)指针变量的使用
取地址运算符&:单目运算符&是用来取操作对象的地址。例:&i 为取变量 i 的地址。对于常量表达式、寄存器变量不能取地址(因为它们存储在存储器中,没有地址)。
指针运算符*(解引用运算符 ):与&为逆运算,作用是通过操作对象的地址,获取存储的内容。例:x = &i,x 为 i 的地址,*x 则为通过 i 的地址,获取 i 的内容。
//声明了一个普通变量 a int a; //声明一个指针变量,指向变量 a 的地址 int *pa; //通过取地址符&,获取 a 的地址,赋值给指针变量 pa = &a; //通过间接寻址符,获取指针指向的内容 printf("%d", *pa);
(1)指针与整数的加减运算
指针变量的自增自减运算。指针加 1 或减 1 运算,表示指针向前或向后移动一个单元(不同类型的指针,单元长度不同)。
(2)指针减指针
前提是两个指针指向同一个空间。
指针减指针得到两个指针间的元素个数
例: Arr[9]指向9与10中间部分(下标)
野指针就是指针指向的为止是不可知的(随机的,不正确的,没有明确限制的)
(1)指针未初始化
(2)指针越界
如图:在第十次 访问时,可以进入到循环,相当于从第10个点向后访问4个字节,之后的字节不属于原数组,则越界了,则称为野指针。(只有在超出范围后,在会发生崩溃)
//越界访问 *int arr[10] ={ 0 }; int* p = arr; int i=0; for (i=0; i <= 10; i++)//共执行了11次,而arr数组总共只有10个元素 { *p = i; p++; }
(3)指针指向的空间释放
int* test()//局部变量 { int a = 10; return &a; } int main() { int *p=test();//返回值是a的地址,而局部变量在引用之后就会销毁。 *p = 20; return 0; }
1.指针初始化
2.小心指针越界。
3.指针指向空间释放即置NULL
4.指针使用之前检查有效性。
在指针的类型中我们知道有一种指针类型为字符指针 char* ; 一般使用:
int main() { const char* pstr = "hello ";//这里是把一个字符串放到pstr指针变量里了吗? printf("%s\n", pstr); return 0; }
本质是把字符串 hello首字符的地址放到了pstr中。
int main() { char str1[] = "hello bit."; char str2[] = "hello bit."; const char *str3 = "hello bit."; const char *str4 = "hello bit."; if(str1 ==str2) printf("str1 and str2 are same\n"); else printf("str1 and str2 are not same\n"); if(str3 ==str4) printf("str3 and str4 are same\n"); else printf("str3 and str4 are not same\n"); return 0; }
最终输出的结果是
这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域。当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。 但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同。
之前我们可以通过下标访问数组元素,学习了指针之后,我们可以通过指针访问数组的元素。在数组中,数组名即为该数组的首地址,结合上面指针和整数的加减,我们就可以实现指针访问数组元素。
(1)二维数组的地址
int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; int *p=a[0];//此处的p内存放的数组a第一行的地址。
(1)多级指针(指向指针的指针)
指针变量作为一个变量也有自己的存储地址,而指向指针变量的存储地址就被称为指针的指针,即二级指针。依次叠加,就形成了多级指针。
指针变量也是一种变量,也会占用存储空间,也可以使用 &获取它的地址。C语言不限制指针的级数,每增加一级指针,在定义指针变量时就得增加一个星号 *。p1 是一级指针,指向普通类型的数据,定义时有一个 *;p2 是二级指针,指向一级指针 p1,定义时有两个*。例:假设有一个 int 类型的变量 a,p1是指向 a 的指针变量,p2 又是指向 p1 的指针变量,它们的关系如下图所示:
1.parr的第一个数组里放arr1的首元素地址
2.parr每个元素的类型是int*。
3.该数组里每个元素都是指针,所以它是一个指针数组
4.Parr[i][j],也可以写成parr[i]+j,
5.相当于地址+j为向后挨个元素指向
如以下语句:
int (*p)[10]; //解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。 //所以p是一个指针,指向一个数组,叫数组指针。 //这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。
让我们看一段代码
#include <stdio.h> int main() { int arr[10] = { 0 }; printf("arr = %p\n", arr); printf("&arr= %p\n", &arr); printf("arr+1 = %p\n", arr+1); printf("&arr+1= %p\n", &arr+1); return 0; }
运行结果如下
根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是意义应该不一样的。
实际上: &arr 表示的是数组的地址,而不是数组首元素的地址。(细细体会一下)
本例中 &arr 的类型是: int(*)[10] ,是一种数组指针类型。
数组的地址+1,跳过整个数组的大小。
#include <stdio.h> void print_arr1(int arr[3][5], int row, int col)//形参用数组来接受 { int i = 0; for(i=0; i<row; i++) { for(j=0; j<col; j++) { printf("%d ", arr[i][j]); }}} void print_arr2(int (*arr)[5], int row, int col)//形参用数组指针来接受 { int i = 0; for(i=0; i<row; i++) { for(j=0; j<col; j++) {printf("%d ", arr[i][j]);} printf("\n");}} int main() { int arr[3][5] = {1,2,3,4,5,6,7,8,9,10}; print_arr1(arr, 3, 5); //数组名arr,表示首元素的地址 //但是二维数组的首元素是二维数组的第一行 //所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址 //可以数组指针来接收 print_arr2(arr, 3, 5); return 0; }
returnType (*pointerName)(param list);
其中,returnType 为函数返回值类型,pointerNmae 为指针名称,param list 为函数参数列表。参数列表中可以同时给出参数的类型和名称,也可以只给出参数的类型,省略参数的名称,这一点和函数原型非常类似。 注意( )的优先级高于*,第一个括号不能省略,如果写作returnType *pointerName(param list);就成了函数原型,它表明函数的返回值类型为returnType *。
int (*pf[4])(int,int)={Add,Sub,Mul,Div};
函数指针数组,在函数指针的基础上,将指针变为指针数组
以上就是初阶指针的基本内容了!!!!非常感谢你能看到这里!
如果你觉得你有些想法和我一样,想和我一起提升自己可以关注私信我,与我一起进步,一起共同努力!!!!!