C语言学习记录——7000+字长文-复习&学习指针(指针、地址、指针变量、指针与数组、指针与函数、指针数组、多级指针)一

简介: C语言学习记录——7000+字长文-复习&学习指针(指针、地址、指针变量、指针与数组、指针与函数、指针数组、多级指针)一

指针与地址

地址也称内存地址。

概念:

在计算机中,所有的运行数据都存放在内存储器中,内存储器的一个字节占用一个内存单元。为方便访问这些内存单元,我们为每个内存单元进行了编号,这些编号就称为内存地址。

我们简单理解为:内存储器为一个酒店,内存单元是住客们,内存地址就相当于他们所住房间的门牌号。

例如:使用取地址运算符&时,就是在获取变量的内存地址

指针

概念:

根据内存地址就可以找到相应的内存单元,所以通常也把地址称为指针。

看下面的代码:

#include <stdio.h>
int main()
{
  int n;
  int *p = &n;
  printf("%p\n", p);
  return 0;
}

其运行结果为:  0032F824

该结果即是变量n的内存地址,也称为其指针。

 

指针变量

概念:

在C语言中,允许用一个变量来存放指针,这种变量就称为指针变量。

其与一般变量一样,要先声明后使用

定义形式:

类型说明符(数据类型) * 指针变量名;

其中类型说明符表示该指针变量所指向的变量是什么数据类型,* 是指说明符,当*单独使用时,则为解引用操作符。

例如:

#include <stdio.h>
int main()
{
  int n = 5;
  int *p = &n;
  printf("%d\n", *p); //此处对P进行解引用操作,通过p的地址找到变量n
  return 0;
}

运行结果为:   5

需要注意一点的是:* 是单目运算符,结合性为“左结合”,后面只能接指针变量。

 

指针与数组

指针与一维数组

指向一维数组元素的指针

如果定义了一个一维数组,此时再定义一个指针,并把数组的第一个元素的起始地址赋值给该指针,则该指针就指向了这个一维数组。

例如:

#include <stdio.h>
int main()
{
  int arr[5] = { 1,2,3,4,5 },i;
  int *p;
  p = &arr[0];
  for (i = 0; i < 5; i++)
  {
    printf("arr[%d] = %d\n", i, *(p + i));
  
  }
  return 0;
}

其运行结果为:

指针p与一维数组arr的关系

image.png

指针与整数的加减运算

当指针指向数组元素后,加上或减去一个整数n,表示把指针的当前位置(指向某数组元素)向后或向前移动n个元素的位置。

例如:

#include <stdio.h>
int main()
{
  int arr1[5] = { 1,2,3,4,5 };
  float arr2[5] = { 1.0,2.0,3.0,4.0,5.0 };
  int *p;
  float *b;
  p = &arr1[0];
  b = &arr2[0];
  printf("指针加整数前\n%d\n%.2f\n", *p, *b);
  printf("\n");
  p += 3;
  b += 4;
  printf("指针加整数后\n%d\n%.2f\n", *p, *b);
  return 0;
}

其运行结果为:

 

指针的增量运算

指向数组元素的指针变量的值可以改变。如p++是合法的,使得p指向下一个数组元素。而arr++是非法的,因为arr是数组的首地址,是一个常量。

例如:

#include <stdio.h>
int main()
{
  int arr[5] = { 1,2,3,4,5 },*p;
  p = &arr[0];
  printf("增量运算前\n%d\n", *p);
  p++; //或者++p; 
  //此时指针在原来的基础上加上了4(查阅资料,根据VC++6.0编译环境int型数据占4个字节)
  //从而使得指针指向数组的下一个元素
  printf("\n");
  printf("增量运算后\n%d\n", *p);
  return 0;
}

其运行结果为:

 

指针与指针的减法运算

当两个指针指向同一片连续的存储单元时,指针的减法运算的结果是一个整数,其值为这两个指针变量中的地址之差除以数据类型的长度。

例如:

#include <stdio.h>
int main()
{
  //建立一维数组和两个指针变量
  float arr[5] = { 1.0,5.0,2.0,6.0,9.0 },*p,*b;
  p = &arr[4];
  b = &arr[1];
 
  //计算float型数据占多少个字节
  printf("float型数据占%d个字节\n\n", sizeof(arr[0]));
 
  //打印出p和b的地址,不难算出(16进制转10进制来计算)相减之后结果为12
  printf("p中存放的地址:%p\nb中存放的地址:%p\n两个地址相减得到结果为12\n\n", p, b);
 
  //打印出两个指针相减的结果
  printf("两个指针相减的结果为:%d\n(12 / 4)", p - b);
  return 0;
}

其运行结果为:

 

指针与指针的关系运算

当两个指针指向同一片连续的存储单元时,两个指针可以进行关系运算,即表示它们之间的位置关系。

int arr[10],*p,*b;
 
p == b;  //表示判断p和b是否指向同一数组元素
 
p > b;   //表示判断p所指元素是否在b所指元素的后面
 
p < b;   //表示判断p所指元素是否在b所指元素的前面

用这一知识点,将数组arr中n个整数按相反的顺序存放

