前言:
什么是指针?
指针是编程语言中的一个对象,利用地址,它的值直接指向存在电脑存储器中另一个地方的值。
由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”
即:能通过它能找到以它为地址的内存单元
另外,平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量。
/知识点汇总/
1.指针变量就是变量,用来存放地址,地址唯一标识一块内存空间
2.指针的大小,是固定的4/8个字节(32位/64位)、
3.指针是有类型的,指针的类型决定了指针±整数的步长,以及指针解引用的权限、
4.指针的运算(指针加减整数:偏移量;指针减指针:是指针和指针之间的元素个数;指针关系运算:指针大小比较)
详见:指针初阶
指针就是地址,口语中说的指针指的是指针变量
整型指针:指向整型数据的指针
字符指针:就是指向字符型数据的指针变量
#include
int main()
{
char ch = 'w';
char* pc = &ch;
char* p = "abcdef";//这里的字符串等价于表达式,然后返回的结果是第一个字符的地址(可理解为数组)
//注意的是,这里的“abcdef”属于常量表达式,不可改变
printf("%c\n","abcdef"[3]);//d
//类似于数组名 --- “abcdef” ,所以可理解为数组形式
const* pd = "abccdef";
printf("%s\n", pd);//abcdef
printf("%c\n", *pd);//a
return 0;
}
注意的是:
(1)、这里的字符串等价于表达式,然后返回的结果是第一个字符的地址(可理解为数组)
(2)、这里的“abcdef”属于常量表达式,不可改变
例题:
#include
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char str3 = "hello bit.";
const char str4 = "hello bit.";
if (str1 == str2)
{
printf("1 to 2 same\n");
}
else
{
printf("1 to 2 not same\n");
}
if (str3 == str4)
{
printf("3 to 4 same\n");
}
else
{
printf("3 to 4 not same\n");
}
if (&str3 == &str4)
{
printf("&3 to &4 same\n");
}
else
{
printf("&3 to &4 not same\n");
}
return 0;
}
小结:
(1)、str1 与 str2 是两个不同的空间地址所以,不相等
(2)、str3 与 str4 由于保存的是同一个常量表达式,所以它们指向的地址相同,则得到的值相同;但是str3 和str4 本身的地址是不同的
字符数组 – 存放字符的数组
整型数组 – 存放整型的数组
指针数组 – 存放指针的数组,存放在数组中的元素是指针类型
如:
int* arr[5];
char* ch[6];
#include
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
//int* int* int*
//指针数组
int* arr[] = {arr1,arr2,arr3};
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ",arr[i][j]);
}
printf("\n");
}
return 0;
}
#include
int main()
{
//指针数组
char* arr[5] = {"hello bit","hehe","abcf","cojag","waofrdf"};
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%s\n",arr[i]);
}
return 0;
}
指针数组 ---- 是数组,是存放指针的数组
数组指针 — 是指针
字符指针 — 是指向字符的指针
整型指针 – 指向整型的指针
浮点型指针 – 指向浮点数的指针
数组名是数组首元素的地址,但是存在两个例外,sizeof(数组名),&数组名;
两个例外的数组名,表达的是,整个数组的地址
数组指针 — &arr
虽然与首元素地址值相同,但进行指针运算时±,发现,加减的整个数组的大小字节
同时指针类型决定了,指针+1,到底+几个字节(步长)
#include
int main()
{
int arr[10];
printf("%p\n",arr);
printf("%p\n",arr+1);
printf("%p\n", &arr);
printf("%p\n", &arr + 1);
printf("%p\n", arr[0]);
printf("%p\n", arr[0] + 1);
printf("%p\n", &arr[0]);
printf("%p\n", &arr[0] + 1);
return 0;
}
#include
int main()
{
int arr[10] = { 0 };
//int* p = &arr;
int(*p)[10] = &arr;//p是用来存放数组的地址的,p就是数组指针
char* arr2[5];
char* (*pc)[5] = &arr2;
//(*pc)说明pc是一个指针类型,(*pc)[5],表示数组指针,char* 表示该数组类型属于字符指针类型
//int arr3[] = { 1,2,3 };
//int(*p3)[] = &arr3;
//数组指针的[]操作符的参数必须指明,否则报错
return 0;
}
#include
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//数组指针
int(*p)[10] = &arr;
int i = 0;
for(i=0;i<10;i++)
{
printf("%d ",(*p)[i]);
}
//指针变量
int* p = &arr;
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", p[i]);
}
return 0;
}
二维数组的数组名是首元素的地址,即:数组指针的首元素地址是,第一行的地址
#include
//二维数组传参也是二维数组的形式
//void print(int arr[3][5], int row, int col)//形参是数组形式
//{
// int i = 0;
// for (i = 0; i < 3; i++)
// {
// int j = 0;
// for (j = 0; j < 5; j++)
// {
// printf("%d ",arr[i][j]);
// }
// printf("\n");
// }
//}
void print(int (*p)[5], int row, int col)//形参是指针形式,指向第一行
{
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ",p[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{6,7,8,9,10} };
print(arr,3,5);//二维数组传参,行,列
return 0;
}
#include
void test(int arr[])//ok
{}
void test(int arr[10])//ok
{}
void test(int *arr)//ok
{}
void test2(int* arr[20])//ok
{}
void test2(int* *arr)//ok
{}
int main()
{
int arr[10] = { 0 };
int* arr2[20] = { 0 };
test(arr);
test2(arr2);
return 0;
}
#include
void test(int arr[3][5])//ok
{}
void test(int arr[][])//No,列不可以省
{}
void test(int arr[][5])//ok
{}
void test(int *arr)//No,不能以一维数组接收二维数组,因为二维数组的首元素地址是第一行的地址,而不是第一个元素的地址
{}
void test(int* arr[5])//No,类型不同,int* 与int
{}
void test(int (*arr)[5])//ok,数组指针,指向第一行
{}
void test(int **arr)//No,二级指针用于指向一级指针的地址
{}
int main()
{
int arr[3][5];
test(arr);
return 0;
}
一级指针传参:
一级指针传参时,形参写成一级指针形式
#include
void print(int* p, int sz)//一级指针传参时,形参写成一级指针形式就可以了
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d\n",*(p+i));
}
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
//一级指针p传给函数,作形参
print(p,sz);
return 0;
}
思考扩展1:
当我们遇见一个函数的形参是一级指针的时候,考虑函数的实参可以传什么?
答:只要是可以传递的对应类型的地址/一级指针变量就可以,以及数组名也行。
二级指针传参:
二级指针传参时,形参写成二级指针形式
#include
void test(int** ptr)
{
printf("num = %d\n",**ptr);
}
int main()
{
int n = 10;
int* p = &n;
int** pp = &p;
test(pp);
//test(&p);等价
return 0;
}
思考扩展2:
当我们遇见一个函数的形参是一级指针的时候,考虑函数的实参可以传什么?
答:只要是可以传递的对应类型的一级指针地址/二级指针变量就可以,一级指针类型的数组名也行。
数组指针 – 指向数组的指针 – 存放的是数组的地址 — &数组名就是整个数组的地址
函数指针 – 指向函数的指针 — 存放的是函数的地址
如何取得函数的地址呢?也是通过 &函数名吗?
答:&操作符加函数名即可
#include
int Add(int x, int y)
{
return (x + y);
}
int main()
{
//&函数名就是函数的地址
//函数名也是函数的地址
printf("%p\n", &Add);
printf("%p\n", Add);
//均表示函数的地址
//函数指针
//int (*pf1)(int , int) = Add;//pf1就是函数指针变量
int (*pf2)(int, int) = &Add;//pf2同样是函数指针变量
int ret = (*pf2)(2, 3);
//因为Add与&Add等价
//常规写法:
//int ret = Add(2,3);
//所以*号就可以省略
//int ret = pf2(2,3)
printf("%d\n",ret);
}
int arr[5];//arr是一个存放5个整型数据的数组
int* parr1[10];//parr1是一个数组,数组的10个元素类型是int*
int(*parr2)[10];//parr2是一个数组指针,该指针指向的数组,指向数组的10个元素类型是int
int(*parr3[10])[5];//parr3是一个数组,是存放数组指针的10元素的数组,存放的这个数组指针,指针指向的5个元素是int类型
#include
int main()
{
(*(void(*)())0)();
//整体是一个函数调用
//(void(*)()函数指针类型
//(void(*)()) 强制类型转换为函数指针类型
//(*(类型)0)(); --- 所以这段代码表示:
//调用0地址处的函数,这个函数没有参数,且返回值类型为void省略不写
void (*signal(int, void(*)(int)))(int);
//整体是一个函数声明
//void(*)(int)函数指针作为参数
//signal(int, void(*)(int)) --- 函数,一个参数是int类型,一个参数是函数指针类型,返回值类型也是作为函数指针类型
//(*signal(int, void(*)(int))) --- 函数指针
//(*signal(int, void(*)(int)))(int) --- 函数指针参数是一个int类型参数
//void (*signal(int, void(*)(int)))(int) -- 一个返回值类型为空,参数是一个整型的函数指针的声明
//所以这段代码表示:
//是一个函数声明,声明的是signal函数,signal函数的参数有两个,一个是int类型,一个是函数指针类型,该类型是void(*)(int),该函数指针指向的函数,参数类型是int类型,返回值类型为void
//signal函数的返回值类型也是函数指针类型,该类型是void (*)(int),该函数指针指向的函数,参数类型是int类型,返回值类型为void
//利用typedef 对这段代码简化:
typedef void(*pfun_t)(int);
//typedef 对函数指针类型重命名时,被重名的变量名,放在*面,即pfun_t的位置
pfun_t signal(int, pfun_t);
return 0;
}
C语言指针进阶前篇到此结束啦!
未完待续…
半亩方糖一鉴开,天光云影共徘徊。
问渠哪得清如许?为有源头活水来。–朱熹(观书有感)