C语言指针的定义,使用,指针和指针之间的基本和关系运算,指针与数组的结合使用,二级指针及多级指针的定义和使用,指针数组和数组指针的定义和使用

简介: C语言指针的定义,使用,指针和指针之间的基本和关系运算,指针与数组的结合使用,二级指针及多级指针的定义和使用,指针数组和数组指针的定义和使用

指针的初始化:适用于在不确定指针的指向时。相当于int b=0

int a = 10;
int* pa = &a;
int* p = NULL;//NULL——初始化指针,给指针赋初值

当我们不想使用某个指针但又不想其变成野指针时,就可以将其指向NULL

int a = 10;
int* pa = &a;
*pa = 20;
pa = NULL;//此刻pa不指向a,指向空间已经被释放,将该指针置成空指针。

如果我们对指针pa进行解引用操作,会发生什么情况呢?

在调试过程中,编译器报错pa是nullptr,此时的指针pa是指向Null的,而这个空间是不能被访问的,因为它是一个空指针。


指针的运算:指针和整数的运算,指针和指针的运算,指针的关系运算

通过指针打印数组元素:

#include<stdio.h>
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }, * pa,i,sz;
  pa = arr;//指针pa指向数组arr
  sz = sizeof(arr) / sizeof(arr[0]);
  for (i = 0; i < sz; i++)
  {
    printf("%d ", *pa);//对指针pa进行解引用
    pa++;//指针指向下一个依次进行
}
return 0;
}
1 2 3 4 5 6 7 8 9 10

同样的,指针的步长不仅可以是1,也可以是2,3,,,,但需要注意的点是,跳转的次数不要超过数组的范围,下面举例说明:

#include<stdio.h>
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }, * pa,i,sz;
  pa = arr;
  for (i = 0; i < 5; i++)//指针的步长为2,数组中的元素个数是10个,因此i最大值为4,
              //如果i的值还是等于数组元素的个数此时就会发生报错,原因是越界了
  {
    printf("%d ", *pa);
    pa+=2;
  }
  return 0;
}

另外,指针的指向不仅可以是++,也可以是–,实现方法就是让指针初始指向发生改变,下面举例说明:

#include<stdio.h>
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }, * pa,i;
  pa = &arr[9];//&不要忘记加
  for (i = 0; i <5; i++)
  {
    printf("%d ", *pa);
    pa-=2;
  }
  return 0;
}
10 8 6 4 2

指针-指针:大地址—小地址得到元素个数,小地址—大地址的绝对值是元素个数

举例:

#include<stdio.h>
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }, * pa,i;
  pa = arr;
  printf("%d\n", &arr[9] - &arr[2]);//大地址-小地址
  printf("%d\n", &arr[5] - &arr[1]);
  printf("%d\n", &arr[2] - &arr[9]);//小地址-大地址 
  printf("%d\n", &arr[1] - &arr[7]);
  return 0;
}
7
4
-7
-6

注意:指针-指针适用于指向同一个数组的指针,而不能用于字符型和整形指针之间,由于字符型指针的步长为1,而整形为4,两者类型不兼容,无法进行相减。

使用指针计算字符串长度:

#include<stdio.h>
int my_strlen(char* str)
{
  char* start = str;
  char* end = str;
  while (*end != '\0')//使指针end指向字符串末位置
  {
    end++;
  }
  return end - start;//使用大地址减小地址,返回字符串长度
}
int main()
{
  char arr[] = "good";
  int len = my_strlen(arr);
  printf("%d", len);
  return 0;
}
4

指针的关系运算:比较指针的大小

方法一:

#define N_VALUES 5
float values[N_VALUES];
float* vp;
for (vp = &values[N_VALUES]; vp > &values[0];)//如果满足后一个指针大于初始值指针,则将其赋值为0,再指向上一个,依次进行
{
  *--vp = 0;
}

实现过程:指针的初始位置是下标为5的元素,判断该地址大于元素首地址,进入循环,先进行vp–,使它指向上一个元素,再将其赋值为0,直到指向首元素的地址,结束循环。

方法二:

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

实现过程:指针初始指向下标为4的元素的地址,判断该地址大于首元素地址,进入循环,将该地址中值赋值为0,再指向上一个元素,直到不满足vp>=&values[0],结束循环。

第二种方法在绝大部分的编译器上是可以顺利完成,但是我们应该还是避免使用第二种方法,而推荐第一种写法,因为标准并不保证它可行。


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