#include <stdio.h>
#define N 5
int main()
{
  int arr[N], i, tmp, * p, * q;
  printf("请随机输入%d个数字\n", N);
  for (i = 0; i < N; i++)
  {
    printf("第%d个:", i + 1);
    scanf("%d", &arr[i]);
  }
  p = arr;               //p指向数组中的第一个元素
  q = arr + (N - 1);     //q指向数组中的最后一个元素
  while (p < q)          //逆序存放数组中的元素
  {
    tmp = *p;
    *p = *q;
    *q = tmp;
    p++;
    q--;
  }
  printf("逆序输出为:");
  for (i = 0; i < N; i++)
    printf("%d ", arr[i]);
  printf("\n");
  return 0;
}


其运行结果为:

 

指针与二维数组

二维数组的元素及地址

定义一个二维数组int arr[ 3 ][ 4 ];表示二维数组有3行4列共12个元素,在内存中按行存放。

二维数组arr也可以理解为由3个元素组成,即arr[ 0 ]、arr[ 1 ]、arr[ 2 ],而每个元素是一个一维数组,且都包含了4个元素。

二维数组在内存中的存储方式示意图 image.png

通过行指针引用二维数组的元素

行指针

行指针是一种特殊的指针,它专门用于指向一维数组,定义行指针的一般形式如下:

类型说明符(* 指针名) [常量表达式];

其中,类型说明符代表行指针所指一维数组的元素类型,指针名与前面的指针说明*必须用括号括起来,常量表达式是指针所指向的一维数组的长度。

例如:

int arr[3][4];
int (*p)[4] = &arr[0];
//也可以写成int (*p)[4] = arr;

上面语句中定义了一个指向一维数组的指针p,指向包含4个int型元素的一维数组arr[ 0 ]。此时p + 1指向下一个一维数组arr[ 1 ],因此,*(p + 1)+ 2是arr[ 1 ][ 2 ]的地址,那么


*(*(p + 1)+ 2)就是二维数组元素arr[ 1 ][ 2 ]的值

故,以下几种方式都可以表示二维数组元素arr[ i ][ j ].

int arr[3][4], i , j ;
int (*p)[4] = &arr[0];
//二维数组arr,arr首元素的地址p
 
* (*(arr + i) + j);
 
* (*(p + i) + j);
 
p[i][j];

通过列指针引用二维数组的元素

当指针p指向二维数组的首元素之后,p + 1将指向二维数组的第二个元素,p + 2将指向二维数组的第三个元素,以此类推

例如:

#include <stdio.h>
int main()
{
  int arr[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} };
  int *p = &arr[0][0];
  printf("%d\n", *p);
  printf("%d\n", *(p + 1));
  return 0;
}

其运行结果为:

 

用指针法输入输出二维数组中的各个元素:

 

//行指针法
#include <stdio.h>
int main()
{
  int arr[3][4], i, j;
  int (*ptr) [4] = &arr[0];
  for (i = 0; i < 3; i++)
  {
    for (j = 0; j < 4; j++)
    {
      scanf("%d", *(ptr + i) + j);
    }
  }
  printf("\n");
  for (i = 0; i < 3; i++)
  {
    for (j = 0; j < 4; j++)
    {
      printf("%d\t", *(*(ptr + i) + j));
    }
    printf("\n");
  }
  return 0;
}
//列指针法
#include <stdio.h>
int main()
{
  int arr[3][4],i,j;
  int *p = &arr[0][0];
  for (i = 0; i < 3; i++)
  {
    for (j = 0; j < 4; j++)
    {
      scanf("%d", p+i+j);
    }
  }
  printf("\n");
  for (i = 0; i < 3; i++)
  {
    for (j = 0; j < 4; j++)
    {
      printf("%d\t", *(p + i + j));
    }
    printf("\n");
  }
  return 0;
}

以上运行结果都为:

 

指针与字符数组

字符数组在C语言中可以用字符串常量初始化,也可以整串地输入或输出。

由于指针与数组的等价性,字符指针也有如下特点:


C语言学习记录——7000+字长文-复习&学习指针(指针、地址、指针变量、指针与数组、指针与函数、指针数组、多级指针)二:https://developer.aliyun.com/article/1530355

目录
相关文章
|
1月前
使用指针访问数组元素
【10月更文挑战第30天】使用指针访问数组元素。
36 3
|
19天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
23天前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
23天前
|
容器
在使用指针数组进行动态内存分配时,如何避免内存泄漏
在使用指针数组进行动态内存分配时,避免内存泄漏的关键在于确保每个分配的内存块都能被正确释放。具体做法包括:1. 分配后立即检查是否成功;2. 使用完成后及时释放内存;3. 避免重复释放同一内存地址;4. 尽量使用智能指针或容器类管理内存。
|
23天前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。
|
26天前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
48 4
|
1月前
使用指针访问数组元素
【10月更文挑战第31天】使用指针访问数组元素。
42 2
|
1月前
|
算法 索引
单链表题+数组题(快慢指针和左右指针)
单链表题+数组题(快慢指针和左右指针)
39 1
|
2月前
|
存储
如何使用指针数组来实现动态二维数组
指针数组可以用来实现动态二维数组。首先,定义一个指向指针的指针变量,并使用 `malloc` 为它分配内存,然后为每个子数组分配内存。通过这种方式,可以灵活地创建和管理不同大小的二维数组。
|
2月前
|
存储
如何通过指针数组来实现二维数组?
介绍了二维数组和指针数组的概念及其区别,详细讲解了如何使用指针数组模拟二维数组,包括定义与分配内存、访问和赋值元素、以及正确释放内存的步骤,适用于需要动态处理二维数据的场景。