C指针基础
指针定义
int x=98;
int *pointer=&x; //指向数据的类型 * 变量名
//确定了指向数据的类型的同时也确定了指针指向内容的大小
//而指针的大小根据可寻址的字长决定
(等同于)
int x=98;
int *pointer
pointer=&x; //如果写成pointer=x;会报错: 无法从“int”转换为“int *”
指向地址的指针(指针的指针)定义
int x=98;
int *pointer=&x;
int **ppointer=&pointer; //指向数据的类型 ** 变量名
(注意下面这种情况是错误的)
//按照中文的意思指针的指针嘛不应该是下面这样定义嘛
int x=98;
int *pointer=&x;
int *ppointer=&pointer;//但这样是不允许的要使用 **
取地址与解引用操作符
“&”:取地址操作符
“*”:解引用操作符,提取指针指向的内容
int x = 128;
int *myp = &x;
int** mypp = &myp;
cout <<myp<<endl;
cout << mypp << endl;
cout << *myp << endl;
cout << **mypp << endl;
cout << *mypp << endl;
*mypp :一次解引用访问c指示的地址,获取该地址的内容(内容为指针型),mypp 指向了myp的地址,myp指向了x所在的内存地址,则mypp 一次解引用获得的是myp的值也就是x的内存地址。
**mypp :拥有两次解引用第一个解引用获得的是myp的值也就是x的内存地址,那么第二个解引用就获得x的内存地址对应的值128。
指向数组的指针
数组就不多解释了直接上代码
int i;
char x[20] = "0123456789ABCDEFGHI";
for (i = 0; i < 19; i++)
{
printf("x[%d]:%c\n", i, x[i]);
}
char* p_x;
for (p_x = &x[0]; p_x < &x[19]; p_x++)//这里吧指针指向了数组的第一个元素的内存地址,p_x++为指针向后移动,移动的大小看它所定义的数据类型,即char一个字节
{
printf("%c", *p_x);
}
指针数组
所谓指针数组就是数组的元素类型为指针,每个元素存放着一个内存地址。
int i;
char x[20] = "0123456789ABCDEFGHI";
for (i = 0; i < 19; i++)
{
printf("x[%d]:%c\n", i, x[i]);
}
char* p_x[20];//定义一个指针数组
for (i = 0; i < 19; i++)//遍历指针数组,把前面字符数组的每个元素的内存地址赋值给对应的指针数组的元素
{
p_x[i] = &x[i];
}
for (i = 0; i < 19; i++)//遍历指针数组通过解引用*获得每个指针数组元素中的内存单元的地址对应内存单元的指
{
printf("%c", *p_x[i]);
}
常量指针
1.字符串常量指针:字符串常量可以直接作为指针基址,加上偏移步长,可以得到剩下的字符串的起始地址。
例如:printf(%s,“abcdefgh”+2);输出结果为:cdefgh
2.const指针
主要分为3类:
- 指针指向的内容不可变,但指针本身可以改变:指针指向的地址可变,但地址所在的内容不能变。
//声明方式为:
const int *a;
int const *a;
//应用举例:
#include<stdio.h>
int main(int argc,char **argv)
{
int a[]={12,13,14,15}
int *pr;//普通指针
for(pr=a;pr<=&a[3];pr++)
{
(*pr)++;
}
const int *cpr;
for(cpr=a;cpr<=&a[3];cpr++)
{
printf("%d\n",*cpr);
}
return 0;
}
输出结果为:13 14 15 16 指针cpr与pr的区别在于cpr指针指向的内容是无法被修改的,若强制进行修改,让cpr实现pr对数组a[]各个元素加1的功能(即将上面代码中的pr与cpr互换),编译器将会报错。
- 指针本身不能变,指向的内容可以修改:即指针指向的地址不能改变,但地址所在的内容可以改变。
//声明方式为:
int *const a;
//应用举例:
#include<stdio.h>
void add(int *const cpr)
{
(*cpr)++;
}
int main(int argc,char **argv)
{
int a[]={12,13,14,15}
int *pr;//普通指针
for(pr=a;pr<=&a[3];pr++)
{
add(pr);
printf("%d\n",*pr);
}
return 0;
}
add函数中,将指针cpr声明为int *const类型,这类型指针不能移动但能修改它指向的内容,这样可以让add函数实现将传入的参数加1,但不能做其他越权的工作,比如移动参数指针。对于此类型指针,如果强行修改指针指向的地址,将会出现错误。
- 指针本身不能改变,指向的内容也不能改变:
//声明方式:
const int *const a;
//应用举例:
#include<stdio.h>
int main(int argc,char **argv)
{
int a[]={12,13,14,15}
const int *const cpr;
for(cpr=a;cpr<=&a[3];cpr++)//①
{
(*cpr)++;//②
printf("%d\n",*cpr);
}
return 0;
}
如果看了前两种方式,那么就一定知道,这个应用举例的代码是无法编译通过的,错误结果分别出现在①和②两个步骤,因为此类型的指针指向的地址不能改变,地址对应的内容也无法改变。
函数指针
c语言中的数据变量无论是在程序栈中还是堆中,都拥有自己的内存地址,而函数也一样,函数的代码也需要调入内存才可以执行,它们在代码区也拥有自己的起始地址,所以我们可以定义指针指向函数,存储函数的起始地址。
//应用举例:
#include<stdio.h>
int add(int a,int b)
{
return a+b;
}
int main(void)
{
int (*myfunc)(int a,int b);//函数指针声明
myfunc=add;
int x=myfunc(12,36);
printf("%d",x);
return 0;
}
编译结果为输出: 48,可以看见,使用函数指针相当于为add函数起了一个别名,通过myfunc就可以直接调用add函数,那么利用这个机制,能让c语言模仿c++的类。
//应用举例:
#include<stdio.h>
struct mynum{
int a;
int b;
void (*add)(int a,int b,int *result);
}
int yadd(int a,int b,int *result)
{
(*result)=a+b;
}
int main(void)
{
struct mynum anum;
anum.a=12;
anum.b=10;
anum.add=yadd;
anum.add(anum.a,anum.b,&anum.result);
printf("%d\n",anum.result);
return 0;
}
函数指针数组
函数指针数组是指指以某数组的元素为指针,这些指针均指向函数的起始地址,这样做的好处是可以定义若干个函数,然后将这些函数的起始地址放入指针数组中,这样,我们就可以通过调用指针数组的元素来调用函数执行。
//定义方法:返回类型 (*函数指针变量名【】)(参数列表)
//应用举例:
int add(int a,int b){
return a+b;
}
int sub(int a,int b){
return a-b;
}
int main(void){
int (*operatefunc[])(int,int)={add,sub};
int result=0;
result=operatefunc[0](1,2);//operatefunc是一个数组,第一个元素operatefunc[0]指向add函数的起始地址
printf("%d\n",result);
result=operatefunc[1](4,3);//第二个元素operatefunc[1]指向sub函数的起始地址
printf("%d\n",result);
}
输出结果为:3 1 分别调用加法和减法。