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;       //非法

内容概要:本文档是详尽的 Android SDK 中文帮助文档,介绍了 Android SDK 的核心概念、组件、开发环境搭建、基础开发流程及常用工具使用指南。首先解释了 Android SDK 的定义及其核心价值,即提供标准化开发环境,使开发者能高效构建、测试、优化 Android 应用。接着详细列出了 SDK 的核心组件,包括 Android Studio、SDK Tools、Platform Tools、Build Tools、Android 平台版本和系统镜像。随后,文档提供了详细的环境搭建步骤,适用于 Windows、macOS 和 Linux 系统,并介绍了基础开发流程,以“Hello World”为例展示了从创建项目到运行应用的全过程。此外,还深入讲解了 ADB、AVD Manager 和 SDK Manager 等核心工具的功能和使用方法。最后,文档涵盖了调试与优化工具(如 Logcat、Profiler 和 Layout Inspector)、关键开发技巧(如多版本 SDK 兼容、Jetpack 库的使用和资源文件管理)以及常见问题的解决方案。 适合人群:具有初步编程知识,希望深入了解 Android 应用开发的开发者,尤其是新手开发者和有一定经验但需要系统化学习 Android SDK 的技术人员。 使用场景及目标:①帮助开发者快速搭建 Android 开发环境;②指导开发者完成基础应用开发,理解核心工具的使用;③提高开发效率,掌握调试与优化技巧;④解决常见开发过程中遇到的问题。 阅读建议:此文档内容全面且实用,建议读者按照章节顺序逐步学习,结合实际开发项目进行练习,尤其要注意动手实践环境搭建和基础开发流程,同时参考提供的扩展学习资源,进一步提升开发技能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值