C语言 指针

指针,恐怕是C里面最令人头疼的东西了。
一般来讲,指针是一个其数值为地址的变量(或更一般的说是一个数据对象)。

ptr = &pooh; 
// 把pooh的地址赋给ptr,称为ptr指向pooh
// ptr和&pooh的区别在于,ptr是一个变量,后者是一个常量

间接运算符 *

假定ptr指向bah,如下所示:
ptr = &bah;

这时就可以使用间接(indirection)运算符*(也称取值 dereferencing 运算符)

val = * ptr; // 得到ptr指向的值
ptr = &bah;
val = *ptr;
//上面2句等同于:val = bah;

取地址运算符:&,后面跟一个变量名时,&给出该变量的地址。
9.7.2 指针声明
申明指针变量时,还需要说明指针所指向变量的类型,因为不同的变量类型占用的存储空间大小不同,而有些指针操作需要知道变量类型所占用的存储空间,同时,程序也需要了解地址中存储的是何种数据,例如long和float两种类型的数值可能使用相同大小的存储空间,但是它们的存储方式完全不同。

int * pi;
char * pc;
float *pf, *pg;
//类型标识符表明了被指向变量的类型,而星号表示该变量为一指针
//int *pi;的意思是pi是一个指针,而*piint类型的。那么pi是什么类型呢?是“指向int的指针”类型,ANSI C专门为指针提供了%p输出格式。

9.7.3 使用指针在函数间通信

#include <stdio.h>
void interchange(int * u, int * v);
int main(void){
    int x = 5, y = 10;
    printf("Originally x = %d and y = %d.\n", x, y);
    interchange(&x, &y);//向函数传递地址
    printf("now x = %d and y = %d.\n", x, y);
    return 0;
}

void interchange(int * u, int * v){
    int temp;
    temp = *u;
    *u = *v;
    *v = temp;

}

10. 数组和指针

10.3 指针和数组
数组名同时也是该数组元素的地址。即如果flizny是一个数组,那么:

flizny = &flizny[0];
dates + 2 == &dates[2] //相同的地址
*(dates + 2) == dates[2] //相同的值
ar[n] = *(ar+n),寻址到内存中的ar,然后移动n个单位,再取出数值。

间接运算符*的优先级高于+,因此*date+2等价于(*date)+2

声明数组 参量:
由于数组名就是数组首元素的地址,所以如果实际参数是一个数组名,那么形式参数必须是与之相匹配的指针。在(而且仅在)这种场合中,C对于int ar[]和int *ar作出同样解释,即ar是指向int的指针。由于原型允许省略名称,因此下面4个是等价的:

int sum(int *ar, int n);
int sum(int *, int);
int sum(int ar[], int n);
int sum(int [], int);
//定义函数时,名称不可以省略:
int sum(int *ar, int n){}
int sum(int ar[], int n){}

10.5 指针操作

#include <stdio.h>
int main(void){
    int urn[5] = {100,200,300,400,500};
    int * ptr1, * ptr2, * ptr3;
    ptr1 = urn;
    ptr2 = &urn[2];
    printf("pointer value, dereferenced pointer, pointer address:\n");
    printf("ptr1=%p,*ptr1=%d,&ptr1 = %p\n",ptr1,*ptr1,&ptr1);
    //指针加法
    ptr3 = ptr1 + 4;
    printf("\n adding an int to a pointer:\n");
    printf("ptr1 + 4 = %p, *(ptr1+3)=%d\n",ptr1+4,*(ptr1+3));

    ptr1++;//递增指针
    printf("\n values after ptr1++\n");
    printf("ptr1 = %p, *ptr1 = %d, &ptr1 = %p \n", ptr1, *ptr1, &ptr1);

}

增加指针的值——可以通过一般的加法或增量运算符来增加一个指针的值。对指向某数组元素的指针做增量运算,可以让指针指向该数组的下一个元素。因此,ptr1++运算把ptr1加上数值4(我们的系统上int为4字节),使ptr1指向urn[1],现在ptr1的值是0x0012ff3c(下一个数组元素的地址),*ptr的数值为200(urn[1]的值),请注意ptr1本身的值仍是0x0012ff34.因为变量不会因为它的值的变化而移动位置。

不能对未初始化的指针取值

int *pt;
*pt = 5;//可怕的错误

为什么这样的代码危害很大?爹行表示把数值5存到pt所指向的地址,但由于pt没有被初始化,因此它的值是随机的,不知道5会被存储到什么位置。
切记:当创建一个指针时,系统只分配了用来存储指针本身的内存地址,并不分配用来存储数据的内存地址。因此,在使用指针之前,必须给它赋予一个已分配的内存地址。

10.6.1 对形参使用const

