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

目录
相关文章
|
4天前
|
C语言
C语言5 字符输出函数和格式输出函数
C语言5 字符输出函数和格式输出函数
10 1
|
6天前
|
存储 安全 编译器
【C++入门 四】学习C++内联函数 | auto关键字 | 基于范围的for循环(C++11) | 指针空值nullptr(C++11)
【C++入门 四】学习C++内联函数 | auto关键字 | 基于范围的for循环(C++11) | 指针空值nullptr(C++11)
|
6天前
|
算法 编译器 C语言
深入浅出C语言—【函数】下
深入浅出C语言—【函数】下
|
18天前
|
Java C语言 C++
定义C语言的int main()函数
定义C语言的int main()函数
|
4天前
|
存储 C语言
C语言6 字符串输入和格式输入函数
C语言6 字符串输入和格式输入函数
8 0
|
21天前
|
C语言
指针进阶(C语言终)
指针进阶(C语言终)
|
6天前
|
编译器 C语言
【C语言初阶】指针篇—下
【C语言初阶】指针篇—下
|
6天前
|
存储 C语言
【C语言初阶】指针篇—上
【C语言初阶】指针篇—上
|
17天前
|
搜索推荐 程序员 C语言
指针赋值与引用传递:C语言的基础知识与实践技巧
指针赋值与引用传递:C语言的基础知识与实践技巧