C语言--深入理解指针
C语言--深入理解指针
一. 指针的概念
要知道指针的概念,要先了解变量在内存中是如何存储的。在存储时,内存被分为一块一块的,每一块都有一个特有的编号。而这个编号可以暂时理解为指针,就是酒店的门牌号一样。
- 1.变量和地址
看下面代码
void main(){
int x = 10, int y = 20;
}
代码中声明了两个变量x,y
。把内存当做一个酒店,而每个房间就是一块内存。那么int x = 10;
和int y = 20;
的实际含义是:去酒店订了两个房间,门牌号暂时用px
、py
表示,让10住进px
,让y
住进py
,其中门牌号就是px、py
,也就是房间的地址。x
和y
在这里可理解为具体的房间,房间x
的门牌号(地址)是px
,房间y
的门牌号(地址)是py
。而10和20,通过px
、py
两个门牌,找到房间,住进x
、y
。
- 2.指针变量和指针的类型
指针变量就是一个变量,存储的内容是一个指针。可以理解为指针变量就是一张房卡,房卡存储了房间号的信息。在定义一个变量的时候,要确定其类型,同样,定义指针变量时也是一样的,必须确定指针类型。int
变量的指针需要用int
类型的指针存储,float
变量的指针需要用float
类型的指针存储。就像你只能用酒店A的房卡存储酒店A中房间号的信息一样。
二. 变量的指针与指针变量
变量的指针就是变量的存储地址,指针变量就是存储指针的变量。
- 1.指针变量的定义及使用
- 1.1指针变量的定义
指针变量的定义形式:数据类型 *指针名
。
int *x;
表示int
类型指针变量x
;
float *t;
表示float
类型指针变量t
;
char *ch;
表示char
类型的指针变量ch
。 - 1.2指针变量的使用
取地址运算符&
:单目运算符&
是用来取操作对象的地址。例如&i
为取变量i的地址。对于常量表达式、寄存器变量不能取地址(因为它们存储在存储器中,没有地址)。
指针运算符*
(间接寻址符):与&
为逆运算,作用是通过操作对象的地址,获取存储的内容。例如x=&i
,x
为i
的地址,*x
则为通过i的地址,获取i
的内容。
int a;
//声明一个整型变量a
;
int *pa;
//声明一个指针变量,指向变量a
地址;
pa = &a
//通过取地址符&
获取a
地址,赋值给指针变量pa
。
printf("%d",*pa);
//通过间接寻址符,获取指针指向的地址上的内容。 - 1.3
&
和*
的结合方法
&
和*
都是右结合计算的。假设变量x = 10
,则*&x
的含义是先获取x
的地址,再获取地址上的内容,因为&
和*
互为逆运算,所以x = *&x
。
实例:
输入x和y
两个整数,然后将其中的值大的赋值给x
,小的赋值给y
。即:假设输入x = 8,y = 9
。就将9
赋值给x
,8
赋值给y
。
#include<stdio.h>
int main(){
int x, y;
int *px,*py;
int t;
scanf("%d",&x);
scanf("%d",&y);
px = &x;
py = &y;
if(*px <*py){
t = *px;
*px = *py;
*py = t;
}
printf("x = %d, y=%d",*px,*py);
return 0;
}
结果显示:
- 2.指针的初始化
指针变量与其他变量一样,在定义时可以赋值,即初始化。也可以赋值“NULL”或“0”,如果赋值为“0”,此时的“0”含义并不是数字“0”,而是Null的字符码值。
int x;
int *px = &x; //利用取地址获取x的地址,在指针变量px定义时,赋值给px
- 3.指针运算
- 3.1赋值运算
指针变量可以相互赋值,也可以赋值某个变量的地址,或者赋值一个具体的地址。
int *px, *py, *pz, x = 10;
px = &x;//赋予某个变量的地址
py = px;//相互赋值
- 3.2指针与整数的加减运算
指针变量的自增自减运算,指针加1或减1运算,表示指针向前或向后移动一个存储单位(不同类型的指针,存储单位不同)。
指针变量加上或减去一个整型数,具体加几就是向前移动几个单元,减几就是向后移动几个单元。
int x, y, z;//定义三个变量,假设他们地址是连续的,分别为400,404,408
pz = 4000;//赋值具体的地址
int *p1 = Null, *p2 = 0; //定义指针变量,分别赋值Null和0
int *px = &x;//定义一个指针,指向x
printf("x = %d",*px);//因为px指向x,所以*px = x
px + 1;
//表示向前移动一个存储单元
printf("y = %d",*(px+1));//获取px指针下一个存储单元的内容
- 3.3关系运算
假设有指针变量px, py
。
px > py
表示px
指向的存储地址是否大于py
指向的地址
px == py
表示px
和py
是否指向同一个存储单元
px == 0
和px != 0
表示px
是否为空指针
int num[2] = {1, 3};
//将数组中第一个元素地址和第二个元素的地址赋值给 px、py
int *px = x[0], *py = x[1];
int *pz = x[0];
int *pn;
if(py > px){// py > px
printf("py 指向的存储地址大于 px 所指向的存储地址");
}
if(pz == px){ //pz 和 px 都指向 x[0]
printf("px 和 pz 指向同一个地址");
}
if(pn == NULL || pn == 0){//pn 没有初始化
printf("pn 是一个空指针");
}
三. 指针和数组
可以通过下标访问数组元素,学习指针之后,可以通过指针访问数组的元素。在数组中,数组名就是数组的首地址,结合指针和整数的加减,可以实现指针访问数组元素。
- 1.指向数组的指针
int nums[10],*p;
上面定义了一个数组,在定义时分配了10个连续的int内存空间。而一个数组的首地址为数组名nums,或者第一个元素的首地址也是数组的首地址。有两种方式让指针变量p指向数组nums。
方式1:p = nums;
方式2:p = &nums[0];
两种方式等价。
如下几种操作,使用指针操作数组:
1)*p = 1;
此操作为赋值操作,即将指针指向的存储空间赋值为1.此时p
指向数组nums
的第一个元素,则此操作将nums
第一个元素赋值为0
,即nums[0] = 1
。
2)p + 1;
此操作为指针加整数操作,即向前移动一个单元。此时p+1
指向nums[0]的下一个元素,即nums[1]。通过p+整数可以移动到想要操作的元素(此整数可以为负数)。
实例:
#include<stdio.h>
int main(){
int nums[5] = {2,3,1,4,2};
int *p = nums,i;
printf("nums[0] = %d\n",*p);
printf("nums[1] = %d\n",*(p+1));
for(i = 0;i<5;i++){
printf("nums[%d] = %d\n", i, *(p+i));
}
return 0;
}
结果显示:
注意:数组名不等价于指针,指针变量可以进行
p++
和&
操作,而这些操作对数组名是非法的。
- 2.字符指针与字符数组
在C语言中,没有提供字符串数据类型,但是可以通过字符数组和字符指针的方式存储字符串。 - 2.1字符数组方式
char word[] = "hello";
printf("%s",word);
- 2.2字符指针方式
指针方式操作字符串和数组操作字符串类似,可以把定义的指针看做事字符数组的数组名。在内存中存储大致如下:
char *sentence = "hello world!";
printf("%s",sentence);
//输出字符串
printf("%c",,sentence[0]);
//通过下标取字符
printf("%d",strlen(sentence));
//获取字符串长度,其中strlen
函数是string.h
库中的方法。
实例:
#include<stdio.h>
int main(){
char sentence[] = "hello world!";
char word[100];
char *ch = word;
int i;
for(i = 0;sentence[i]!='\0';i++){
*(ch + i) = sentence[i];
}
printf("ch = %s, world=%s\n",ch, word);
return 0;
}
结果显示:
四、指针与函数
- 1.函数参数为指针
#include<stdio.h>
void swap(int *x, int *y);
int main(){
int x = 20, y= 10;
swap(&x, &y);
printf("x = %d, y = %d\n",x, y);
return 0;
}
void swap(int *x, int *y){
int t;
t = *x;
*x = *y;
*y = t;
}
结果显示:
函数传入的参数是指针,所以调用
swap
方法后,x、y
的内容发生交换,,如果传入x、y
,那么交换旨在swap
中有效,在main
中没有交换。
- 2.函数的返回值为指针
返回值为指针的函数声明如下:
数据类型 *函数名(参数列表){
函数体;
}
实例:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
/* 要生成和返回随机数的函数 */
int * getRandom( )
{
static int r[10];
int i;
/* 设置种子 */
srand( (unsigned)time( NULL ) );
for ( i = 0; i < 10; ++i)
{
r[i] = rand();
printf("%d\n", r[i] );
}
return r;
}
/* 要调用上面定义函数的主函数 */
int main ()
{
/* 一个指向整数的指针 */
int *p;
int i;
p = getRandom();
for ( i = 0; i < 10; i++ )
{
printf("*(p + [%d]) : %d\n", i, *(p + i) );
}
return 0;
}
结果显示:
- 3.函数指针
函数指针是指向函数的指针变量。通常说指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数。函数指针可以像一般函数一样,用于调用函数、传递参数。
函数指针变量的声明:
typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数、返回值的函数指针类型
实例:
#include <stdio.h>
int max(int x, int y)
{
return x > y ? x : y;
}
int main(void)
{
/* p 是函数指针 */
int (* p)(int, int) = & max; // &可以省略
int a, b, c, d;
printf("请输入三个数字:");
scanf("%d %d %d", & a, & b, & c);
/* 与直接调用函数等价,d = max(max(a, b), c) */
d = p(p(a, b), c);
printf("最大的数字是: %d\n", d);
return 0;
}
结果显示: