指针变量的初始化,C语言指针变量初始化详解

原文链接,优秀

本节来解决如何给一个指针变量初始化。即怎样使一个指针变量指向另一个变量。

前面章节中的某些程序实际上已经使用了,即可以用赋值语句使一个指针变量得到另一个变量的地址,从而使它指向该变量。比如:

inti,*j;

j=&i;

这样就将变量 i 的地址放到了指针变量 j 中,通过 i 的地址,j 就能找到 i 中的数据,所以 j 就“指向”了变量 i。其中 & 是“取地址运算符”,与 scanf 中的 & 是一样的概念;* 为“指针运算符”,功能是取其内部所存变量地址所指向变量中的内容。因为 j 是定义成指针型变量,所以 j 中只能存放变量的地址,所以变量i前一定要加 &。需要注意的是,指针变量中只能存放地址,不要将一个整数或任何其他非地址类型的数据赋给一个指针变量。

此外,还有两点需要注意:

j 不是 i,i 也不是 j。修改j的值不会影响i的值,修改 i 的值也不会影响 j 的值。j 是变量 i 的地址,而 i 是变量 i 里面的数据。一个是“内存单元的地址”,另一个是“内存单元的内容”。

定义指针变量时的“*j”和程序中用到的“*j”含义不同。定义指针变量时的“*j”只是一个声明,此时的“*”仅表示该变量是一个指针变量,并没有其他含义。而且此时该指针变量并未指向任何一个变量,至于具体指向哪个变量要在程序中指定,即给指针变量初始化。而当指定 j 指向变量 i 之后,*j 就完全等同于 i 了,可以相互替换。

下面给大家写一个程序:

# include<stdio.h>

intmain(void)

{

inti=3,*j;//*j表示定义了一个指针变量j

j=&i;

printf("*j = %d\n",*j);//此时*j完全等同于i

printf("j = %d\n",j);//j里面存储的是变量i的地址

return0;

}

输出结果是:

*j = 3

j = 1245052

下面再将上面这个程序修改一下:

# include<stdio.h>

intmain(void)

{

inti=3;

int*j=&i;//*j表示定义了一个指针变量j, 并将变量i的地址赋给它

printf("*j = %d\n",*j);//此时*j完全等同于i

printf("j = %d\n",j);//j里面存储的是变量i的地址

return0;

}

输出结果是:

*j = 3

j = 1245052

这个程序与第一个程序有什么不同?同样是将变量 i 的地址赋给指针变量 j,第一个程序是“j=&i;”,而第二个程序是“*j=&i;”。原因是,前者是定义指针变量后对它初始化,即先定义后初始化;而后者是定义指针变量时对它进行初始化,即定义时初始化。通过这个对比我们可以更鲜明地看出定义指针变量时的“*j”和程序中用到的“*j”含义的不同。

那么指针变量和指针变量之间可不可以相互赋值呢?我们看看下面这个程序:

# include<stdio.h>

intmain(void)

{

int*i,*j;

intk=3;

i=&k;

j=i;//直接指针变量名之间进行赋值

printf("*j = %d\n",*j);//此时*j完全等同于k

printf("j = %d\n",j);// j里面存储的是变量k的地址

return0;

}

输出结果是:

*j = 3

j = 1245044

可见,可以直接将一个指针变量赋给另一个指针变量,只要将指针变量名赋给另一个指针变量名即可。但是需要注意的是:

这两个指针变量的基类型一定要相同。

在赋值之前,赋值运算符“=”右边的指针变量必须是已经初始化过的。也就是说,切忌将一个没有初始化的指针变量赋给另一个指针变量。这是非常严重的语法错误。

同样,也可以在定义指针变量时就给它赋初值:

# include<stdio.h>

intmain(void)

{

intk=3;

int*i=&k;

int*j=i;

printf("*j = %d\n",*j);//此时*j完全等同于k

printf("j = %d\n",j);//j里面存储的是变量k的地址

return0;

}

输出结果是:

*j = 3

j = 1245048

注意,“int*j=i;”千万不要写成“int*j=*i;”。因为此时 *i 不是定义指针变量 i,而是完全等同于变量 k。所以 int 型变量不能赋给 int* 型的变量。

指针常见错误

1) 引用未初始化的指针变量

试图引用未初始化的指针变量是初学者最容易犯的错误。未初始化的指针变量就是“野”指针,它指向的是无效的地址。

有些书上说:“如果指针变量不初始化,那么它可能指向内存中的任何一个存储单元,这样就会很危险。如果正好指向存储着重要数据的内存单元,而且又不小心向这个内存单元中写入了数据,把原来的重要数据给覆盖了,这样就会导致系统崩溃。”这种说法是不正确的!如果真是这样的话就是编译器的一个严重的 BUG!