int sum(const int ar[], int n);
int sum(const int ar[], int n){
    int i;
    int total = 0;
    for(i = 0; i < n; i++){
        total += ar[i];
    }
    return total;
}

10.6.2 const 数组常量 指针常量 指向常量的指针
指向常量的指针不能用于修改数值:

double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
const double * pd = rates;

pd声明为指向const double 的指针,这样,就不可以使用pd来修改它所指向的数值。

*pd  = 29.89;  //不允许
pd[2] = 222.22;//不允许
rates[0] = 99.99; //允许

pd++; //让pd指向rates[1],允许

通常把指向常量的指针用作函数参量,以表明函数不会用这个指针来修改数据。
如:

void show_array(const double *ar, int n);

关于指针赋值和const有一些规则:首先,将常量或非常量数据的地址赋给指向常量的指针是合法的:

 double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
 const double locked[4] = {0.08, 0.075, 0.0725, 0.07};
 const double * pc = rates; //合法
 pc = locked; //合法
 pc = &rates[3]; //合法

//只有非常量数据的地址才可以赋给普通指针
 double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
 const double locked[4] = {0.08, 0.075, 0.0725, 0.07};‘
 double * pnc = rates; //合法
 pnc = locked; //非法
 pnc = &rates[3]; //合法
 //  这样的规则是合理的,否则,您就可以使用指针来修改被认为是常量的数据。

还可以用const来声明并初始化指针,以保证指针不会指向别处,关键在于const的位置:

double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
double * const pc = rates;
pc = &rates[2];  //不可以
*pc = 92.99;     //可以

这样的指针仍然可用于修改数据,但它只能指向最初赋给它的地址。
最后,可以使用两个const来创建指针,这个指针既不可以更改所指向的地址,又不可以修改所指向的数据。

10.7 指针和多维数组

int zippo[4][2];

数组名zippo同时也是数组首元素的地址(这里zippo是包含2个int的数组的地址)。

  • 因为zippo是数组首元素的地址,所以zippo的值和&zippo[0]相同。zippo[0]的值同其首元素(一个整数)的地址&zippo[0][0]相同。
  • 对一个指针(也就是地址)加1,会对原来的数值加上一个对应类型大小的数值,zippo所指向对象的大小是2个int,zippo所指向的对象的大小是一个int,因此zippo+1和zippo[0]+1的结果不同。
  • 对一个指针(也就是地址)取值(使用运算符或者带有索引的[]运算符)得到的是该指针所指向对象的数值。因为zippo[0]是其首元素zippo[0][0]的地址,所以(zippo[0])代表存储在zippo[0][0]中的数值,即一个int数值。同样,*zippo代表其首元素zippo[0]的值,但是zippo[0]本身就是一个int型的地址,即&zippo[0][0],因此*zippo是&zippo[0][0].对这两个表达式同时应用取值运算符将得到** zippo 等价于 *&zippo[0][0] == zippo[0][0]

zippo == &zippo[0]
*(zippo[0]) == zippo[0][0]
*zippo == zippo[0]的值 == &zippo[0][0]
*zippo == &zippo[0][0] == zippo[0][0]
简言之,zippo是地址的地址,需要2辞去之词可以得到通常的数值。

10.7.1 指向多为数组的指针
int (*pz)[2]; //pz指向一个包含2个int值的数组:pz先和*结合,从而创建一个指向包含2个int值的数组的指针
int *pz[2];//pz是包含2个某种元素的数组,与*结合,表示pz是2个指针组成的数组,最后用int来定义,表示pz是由2个指向int值的指针构成的数组。这种声明会创建2个指向单个int值的指针。

#include <stdio.h>
int main(void){
    int zippo[4][2] = {{2,4},{6,8},{1,3},{5,7}};
    int (*pz)[2];
    pz = zippo;
    printf("pz = %p, pz+1=%p \n",pz,pz+1);
    printf("pz[0]=%p,pz[0]+1 = %p\n",pz[0],pz[0]+1);
    printf("*pz=%p, *pz+1 = %p \n",*pz, *pz+1);
    printf("pz[0][0] = %d\n",pz[0][0]);
}
10.7.2 指针兼容性
int n = 5;
double x;
int *pi = &n;
double *pd = &x;
x = n;
pd = p1;//出错

int * pt;
int (*pa)[3];
int ar1[2][3];
int ar2[3][2];
int **p2; //指向指针的指针
有如下结论:
pt = &ar1[0][0]; //指向int
pt = ar1[0];     //指向int
pt = ar1;        //非法
pa = ar1;        //指向int[3]
pa = ar2;        //非法
p2 = &pt;        //指向int *
*p2 = ar2[0];    //指向int
p2 = ar2;       //非法

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REaDME.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值