首页 > 编程笔记 > C语言笔记 > 指针
阅读:10,250
指针变量的初始化,C语言指针变量初始化详解
本节来解决如何给一个指针变量初始化。即怎样使一个指针变量指向另一个变量。
前面章节中的某些程序实际上已经使用了,即可以用赋值语句使一个指针变量得到另一个变量的地址,从而使它指向该变量。比如:
int i, *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
int main(void)
{
int i = 3, *j; //*j表示定义了一个指针变量j
j = &i;
printf("*j = %d\n", *j); //此时*j完全等同于i
printf("j = %d\n", j); //j里面存储的是变量i的地址
return 0;
}
输出结果是:
*j = 3
j = 1245052
下面再将上面这个程序修改一下:
# include
int main(void)
{
int i = 3;
int *j = &i; //*j表示定义了一个指针变量j, 并将变量i的地址赋给它
printf("*j = %d\n", *j); //此时*j完全等同于i
printf("j = %d\n", j); //j里面存储的是变量i的地址
return 0;
}
输出结果是:
*j = 3
j = 1245052
这个程序与第一个程序有什么不同?同样是将变量 i 的地址赋给指针变量 j,第一个程序是“j=&i;”,而第二个程序是“*j=&i;”。原因是,前者是定义指针变量后对它初始化,即先定义后初始化;而后者是定义指针变量时对它进行初始化,即定义时初始化。通过这个对比我们可以更鲜明地看出定义指针变量时的“*j”和程序中用到的“*j”含义的不同。
那么指针变量和指针变量之间可不可以相互赋值呢?我们看看下面这个程序:
# include
int main(void)
{
int *i, *j;
int k = 3;
i = &k;
j = i; //直接指针变量名之间进行赋值
printf("*j = %d\n", *j); //此时*j完全等同于k
printf("j = %d\n", j); // j里面存储的是变量k的地址
return 0;
}
输出结果是:
*j = 3
j = 1245044
可见,可以直接将一个指针变量赋给另一个指针变量,只要将指针变量名赋给另一个指针变量名即可。但是需要注意的是:
这两个指针变量的基类型一定要相同。
在赋值之前,赋值运算符“=”右边的指针变量必须是已经初始化过的。也就是说,切忌将一个没有初始化的指针变量赋给另一个指针变量。这是非常严重的语法错误。
同样,也可以在定义指针变量时就给它赋初值:
# include
int main(void)
{
int k = 3;
int *i = &k;
int *j = i;
printf("*j = %d\n", *j); //此时*j完全等同于k
printf("j = %d\n", j); //j里面存储的是变量k的地址
return 0;
}
输出结果是:
*j = 3
j = 1245048
注意,“int*j=i;”千万不要写成“int*j=*i;”。因为此时 *i 不是定义指针变量 i,而是完全等同于变量 k。所以 int 型变量不能赋给 int* 型的变量。
指针常见错误
1) 引用未初始化的指针变量
试图引用未初始化的指针变量是初学者最容易犯的错误。未初始化的指针变量就是“野”指针,它指向的是无效的地址。
有些书上说:“如果指针变量不初始化,那么它可能指向内存中的任何一个存储单元,这样就会很危险。如果正好指向存储着重要数据的内存单元,而且又不小心向这个内存单元中写入了数据,把原来的重要数据给覆盖了,这样就会导致系统崩溃。”这种说法是不正确的!如果真是这样的话就是编译器的一个严重的 BUG!
编译器的设计人员是不会允许这么大的 BUG 存在的。那么如果指针变量未初始化,编译器的设计人员是如何处理这个问题的呢?肯定不可能让它乱指。以VC++6.0这个编译器为例,如果指针变量未初始化,那么编译器会让它指向一个固定的、不用的地址。下面来写一个程序:
# include
int main(void)
{
int *p, *q;
printf("p = %#X\n", p);
printf("q = %#X\n", q);
return 0;
}
输出结果是:
p = 0XCCCCCCCC
q = 0XCCCCCCCC
可见,在 VC++6.0 中只要指针变量未初始化,那么编译器就让它指向 0XCCCCCCCC 这个内存单元。而且这个内存单元是程序所不能访问的,访问就会触发异常,所以也不怕往里面写东西。
而如果在 VS 2008 这个编译器中,程序虽然能编译通过,但是在运行的时候直接出错,它并不会像 VC++6.0 那样还能输出所指向的内存单元的地址。
下面来看一个程序:
# include
int main(void)
{
int i = 3, *j;
*j = i;
return 0;
}
程序中,j 是 int* 型的指针变量。j 中存放的应该是内存空间的地址,然后“变量 i 赋给 *j”表示将变量i中的值放到该地址所指向的内存空间中。但是现在 j 中并没有存放一个地址,程序中并没有给它初始化,那么它指向的就是 0XCCCCCCCC 这个内存单元。这个内存单元是不允许访问的,即不允许往里面写数据。而把 i 赋给 *j 就是试图往这个内存空间中写数据,程序执行时就会出错。但这种错误在编译的时候并不会报错,只有在执行的时候才会出错,即传说中的“段错误”。所以,一定要确保指针变量在引用之前已经被初始化为指向有效的地址。
在实际编程中,这种错误常见的另一个地方是用 scanf 给指针变量所指向的内存单元赋值。我们看看下面这个程序:
# include
int main(void)
{
int *i;
scanf("%d", i);
return 0;
}
该程序试图给指针变量 i 所指向的内存单元赋值。但现在指针变量 i 并没有初始化,所以程序执行时出错。所以同样,在使用 scanf 时必须要先给指针变量 i 初始化。比如像下面这样写:
# include
int main(void)
{
int *i, j;
i = &j; //先给指针变量i初始化
scanf("%d", i); //i本身就是地址, 所以不用加&
printf("%d\n", *i);
return 0;
}
输出结果是:
10
10
能不能使用 scanf 给指针变量初始化?指针变量里面存放的是地址,而内存中有数不清的单元,每个单元都有一个地址,你知道每个单元的地址吗?你知道哪些地址是空闲可用的,而哪些地址正存储着重要数据不能用吗?不知道的话怎么用scanf给它初始化呢?万一随便写一个地址正好是存储着非常重要的数据的内存单元地址,那系统就真的崩溃了!
随着大家编程能力的不断提高,慢慢地就会发现,其实编程最重要、最核心的就是如何处理内存的问题,如何与内存打交道。
2) 往一个存放NULL地址的指针变量里面写入数据
这也是编程中最容易犯的错误,不仅是初学编程的,即使是有一些经验的程序员也会不小心犯这个错误。我们把前面的程序改一下:
# include
int main(void)
{
int i = 3;
int *j = NULL;
*j = i;
return 0;
}
之前是没有给指针变量j初始化,现在初始化了,但是将它初始化为指向 NULL。NULL 也是一个指针变量。NULL 指向的是内存中地址为 0 的内存空间。以 32 位操作系统为例,内存单元地址的范围为 0x00000000~0xffff ffff。其中 0x00000000 就是 NULL 所指向的内存单元的地址。但是在操作系统中,该内存单元是不可用的。凡是试图往该内存单元中写入数据的操作都会被视为非法操作,从而导致程序错误。同样,这种错误在编译的时候也不会报错,只有在执行的时候才会出错。这种错误也属于“段错误”。
然而虽然这么写是错误的,但是将一个指针变量初始化为指向 NULL,这在实际编程中是经常使用的。就跟前面讲普通变量在定义时给它初始化为 0 一样,指针变量如果在定义时不知道指向哪里就将其初始化为指向 NULL。只是此时要注意的是,在该指针变量指向有效地址之前不要往该地址中写入数据。也就是说,该指针变量还要二次赋值。
既然不能往里面写数据,而且还容易犯错,为什么还要这样给它初始化呢?直接同前面定义普通变量时一样,在定义时也不初始化,等到后面知道该给它赋什么值时再给它赋值不行吗?可以!但还是建议大家将它初始化为 NULL,就同前面将普通变量在定义时初始化为 0 一样。这是很好的一种编程习惯。
最后关于 NULL 再补充一点,NULL 是定义在 stdio.h 头文件中的符号常量,它表示的值是 0。
所有教程
socket
Python基础教程
C#教程
MySQL函数
MySQL
C语言入门
C语言专题
C语言编译器
C语言编程实例
GCC编译器
数据结构
C语言项目案例
C++教程
OpenCV
Qt教程
Unity 3D教程
UE4
STL
Redis
Android教程
JavaScript
PHP
Mybatis
Spring Cloud
Maven
vi命令
Spring Boot
Spring MVC
Hibernate
Linux
Linux命令
Shell脚本
Java教程
设计模式
Spring
Servlet
Struts2
Java Swing
JSP教程
CSS教程
TensorFlow
区块链
Go语言教程
Docker
编程笔记
资源下载
关于我们
汇编语言
大数据
云计算
VIP视频
优秀文章
Go语言接口的嵌套组合
C++ set迭代器(STL set迭代器)详解
Shell if条件判断语句用法详解
VGGNet、ResNet、Inception和Xception图像分类及对比
类其实也是一种作用域
MySQL DATEDIFF函数:获取两个日期的时间间隔
Linux哪些数据需要备份
Vim多窗口编辑模式
JS标识符、关键字和保留字
JS对象操作(in、instanceof、delete)运算符