编译器的设计人员是不会允许这么大的 BUG 存在的。那么如果指针变量未初始化,编译器的设计人员是如何处理这个问题的呢?肯定不可能让它乱指。以VC++6.0这个编译器为例,如果指针变量未初始化,那么编译器会让它指向一个固定的、不用的地址。下面来写一个程序:

# include<stdio.h>

intmain(void)

{

int*p,*q;

printf("p = %#X\n",p);

printf("q = %#X\n",q);

return0;

}

输出结果是:

p = 0XCCCCCCCC

q = 0XCCCCCCCC

可见,在 VC++6.0 中只要指针变量未初始化,那么编译器就让它指向 0XCCCCCCCC 这个内存单元。而且这个内存单元是程序所不能访问的,访问就会触发异常,所以也不怕往里面写东西。

而如果在 VS 2008 这个编译器中,程序虽然能编译通过,但是在运行的时候直接出错,它并不会像 VC++6.0 那样还能输出所指向的内存单元的地址。

下面来看一个程序:

# include<stdio.h>

intmain(void)

{

inti=3,*j;

*j=i;

return0;

}

程序中,j 是 int* 型的指针变量。j 中存放的应该是内存空间的地址,然后“变量 i 赋给 *j”表示将变量i中的值放到该地址所指向的内存空间中。但是现在 j 中并没有存放一个地址,程序中并没有给它初始化,那么它指向的就是 0XCCCCCCCC 这个内存单元。这个内存单元是不允许访问的,即不允许往里面写数据。而把 i 赋给 *j 就是试图往这个内存空间中写数据,程序执行时就会出错。但这种错误在编译的时候并不会报错,只有在执行的时候才会出错,即传说中的“段错误”。所以,一定要确保指针变量在引用之前已经被初始化为指向有效的地址。

在实际编程中,这种错误常见的另一个地方是用 scanf 给指针变量所指向的内存单元赋值。我们看看下面这个程序:

# include<stdio.h>

intmain(void)

{

int*i;

scanf("%d",i);

return0;

}

该程序试图给指针变量 i 所指向的内存单元赋值。但现在指针变量 i 并没有初始化,所以程序执行时出错。所以同样,在使用 scanf 时必须要先给指针变量 i 初始化。比如像下面这样写:

# include<stdio.h>

intmain(void)

{

int*i,j;

i=&j;//先给指针变量i初始化

scanf("%d",i);//i本身就是地址, 所以不用加&

printf("%d\n",*i);

return0;

}

输出结果是:

10

10

能不能使用 scanf 给指针变量初始化?指针变量里面存放的是地址,而内存中有数不清的单元,每个单元都有一个地址,你知道每个单元的地址吗?你知道哪些地址是空闲可用的,而哪些地址正存储着重要数据不能用吗?不知道的话怎么用scanf给它初始化呢?万一随便写一个地址正好是存储着非常重要的数据的内存单元地址,那系统就真的崩溃了!

随着大家编程能力的不断提高,慢慢地就会发现,其实编程最重要、最核心的就是如何处理内存的问题,如何与内存打交道。

2) 往一个存放NULL地址的指针变量里面写入数据

这也是编程中最容易犯的错误,不仅是初学编程的,即使是有一些经验的程序员也会不小心犯这个错误。我们把前面的程序改一下:

# include<stdio.h>

intmain(void)

{

inti=3;

int*j=NULL;

*j=i;

return0;

}

之前是没有给指针变量j初始化,现在初始化了,但是将它初始化为指向 NULL。NULL 也是一个指针变量。NULL 指向的是内存中地址为 0 的内存空间。以 32 位操作系统为例,内存单元地址的范围为 0x00000000~0xffff ffff。其中 0x00000000 就是 NULL 所指向的内存单元的地址。但是在操作系统中,该内存单元是不可用的。凡是试图往该内存单元中写入数据的操作都会被视为非法操作,从而导致程序错误。同样,这种错误在编译的时候也不会报错,只有在执行的时候才会出错。这种错误也属于“段错误”。

然而虽然这么写是错误的,但是将一个指针变量初始化为指向 NULL,这在实际编程中是经常使用的。就跟前面讲普通变量在定义时给它初始化为 0 一样,指针变量如果在定义时不知道指向哪里就将其初始化为指向 NULL。只是此时要注意的是,在该指针变量指向有效地址之前不要往该地址中写入数据。也就是说,该指针变量还要二次赋值。

既然不能往里面写数据,而且还容易犯错,为什么还要这样给它初始化呢?直接同前面定义普通变量时一样,在定义时也不初始化,等到后面知道该给它赋什么值时再给它赋值不行吗?可以!但还是建议大家将它初始化为 NULL,就同前面将普通变量在定义时初始化为 0 一样。这是很好的一种编程习惯。

最后关于 NULL 再补充一点,NULL 是定义在 stdio.h 头文件中的符号常量,它表示的值是 0。

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

推荐阅读更多精彩内容