1

C语言--深入理解指针

C语言--深入理解指针

一. 指针的概念

要知道指针的概念,要先了解变量在内存中是如何存储的。在存储时,内存被分为一块一块的,每一块都有一个特有的编号。而这个编号可以暂时理解为指针,就是酒店的门牌号一样。

  • 1.变量和地址
    看下面代码
void main(){
  int x = 10, int y = 20;
}

代码中声明了两个变量x,y。把内存当做一个酒店,而每个房间就是一块内存。那么int x = 10;int y = 20;的实际含义是:去酒店订了两个房间,门牌号暂时用pxpy表示,让10住进px,让y住进py,其中门牌号就是px、py,也就是房间的地址。xy在这里可理解为具体的房间,房间x的门牌号(地址)是px,房间y的门牌号(地址)是py。而10和20,通过pxpy两个门牌,找到房间,住进xy

  • 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=&ixi的地址,*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 赋值给 x8 赋值给 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 表示 pxpy 是否指向同一个存储单元
    px == 0px != 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;
}

结果显示:

posted @ 2023-12-04 21:28  Bonne_chance  阅读(15)  评论(0编辑  收藏  举报
1