指针和数组:

通常情况下,数组名表示数组首元素地址,但是在两种特殊的情况下,数组名不是首元素地址。

1:&数组名,此时数组名不是首元素的地址,数组名表示整个数组,&数组名取出的是整个数组的地址。

2:sizeof(数组名),此时,数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小


举例:

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

通过上面的输出结果,我们发现这三种形式输出的结果是数值完全一样的,那么&数组名和其他两种表现形式的差别在哪里呢?

我们可以通过给他们地址加一,再来看输出结果。

#include<stdio.h>
int main()
{
  int arr[10] = { 0 };
  printf("%p\n", arr);
  printf("%p\n", arr+1);//在arr地址的基础上增加了4个字节的大小,也就是arr[1]的地址
  printf("%p\n", &arr[0]);
  printf("%p\n", &arr[0]+1);//在arr[0]地址的基础上增加了4个字节的大小,也就是arr[1]的地址
  printf("%p\n", &arr);
  printf("%p\n", &arr+1);//在&arr地址的基础上增加了40个字节,也就是直接跳到了下一个数组的首地址
}

可见&数组名和数组首元素地址以及数组名,只是在数值上都与数组的首地址相等,但含义是不一样的。

通过指针访问数组元素:

#include<stdio.h>
int main()
{
  int arr[10] = { 0 },i;
  int* p = arr;
  for (i = 0; i < 10; i++)
  {
    printf("%p======%p\n", p + i, &arr[i]);//p+i是通过指针打印数组元素地址,&arr[i]是通过下标打印数组元素地址
  }
  return 0;
}

通过输出结果我们不难发现,无论使用指针还是下标打印数组元素地址,其结果是相同的。

在数组中使用指针访问数组元素:

#include<stdio.h>
int main()
{
  int arr[10] = { 0 },i;
  int* p = arr;
  for (i = 0; i < 10; i++)
  {
    *(p + i) = i;//对指针进行解引用
    printf("%d ", arr[i]);//printf("%d ",*(p+i))这两种输出方式都可以
  }
  return 0;

}

0 1 2 3 4 5 6 7 8 9

二级指针,三级指针的定义和使用:n级指针解引用n次。

#include<stdio.h>
int main()
{
  int arr[10] = { 0 };
  int* p = arr;//p为一级指针(*代表p是指针,int代表p指向的类型)
  int* * pa = &p;//pa为二级指针(*代表pa是指针,int*代表pa指向的类型)
  int** * ppa = &pa;//ppa为三级指针(*代表ppa是指针,int**代表ppa指向的类型)
  printf("%d\n", *p);
  printf("%d\n", **pa);
  printf("%d\n", ***ppa);
  return 0;
}
10
10
10

指针数组和数组指针:指针数组本质是数组(存放指针的数组),数组指针本质是指针

指针数组的定义和使用:

