【C语言初阶】 指针(下)

简介: 指针是C语言中一个重要概念,也是C语言的一个重要特色。正确灵活的运用它,可以使程序简洁、高效,我们要深入学习和掌握指针。指针个概念比较复杂,使用时也比较灵活,所以我们在学习时要多思考,多练习,在实践中掌握它。接下来就让我们进入指针的学习。


五、指针运算



  • 指针+- 整数
  • 指针-指针
  • 指针的关系运算

5.1 指针+- 整数

#include <stdio.h>
int main()
{
  int arr[10] = { 0 };
  int* p = arr;
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    *p = i;
    p++;
  }
  for(i = 0; i < 10; i++)
  {
    printf("%d ", *(p+i));
  }
    for(i = 0; i < 10; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}


46e132d0ca6e4efdaa3b9ac45bf55e21.png


5.2 指针-指针

ea48a45de868446d9aea70232a736ebd.png

指针-指针运算的前提条件是两个指针指向了同一块空间

应用:计算字符串的 长度

#include <stdio.h>
int my_strlen(char* s)
{
  char* start = s;
  while (*s != '\0')
  {
    s++;
  }
  return s - start;
}
int main()
{
  char arr[] = "abcdef";
  int len = my_strlen(arr);
  printf("%d\n", len);
  return 0;
}


5.3 指针的关系运算

#define N_VALUES 5
float values[N_VALUES];
for(vp = &values[N_VALUES]; vp > &values[0];)
{
    *--vp = 0;
}


f71744340c9049ca927f32f9f5f8823f.png


将代码简化,修改为:

#define N_VALUES 5
float values[N_VALUES];
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
    *vp = 0;
}


这种写法在绝大部分的编译器上是可以顺利完成任务的,但是我们还是应该避免这样写,因为标准并不保证它可行。

标准规定:

允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与 指向第一个元素之前的那个内存位置的指针进行比较。

ce14acce5f6f4258b78bcc7448f0ed4e.png


可以拿p和p2比,但是不可以拿p和p1比


六、指针和数组



指针和数组虽然是不同的东西,但它们之间有着一些联系,接下来就让我带着大家学习两者之间的相同点和不同点。


c6ff1ff25ad9469b8ce0b8ed8d6e5218.png


根据上述结果我们可以得出数组名就是数组首元素的地址。

将数组视为指针,也催生除了数组和指针的密切关系

1. int arr[10] = {1,2,3,4,5,6,7,8,9,0};
2. int *p = arr;//p存放的是数组首元素的地址


这里声明了数组arr和指针p。p初始化的值是arr,因为数值名arr会被解释为&arr[0],所以p中存放的值为&arr[0]。也就是说p会被初始化为指向数组arr的起始元素arr[0]的地址。

注意:p指向的是起始元素,不是整个数组。


6.1数组名不是首元素地址的情况


1. 作为sizeof运算符的操作数出现

     sizeof(数组名) 不会生成指向起始元素的指针的长度,而是生成数组整体的长度。

2. 作为取地址操作符的操作数出现

    &数组名 不是指向起始元素的地址的指针,二是指向数组整体的指针。

1. int a[5];
2. int b[5];
3. a=b;       //错误


这样赋值,那么数组a的地址就会被改变。因此 ,赋值表达式的左操作数不能是数组。

“不可以使用赋值运算符改变指向数组起始元素的指针”

int main()
{
    int arr[] = {1,2,3,4,5,6,7,8,9,0};
    int *p = arr; //指针存放数组首元素的地址
    int sz = sizeof(arr)/sizeof(arr[0]);
    for(i=0; i<sz; i++)
   {
        printf("&arr[%d] = %p   <====> p+%d = %p\n", i, &arr[i], i, p+i);
   }
    return 0;
}


31d0268c02314963bcf77db7ad35f5e4.png


6.2 通过指针引用数组元素


  • 下标法:如a[i]的形式
  • 指针法:如*(a+i)或*(p+i)。其中a是数组名,p是指向数组元素的指针变量,初始值p=a
