C语言中的指针

指针概念

指针是一个变量,该变量的数值是地址,或者说,指针是一个数据对象。

类似于,int 类型变量的数值是整数。

与指针相关的运算符

间接运算符:*

ptr 指向 bath,ptr = &bath

获取 bath 中存放的数值,val = * ptr

上面两句语句等价于 val = bath

地址运算符:&

后跟一个变量名时,& 给出该变量的地址。

指针声明

int * pi;

int 表明被指向的变量的类型是整型,* 表示该变量是一个指针。pi 所指向的值(*pi)int 类型,pi
类型是“指向 int 的指针”。

* 和指针名之间的空格是可选的。

指针的输出格式是 %p

使用指针在函数间通信

结合 PHP 中函数的引用赋值来理解。

变量的值在函数中改变还是全局改变。

指针和数组

概念

在 C 中,对一个指针加 1 的结果是对该指针增加 1 个存储单元(storage unit)。对数组而言,地址会增加到下一个元素的地址,而
不是下一个字节。归纳如下:

  1. 指针的数值就是它所指向的对象的地址。对于包含多个字节的数据类型,对象的地址通常是指其首字节的地址。

  2. 在指针前运用运算符 * 可以得到该指针所指向的对象的数值。

  3. 对指针加1,等价于对指针的值加上它所指向的对象的字节大小。

函数、数组和指针

声明数组参量

下面的四种原型都是等价的

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)
{
}

声明形式参量时,int *arint ar[] 都表示 ar 是指向 int 的指针。

sizeof 求变量、指针、数组大小。

使用指针参数

使用数组形参的函数需要知道数组的起点和终点。告知终点有两种方法,一种是直接使用整数参量指明数组元素的个数,一种是用指针
指明数组的结束地址。比如,

int sum(int * start, int * end);

若数组的元素个数是 SIZE,那么,* end 的值是 &start + SIZE(表示数组的最后一个元素后面的下一个元素)。

一元运算符 *++ 具有相等的优先级别,但它在结合时是从右向左进行的。

指针操作

指针基本操作

  1. 赋值(assignment)。

通常使用数组名或地址运算符&进行地址赋值。地址应该和指针类型兼容。

  1. 求职(value-finding)或取值(dereferencing)。

  2. 取指针地址。

  3. 将一个整数加给指针。

  4. 增加指针的值。

  5. 从指针中减去一个整数。

  6. 减小指针的值。

  7. 求差值(Differencing)。指向同一个数组内两个元素的指针使用此运算。

  8. 比较。两个指针具有相同的类型。

对未初始化的指针取值

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

int * pt;   // 未初始化的指针
*pt = 5;

合法的代码

int i = 5;
int * pt = &i;

或者

double * ptd;
ptd = (double)malloc(30 * sizeof(double));

指针和多维数组

例程

#include <stdio.h>
int main(void)
{
    int zippo[4][2] = {{2, 4}, {6, 8}, {1, 3}, {5, 7}};

    printf("    zippo = %p,    zippo + 1 = %p\n",
           zippo,    zippo + 1);
    printf("zippo[0] = %p, zippo[0] + 1 = %p\n",
           zippo[0],    zippo[0] + 1);
    printf("  *zippo = %p,   *zippo + 1 = %p\n",
           *zippo, *zippo + 1);
    printf("zippo[0][0] = %d\n", zippo[0][0]);
    printf(" *zippo[0] = %d\n", *zippo[0]);
    printf("   **zippo = %d\n", **zippo);
    printf("    zippo[2][1] = %d\n", zippo[2][1]);
    printf("*(*(zippo+2) + 1) = %d\n", *(*(zippo+2) + 1));

    return 0;
}

代码见 E:\code\c\c_primer_plus_c\10_15_zippo1.c

这段代码中的疑点:*zippo[0]**zippo

声明指向二维数组的指针变量

正确的代码

int (* pz)[2];  // 声明一个指向二维数组pz[n][2]的指针 

错误的代码

int * pax[2];   // 创建一个两个指针的数组

指针兼容

int n = 5;
double x;
int * pi = &n;
double * pd = &x;
x = n;  // 隐藏的类型转换
pd = pi;    // 编译时错误

假如有如下声明:

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;   // 非法。不理解

保护数组内容

对形参使用 const

如果不打算在函数中修改数组,在函数原型和函数定义中对参数使用 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;
}

使用了 const,在函数中试图修改使用了 const 的参数时,编译时会发现此错误。

有关 const 的其他内容

使用 const 创建符号常量。

const double PI = 3.14159;

指向常量的指针不能用于修改数值,但可以指向其他地址。

double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
const double * pd = rates;      // pd指向数组开始处

* pd = 29.89;   // 不允许
pd[2] = 222.22;     // 不允许 
rates[0] = 99.99;   // 允许,因为 rates 不是常量
pd++;   // 让pd指向rates[1],允许

将常量或非常量数据的地址赋给指向常量的指针是合法的。

double rates[5] = {88.99, 100.2, 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.2, 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 保证指针不会指向别处。

double rates[5] = {88.99, 100.2, 59.45, 183.11, 340.5};
double * const pc = rates;  // 指针指向数组的开始处
pc = &rates[2];     // 不允许 
*pc = 92.99;        // 可以 

使用 const 禁止修改所指向的数据。

double rates[5] = {88.99, 100.2, 59.45, 183.11, 340.5};
const double * const pc = rates;    // 指针指向数组的开始处
pc = &rates[2];     // 不允许 
*pc = 92.99;        // 不允许  

参考资料

《C Primer Plus(第五版)中文版》

  1. 指针简介: P236-9.7
  2. 指针和数组: P254-10.3
  3. 函数、数组和指针: P256-10.4
  4. 指针操作: P260-10.5
  5. 指针和多维数组: P267-10.7
  6. 保护数组内容: P263-10.6
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容