指针:指针地址或指针变量
指针的意义:
1.指针类型决定了:(1)指针解引用的权限有多大(能操作多少数);(2)指针+1或-1的步长为多少
#include <stdio.h>
int main()
{
int* p;
int a[5] = { 1,2,3,4,5 };
char* p1;
p1 = a;
p = a;
printf("%p\n", p);
printf("%p\n", p + 1);
printf("%p\n", p1);
printf("%p\n", p1 + 1);
return 0;
}
结果如下:
00AFF718
00AFF71C
00AFF718
00AFF719
2.野指针
指针指向的地址随机,不确定
形成原因:(1)指针为初始化(默认为随机值);
int *p;
*p=10;//非法访问内存,运行提示出错
(2)指针越界;(导致程序崩溃,无法运行)
int a[5] = { 1,2,3,4,5 };
int* p;
int i;
p = a;
for (i = 0;i <= 5;i++)//指针越界
{
*p = i;
p++;
}
(3)指针指向的空间释放;
int * text()
{
int a = 10;
return &a;
}
int main()
{
int* p = text();
*p = 20;
return 0;
}
其中text函数中的a作为局部变量离开被调函数就被释放。
如何规避使用野指针:
(1)指针初始化
(2)小心指针越界
(3)指针指向空间释放就置NULL
int *p=NULL;//实际上就是0;表示指针未指向任何存储空间
(4)指针使用前检验有效性
if(p!=NULL)
*p=10;
二、指针的运算
1.指针±整数
int a[3]={1,2,3};
int *p=a;
p++;
p--;
2.指针-指针(得出结果为指针与指针之间的元素个数)
int a[5] = { 1,2,3,4,5 };
printf("%d\n", &a[4] - &a[0]);
两个指针需要在同一内存空间进行;
模拟strlen函数:
int strlen(char* st)
{
char* p = st;
while (*st != '\0')
{
st++;
}
return st - p;
}
int main()
{
char a[] = "abc";
char* str = a;
printf("%d", strlen(a));
return 0;
}
3.指针的关系运算
int a[5] = { 1,2,3,4,5 };
char* p = a;
for (;p < &a[5];p++)
{
*p = 0;
}
指针与数组
int a[5] = { 1,2,3,4,5 };
int *p = a;
/*for (;p < &a[5];p++)
{
*p = 0;
}*/
printf("%d\n", a[2]);//3
printf("%d\n", p[2]);//3
printf("%d\n", *(a+2));//3
printf("%d\n", *(p+2));//3
printf("%d\n", 2[a]);//3
printf("%d\n", 2[p]);//3
printf("%d\n", *(2+p));//3
printf("%d\n",*(2+a));//3
以上输出均等价。
2.指针数组
存放指针的数组
int *a[5];
int main()
{
char s[] = "hello bit";
char s1[] = "hello bit";
const char *s2 = "hello bit";
const char *s3= "hello bit";//s1和s2一样,内存中只会存一份
if (s == s1)
printf("smae\n");
else
printf("not same\n");
if (s3 == s2)
printf("smae\n");
else
printf("not same\n");
}
整形指针的数组:
int *a[5];
一级字符指针的数组:
char *a1[2];
二级字符指针的数组:
char **a2[2];
有点像二维数组.
int a[5] = { 1,2,3,4,5 };
int b[] = { 2,3,4,5,6 };
int c[] = { 3,4,5,6,7 };
int* p[3] = { a,b,c };
int i = 0;
for (i = 0;i < 3;i++)
{
int j = 0;
for (j = 0;j < 5;j++)
{
printf("%d ", p[i][j]);//printf("%d",*(*(p+i)+j));
}
}
printf("\n");
int arr[5]={1,2,3,4,5};
int (*parr)[5]=&arr//取出的是数组的地址,parr是一个数组指针
arr//数组名是首元素arr[0]的地址
数组名是数组首元素的地址
除了(1)sizeof(数组名)————此处数组名表示整个数组,计算的是整个数组大小,单位是字节;(2)&数组名————此处数组名表示整个数组,取出的是整个数组的地址。
void print(int a[3][4], int b, int c)
{
int i = 0, j = 0;
for (i = 0;i < b;i++)
{
for (j = 0;j < c;j++)
{
printf("%d ", a[i][j]);
}
printf("\n");
}
}
void print1(int(*p)[], int b, int c)
{
int i = 0;
int j = 0;
for (i = 0;i < b;i++)
{
for (j = 0;j < c;j++)
{
printf("%d ", *((*p + i) + j));
}
printf("\n");
}
}
int main()
{
int a[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };
print(a, 3, 4);
print1(a, 3, 4);
}
细分:
int (*parr)[10];
表示:数组指针,该指针能够指向一个数组,数组10个元素,每个元素都是int型
int (*parr[10])[5];
表示:parr是一个存储数组指针的数组,该数组能够存放10个数组指针,每个数组指针能够指向一个数组,数组5个元素,每个元素都是int型。
三、数组传参,指针传参
int main()
{
int a[10] = { 1 };
int* a1[5] = { 2 };
test1(a);
test2(a1);
}
1.一维传参方式:
void test1(int a[])
{
}
void test1(int a[10])
{
}
void test1(int *a)
{
}
void test2(int *a[5])
{
}
void test2(int **a)
{
}
2.二维数组传参的三种:
void test(int a[2][3])
{
}
void test(int a[][3])
{
}
void test(int (*a)[3])
{
}
总结:二维数组传参,函数形参的设计只能省略第一个[]的数字,因为对于一个二维数组,可以不知道有多少行,但必须知道一行有多少元素。