(1)下标法
int main()
{
  int arr[] = { 1,2,3,4,5,6,7,8,9 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}
(2)通过数组名计算数组元素地址,找出元素
int main()
{
  int arr[] = { 1,2,3,4,5,6,7,8,9 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  int i = 0;
  for (i = 0; i < sz; i++)
  {
    printf("%d ", *(arr + i));
  }
  return 0;
}
(3)用指针变量指向数组元素
int main()
{
  int arr[] = { 1,2,3,4,5,6,7,8,9 };
  int* p = arr;
  int i = 0;
  for (p = arr; p < arr + 9; p++)
  {
    printf("%d ", *p);
  }
  return 0;
}


注意:在使用指针变量指向数组元素是要注意

1.可以通过改变指针变量的值指向不同的元素,如指针变量p来指向元素,用p++使p的值不断改变从而指向不同的元素。

2.要注意指针变量的当前值。


6.3以变量名和数组名作为函数参数的比较


实参类型 变量名  数组名
要求形参的类型 变量名 数组名或指针变量
传递的信息 变量的值 实参数组首元素的地址
通过函数调用能否改变实参的值 不能改变实参变量的值             能改变实参变量的值             


注意:实参数组名代表的是一个固定的地址,或者说是指针变量,但形参数组名并不是一个固定的地址,而是按指针变量处理。


七、二级指针


ad8efcd961ae44479eabbb553415807a.png


int main()
{
  int a = 10;
  int* p = &a;     //p是一级指针,指针变量也是变量,是变量就有地址,在内存上开辟空间
  int** pp = &p;   //pp是二级指针变量,二级指针变量是用来存放一级指针变量的地址
}


二级指针的运算有:

  • *ppa 通过对ppa中的地址进行解引用,这样找到的是 pa , *ppa 其实访问的就是 pa
1. int a = 10;
2. int* p = &a;    
3. int** pp = &p;
4. *pp=&a;     //等价于 p = &a


  • **ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是 a
1. **pp = 30;
2. //等价于*p = 30
3. //等价于a = 30


八、指针数组



8.1什么是指针数组

一个数组,若其元素均为指针类型数据,称为指针数组。也就是说,指针数组中的每一个元素都存放一个地址。下面定义一个指针数组:

int* p[4];

定义指针数组的形式


类型名 * 数组名[数组长度]

int main()
{
  int arr1[] = { 1,2,3,4,5 };
  int arr2[] = { 2,3,4,5,6 };
  int arr3[] = { 3,4,5,6,6 };
  int* parr[] = { arr1,arr2,arr3 };
  int i = 0;
  for (i = 0; i < 3; i++)
  {
    int j = 0;
    for (j = 0; j < 5; j++)
    {
      printf("%d ", *(parr[i] + j));
    }
    printf("\n");
  }
  return 0;
}


c4bf770b0c5a4a41a6d9d9e570d8eec3.png


本次的内容到这里就结束啦。希望大家阅读完可以有所收获,同时也感谢各位读者的支持。文章有问题可以在评论区留言,博主一定认真认真修改,以后写出更好的文章。

相关文章
|
1月前
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
86 3
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
1月前
|
存储 编译器 C语言
【C语言】指针大小知多少 ?一场探寻C语言深处的冒险 !
在C语言中,指针的大小(即指针变量占用的内存大小)是由计算机的体系结构(例如32位还是64位)和编译器决定的。
56 9
|
1月前
|
安全 程序员 C语言
【C语言】指针的爱恨纠葛:常量指针vs指向常量的指针
在C语言中,“常量指针”和“指向常量的指针”是两个重要的指针概念。它们在控制指针的行为和数据的可修改性方面发挥着关键作用。理解这两个概念有助于编写更安全、有效的代码。本文将深入探讨这两个概念,包括定义、语法、实际应用、复杂示例、最佳实践以及常见问题。
45 7
|
2月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
161 13
|
2月前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
2月前
|
算法 C语言
C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项
本文深入讲解了C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项,通过实例演示了文件操作的基本流程,帮助读者掌握这一重要技能,提升程序开发能力。
137 3
|
2月前
|
存储 算法 程序员
C 语言指针详解 —— 内存操控的魔法棒
《C 语言指针详解》深入浅出地讲解了指针的概念、使用方法及其在内存操作中的重要作用,被誉为程序员手中的“内存操控魔法棒”。本书适合C语言初学者及希望深化理解指针机制的开发者阅读。
|
2月前
|
程序员 C语言
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门。本文深入探讨了指针的基本概念、声明方式、动态内存分配、函数参数传递、指针运算及与数组和函数的关系,强调了正确使用指针的重要性,并鼓励读者通过实践掌握这一关键技能。
44 1
|
2月前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。
|
2月前
|
存储 NoSQL 编译器
C 语言中指针数组与数组指针的辨析与应用
在C语言中,指针数组和数组指针是两个容易混淆但用途不同的概念。指针数组是一个数组,其元素是指针类型;而数组指针是指向数组的指针。两者在声明、使用及内存布局上各有特点,正确理解它们有助于更高效地编程。