写在前面:学习的第一门语言是Java,之前对C也了解一点,也只是了解一点,在加上长时间没有接触了,基本就只会一个Hello World了。现在由于准备升本考试,不得不从头开始学C。这里从零开始,记录C语言学习点滴。欢迎正在学习C语言的小伙伴一起学习,未来可期,一起加油!
指针是C语言中一个非常重要的概念,也是C语言的特色之一。使用指针可以对复杂数据进行处理,能对计算机的内存分配进行控制,在函数调用中使用指针还可以返回多个值。
地址和指针是计算机中的两个重要概念,在程序运行过程中,变量或者程序代码被存储在以字节为单位组织的存储器中。在C语言中,如果定义了一个变量,在编译时就会根据该变量的类型给它分配相应大小的内存单元。例如:假设int类型变量占 2 个字节,则需要分配 2 个字节的内存单元。
计算机为了对内存单元中的数据进行操作,一般是按“地址”存取的,也就是说对内存单元进行标识编号。假设如下有变量定义:
int x = 18, y = 24, z = 20;
因为int类型变量的存储长度为两个字节,因此假设C编译器将他们分配到地址为1000 ~ 1001、1002 ~ 1003和1004 ~ 1005的内存单元中。如下图:
实际上就在程序中,通过变量名进行操作,如调用函数printf("%d", x),输出X的值20。而程序执行时是将变量翻译为它所在的内存地址进行操作的,上述输出操作可以描述为:将X所在的内存地址1000 ~ 1001单元的内容按照整形格式输出。一般以变量所在的内存单元的第一个字节的地址作为它的地址,如变量X的内存地址是1000,y的内存地址是1002,z的地址是1004。变量X、y、z的内容分别为20、24、20。
要注意区分内存单元的内容和内存单元的地址。
在C程序中还有一种使用变量的方法,即通过变量的地址进行操作:用指针访问内存和操纵地址。
假设在定义一个变量P,它位于2000单元,该单元中存放了变量X的地址1000,如下图:
此时,取出变量P的值1000,就可以访问内存1000单元,实现对变量X的操作,也就是说通过变量P,可以间接访问变量X。
与直接使用变量X相比较,使用变量P访问变量X的过程实现了对变量X的间接操作。这种专门用来存放变量地址的变量称为“指针变量”,简称指针。
指针是用来存放内存地址的变量,如果一个指针变量的值是另一个变量的地址,给指针变量指向那个变量。上面提到的P就是一个指针变量,它存放了变量X的地址,即指针变量P指向变量X。
前面几篇多次看到把地址作为scanf()的输入参数的用法。例如,函数调用scanf("%d", &n),把输入的值存储到变量n所在的内存单元里。其中&n表示变量n的内存地址或存储位置。这里&称为地址运算符,&是一元运算符,与其他一元运算符有同样的优先级。
如果在程序中声明一个变量并使用地址作为该变量的值,那么这个变量就是指针变量。定义指针变量的一般形式为:
*类型名 指针变量名;
类型名指定指针变量所指向变量的类型,必须是有效的数据类型,如:int,float,char等。指针变量名是指针变量的名称,必须是一个合法的标识符。
int i, *p;
声明变量 i 是int型,变量 p 是指向int型变量的指针。指针值可以是特殊的地址0,也可以是一个代表机器地址的正整数。
指针声明符 * 在定义指针变量时被使用,说明被定义的那个变量是指针。
指针变量用于存放变量的地址,由于不同类型的变量在内存中占用不同大小的存储单元,所以只知道内存地址,不能确定该地址上的对象。因此在定义指针变量时,除了指针变量名,还需要说明该说明指针变量所指向的内存空间上所存放数据的类型。如下:
int *p; /* 定义一个指针变量P,指向整形变量 */
char *cp; /* 定义一个指针变量cp,指向字符型变量 */
float * fp; /* 定义一个指针变量fp,指向实型变量 */
double *dp1, *dp2; /* 定义两个指针变量dp1和dp2,指向双精度实型变量 */
定义多个指针变量时,每个指针变量前面都必须加上*
指针变量的类型不是指指针变量本身的类型,而是指定它所指向的变量的数据类型。
定义指针及指针赋值
int i, *p;
p = &i;
p = 0;
p = NULL;
p = (int *) 100;
p = &i;语句中的指针p被看作是指向变量 i 或存放变量 i 的地址,也就是将指针p和变量 i 关联起来,这也是指针最常用的赋值方法。指针变量p和整型变量 i 之间的关系如下:
p = 0;和p = NULL;语句说明了怎样把特殊值0赋值给指针p,这时指针的值为NULL。常量NULL在系统文件stdio.h中被定义,其值为0,将他赋给指针时代表空指针。
p = (int *) 100;使用强制类型转换(int *)来避免编译错误,表示P指向地址为100的int类型变量。不建议这样使用,一般不将绝对地址赋值给指针,NULL例外。
在定义指针变量时,要注意以下几点。
如果指针的值是某个变量的地址,通过指针就能间接访问那个变量,这些操作由取地址运算符&和间接访问运算符 * 完成。此外,相同类型的指针还能进行赋值、比较和算术运算。
1、取地址运算和间接访问运算
单目运算符 & 用于给出变量的地址。例如:
int *p, a = 3;
p = &a;
将整型变量a的地址赋给整型指针p,使指针p指向变量a。也就是说,用运算符&取变量a的地址,并将这个地址值作为指针p的值,使指针p指向变量a。
指针的类型和它所指向变量的类型必须相同。
在程序中,“ * ” 除了被用于定义指针变量外,还被用于访问指针所指向的变量,它也称为间接访问运算符。例如:但p指向a时,*p和a访问同一个存储单元,*p的值就是a的值,如下图:
取地址运算和间接访问运算示例。
#include
int main(){
int a = 3, *p; /* 定义整型变量a和整型指针p */
p = &a; /* 把变量a的地址赋给指针p,即p指向a */
printf("a = %d, *p = %d\n", a, *p); /* 输出变量a的值和指针p说指向变量的值 */
*p = 10; /* 对指针p所指向的变量赋值,相当于对变量a赋值 */
printf("a = %d, *p = %d\n", a, *p);
printf("请输入a的值:");
scanf("%d", &a); /* 输入a */
printf("a = %d, *p = %d\n", a, *p);
(*p)++; /* 将指针所指向的变量加1 */
printf("a = %d, *p = %d\n", a, *p);
return 0;
}
运行结果:
表达式*p = *p + 1、++p和(p)++,都是将指针p所指向变量的值加1。而表达式p++等价于*(p++),先取p的值作为表达式的值,在将指针p的值加1,运算后,p不再指向变量a。如:
int a = 1, x, *p;
p = &a;
x = *p++;
指针p先指向a,其后的语句x = *p++;将p所指向的变量a的值赋给变量x,然后修改指针的值,使得指针p不再指向变量a。
2、赋值运算
一旦指针被定义并赋值后,就可以如同其他类型变量一样进行赋值运算。例如:
int a = 3, *p1, *p2; /* 定义整型变量指针p1和p2 */
p1 = &a; /* 使指针p1指向整型变量a */
p2 = p1;
将变量a的地址赋给指针p1,再将p1的值赋给指针p2,因此指针p1和p2都指向变量a。此时,*p1、*p2和a访问同一个存储单元,它们的值一样。如下:
只能将一个指针的值赋给另一个相同类型的指针。
指针变量在定义后需要先赋值再引用。在定义指针变量时,可以同时对它赋初值。例如:
int a;
int *p1 = &a; /* 在定义指针p1的同时给其赋值,使指针p1指向变量a */
int *p2 = p1; /* 在定义指针p2的同时对其赋值,使p2和p1的值相同,都指向变量a */
在进行指针初始化的时候需要注意一下几点。
由于初学C语言,上述内容如有错误地方,恳请各位大佬指出!