#include<stdio.h>
int main()
{
  int arr[10];//整型数组,数组中的元素都是整型
  int a = 10, b = 20, c = 30,i;
  int* arr1[3] = { &a,& b,&c };//指针数组,数组中的元素都是指针
  for (i = 0; i < 3; i++)
  {
    printf("%d ", *(arr1[i]));//对数组中的指针依次进行解引用
  }
  return 0;
}
10 20 30
相关文章
|
23天前
|
存储 C语言 数据格式
【C语言基础考研向】03混合运算和printf讲解
本文分为两部分。第一部分介绍了C语言中的混合运算与类型强制转换的重要性,通过实例展示了当整型数进行除法运算且结果为小数时,必须使用类型转换才能正确存储浮点数结果。第二部分详细讲解了`printf`函数的功能与使用方法,包括格式化输出不同类型数据的基本语法,并通过具体示例演示了如何利用字段宽度和对齐方式来控制输出格式,帮助读者更好地理解和掌握输出格式的控制技巧。
27 10
|
23天前
|
存储 编译器 C语言
【C语言基础考研向】09 一维数组
数组是一种有序集合,用于存储相同类型的数据,便于统一操作与管理。例如,将衣柜底层划分为10个格子存放鞋子,便于快速定位。在C语言中,数组定义格式为 `类型说明符数组名[常量表达式];`,如 `int a[10];` 表示定义了一个包含10个整数的数组。数组初始化时可以直接赋值,也可以部分赋值,且数组长度必须固定。数组在内存中连续存储,访问时需注意下标范围,避免越界导致数据异常。数组作为参数传递时,传递的是首地址,修改会影响原数组。
|
23天前
|
存储 C语言
【C语言基础考研向】10 字符数组初始化及传递和scanf 读取字符串
本文介绍了C语言中字符数组的初始化方法及其在函数间传递的注意事项。字符数组初始化有两种方式:逐个字符赋值或整体初始化字符串。实际工作中常用后者,如`char c[10]=&quot;hello&quot;`。示例代码展示了如何初始化及传递字符数组,并解释了为何未正确添加结束符`\0`会导致乱码。此外,还讨论了`scanf`函数读取字符串时忽略空格和回车的特点。
|
24天前
|
安全 编译器 C语言
C语言常量的定义与使用的注意点
在 C 语言中,常量是在程序运行期间值不变的量,通过字面值、`#define` 或 `const` 关键字定义。字面常量直接在代码中表示固定值,如整数 `100`、浮点数 `3.14`、字符 `&#39;A&#39;` 和字符串 `&quot;Hello, World!&quot;`;`#define` 用于定义宏,如 `#define PI 3.14159`;`const` 则定义不可变变量,如 `const int daysInWeek = 7`。常量可用于数组大小、循环边界等场景,并能提升代码的可读性和可维护性。使用时需注意作用域、类型安全和命名,避免直接使用数字(魔法数字)。
|
26天前
|
存储 算法 C语言
数据结构基础详解(C语言):单链表_定义_初始化_插入_删除_查找_建立操作_纯c语言代码注释讲解
本文详细介绍了单链表的理论知识,涵盖单链表的定义、优点与缺点,并通过示例代码讲解了单链表的初始化、插入、删除、查找等核心操作。文中还具体分析了按位序插入、指定节点前后插入、按位序删除及按值查找等算法实现,并提供了尾插法和头插法建立单链表的方法,帮助读者深入理解单链表的基本原理与应用技巧。
|
26天前
|
存储 人工智能 C语言
C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
本文详细讲解了C语言中的指针,包括指针变量的定义与引用、指向数组及字符串的指针变量等。首先介绍了指针变量的基本概念和定义格式,随后通过多个示例展示了如何使用指针变量来操作普通变量、数组和字符串。文章还深入探讨了指向函数的指针变量以及指针数组的概念,并解释了空指针的意义和使用场景。通过丰富的代码示例和图形化展示,帮助读者更好地理解和掌握C语言中的指针知识。
|
26天前
|
存储 人工智能 C语言
C语言程序设计核心详解 第六章 数组_一维数组_二维数组_字符数组详解
本章介绍了C语言中的数组概念及应用。数组是一种存储同一类型数据的线性结构,通过下标访问元素。一维数组定义需指定长度,如`int a[10]`,并遵循命名规则。数组元素初始化可使用 `{}`,多余初值补0,少则随机。二维数组扩展了维度,定义形式为`int a[3][4]`,按行优先顺序存储。字符数组用于存储字符串,初始化时需添加结束符`\0`。此外,介绍了字符串处理函数,如`strcat()`、`strcpy()`、`strcmp()` 和 `strlen()`,用于拼接、复制、比较和计算字符串长度。
|
2月前
|
存储 编译器 数据处理
【编程秘籍】解锁C语言数组的奥秘:从零开始,深入浅出,带你领略数组的魅力与实战技巧!
【8月更文挑战第22天】数组是C语言中存储同类型元素的基本结构。本文从定义出发,详述数组声明、初始化与访问。示例展示如何声明如`int numbers[5];`的数组,并通过下标访问元素。初始化可在声明时进行,如`int numbers[] = {1,2,3,4,5};`,编译器自动计算大小。初始化时未指定的元素默认为0。通过循环可遍历数组,数组名视为指向首元素的指针,方便传递给函数。多维数组表示矩阵,如`int matrix[3][4];`。动态数组利用`malloc()`分配内存,需用`free()`释放以避免内存泄漏。掌握这些技巧是高效数据处理的基础。
56 2
|
2月前
|
存储 算法 搜索推荐
C语言中数组
C语言中数组
36 0
|
2月前
|
存储 编译器 程序员
【C语言篇】从零带你全面了解数组(超详细)
有时候,数组在创建的时候,我们需要给定⼀些初始值,这种就称为初始化。