指针的进阶(1)上

简介: 指针的进阶(1)

关于指针这个知识点的主题,我们在前面已经初级阶段已经对指针有了大致的理解和应用了。我们知道了指针的概念:

1、指针就是地址,但口语中说的指针通常指的是指针变量,指针变量用来存放地址,地址唯一标识一块内存空间。

2、指针的大小是固定的4/8字节(32位平台/64位平台)。

3、指针是有类型的,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。

4、指针的运算。

5、二级指针:存放一级指针变量的地址的变量。

       理解:指针变量前的第一个*与变量结合表示它是指针,再往前面所有东西表示这个指针所指向对象的类型。

关于初阶指针有什么不太了解,大家可以点击下方链接学习一遍之后,再来学习进阶。

S6---初阶指针_wangjiushun的博客-CSDN博客

接下来,我们继续探讨指针的高级主题。

1.  字符指针

在指针的类型中我们知道有一种指针类型为字符指针:char*

一般我们使用:让一个字符指针指向一个字符变量

int main()
{
  char ch = 'a';
  char* pc = &ch;
  return 0;
}

还有一种使用方式:让一个字符指针指向一个常量字符串的首字符地址

#include<stdio.h>
int main()
{
  const char* pstr = "abcdef";
  printf("%s\n", pstr);
  return 0;
}

代码const char *pstr="abcdef";

很容易让我们误以为是把常量字符串存储到字符指针pstr里了,但是想一想一个字符指针怎么可能放得下7个字节的字符串呢?所以其本质是把字符串abcdef首字符的地址放到了pstr中。

图解:


知识点总结:

1.  常量字符串:

       ①常量字符串不能被修改:用const修饰(如果修改,程序崩溃);

       ②常量字符串出现在表达式中时,这个常量字符串的值是首字符的地址。

2.  const修饰的常变量:变量pstr不能被修改,但是pstr本质上还是一个变量,所以叫常变量

3.  %s-打印字符串,直到遇到'\0'才停止。  

有这么一道面试题:

#include<stdio.h>
int main()
{
  char str1[] = "hello bit";
  char str2[] = "hello bit";
  const char* str3 = "hello bit";
  const char* str4 = "hello bit";
  if (str1 == str2)
  {
    printf("str1 and str2 are same\n");
  }
  else
  {
    printf("str1 and str2 are not same\n");
  }
  if (str3 == str4)
  {
    printf("str3 and str4 are same\n");
  }
  else
  {
    printf("str3 and str4 are not same\n");
  }
  return 0;
}

大家猜猜运行的结果。

运行结果:

为什么会这样了?

知识点:

总结:

①(常量字符串,不能被修改)C/C++会把常量字符串存储到单独的一个内存区域,当有几个指针,指向同一个常量字符串的时候,它们实际会指向同一块内存。

②但是用相同的常量字符串去初始化不同的数组的时候,就会开辟不同的内存块。

③一个变量对应着一个唯一的空间。


图解:


所以str1和str2不同,str3和str4相同。


2.  指针数组

在初阶指针里我们已经学习一次指针数组,指针数组是一个存放指针的数组。

这里,我们在回顾一下:

整形数组-存放整形的数组;

字符数组-存放字符的数组

指针数组-存放指针(地址)的数组

指针数组的使用:使用一维数组模拟二维数组


代码1:存放字符指针的数组

#include<stdio.h>
int main()
{
  //存放字符指针的数组
  const char* arr[4] = { "abcd","hello","hehe","wang" };
  //打印
  int i = 0;
  for (i = 0; i < 4; i++)
  {
    printf("%s\n", arr[i]);
  }
  return 0;
}

图解:


代码2:存放整形指针的数组

#include<stdio.h>
int main()
{
  int arr1[4] = { 1,2,3,4 };
  int arr2[4] = { 2,3,4,5 };
  int arr3[4] = { 3,4,5,6 };
  //存放整形指针的数组
  int* arr[3] = { arr1,arr2,arr3 };
  //打印
  int i = 0;
  for (i = 0; i < 3; i++)
  {
    int j = 0;
    for (j = 0; j < 4; j++)
    {
      printf("%d ", *(arr[i] + j));//等价于arr[i][j]
    }
    printf("\n");
  }
  return 0;
}

图解:

总结:这就是和二维数组一样的,只不过我们是使用指针数组来模拟的。

3.  数组指针

3.1  数组指针的定义

数组指针是指针?还是数组?

答案是:指针。

我们已经熟悉:

整形指针-存放整形地址的指针-指向整形的指针 int*

字符指针-存放字符(字符串)地址的指针-指向字符(字符串)的指针 char*

那数组指针是:存放数组地址的指针-指向数组的指针

代码实例:

int main()
{
  //整形指针
  int a = 10;
  int* pi = &a;
  //字符指针
  char ch = 'w';
  char* pc = &ch;
  //数组指针
  int arr[10] = { 0 };
  //int* pa[10]是这样的吗,但是这不是指针数组,
  //pa先与[]结合了就是数组,
  int(*pa)[10] = &arr;
  return 0;
}

对数组指针的理解:pa先和*结合,说明pa是一个指针变量,然后指向的是一个大小为10个整形的数组。(即pa是一个指针,指向一个数组的指针,叫做数组指针)

数组指针后面的[]表示所指向的数组有几个元素

知识点:注意:[]的优先级要高于*的,所以必须加上()来保证pa先和*结合。

指针变量前的第一个*与变量结合表示它是指针,再往前面所有的东西表示这个指针所指向对象的类型。

接下来,我们深入了解数组名和&数组名

3.2  &数组名VS数组名

代码1:打印&数组名和数组名的地址观察

#include<stdio.h>
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  //打印数组名地址
  printf(" arr-->%p\n", arr);
  //打印&数组名地址
  printf("&arr-->%p\n", &arr);
  return 0;
}

运行结果:

运行之后我们发现:&数组名和数组名的地址是一样的。

难道两个是一样的吗?

代码2:&数组名和数组名+1

//代码2:
//数组名-数组首元素的地址
// &数组名-是数组的地址
// 数组首元素的地址和数组的地址从值的角度来看是一样的,但是意义不一样
// 指针的类型决定了指针+-整数的步长,指针解引用操作的时候的权限
//
#include<stdio.h>
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  printf(" arr---->%p\n", arr);//指针的类型:int*
  printf(" arr+1-->%p\n", arr + 1);//+1,向后跳4个字节
  printf("&arr---->%p\n", &arr);//指针的类型:int(*)[10]
  printf("&arr+1-->%p\n", &arr + 1);//+1,向后跳40个字节
  return 0;
}

运行结果:

图解:

根据上面的代码,我们发现:

数组名-数组首元素的地址

&数组名-是数组的地址

数组首元素的地址和数组的地址从值的角度来看是一样的,但是意义不一样。

一行是一个元素,二维数组是一个一维数组的数组。

//代码:二维数组的数组名
//二维数组的数组名是首元素地址-->第一行的地址
// 对于二维数组一行是一个元素
#include<stdio.h>
int main()
{
  int arr[3][4] = { 0 };
  printf("%p\n", arr);
  printf("%p\n", arr + 1);//跳过了16个字节
  return 0;
}

运行结果:

上机运行实践,也正是如此:arr+1跳40个字节,刚好是一行。

注意区分:二维数组的第一行第一个元素地址是&arr[0][0],

3.3  数组指针的使用

代码1:一维数组数组指针的使用,但是我们很少这样写代码

//代码1:一维数组数组指针的使用,但是我们很少这样写代码
#include<stdio.h>
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  //数组指针
  int(*p)[10] = &arr;
  //打印数组
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d ", (*p)[i]);//*p[i]这种写法的话,p先与[i]结合再解引用
  }
  return 0;
}

代码2:二维数组数组指针的使用

#include<stdio.h>
void print1(int arr[3][4], int r, int c)
{
  int i = 0;
  for (i = 0; i < r; i++)
  {
    int j = 0;
    for (j = 0; j < c; j++)
    {
      printf("%d ", arr[i][j]);
    }
    printf("\n");
  }
}
void print2(int(*p)[4], int r, int c)
{
  int i = 0;
  for (i = 0; i < r; i++)
  {
    int j = 0;
    for (j = 0; j < c; j++)
    {
      printf("%d ", *(*(p + i) + j));//等价于(*(p+i))[j],也等价p[i][j]
    }
    printf("\n");
  }
}
int main()
{
  int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };
  //调用函数打印数组
  print1(arr, 3, 4);
  print2(arr, 3, 4);
  return 0;
}

图解:

学了指针数组和数组指针我们一起回顾并看看下面代码的意思:

int arr[5];

int *parr1[10];

int (*parr2)[10];

int (*parr3[10])[5];


相关文章
|
5月前
|
C语言
指针进阶(C语言终)
指针进阶(C语言终)
|
5月前
|
机器学习/深度学习 搜索推荐 算法
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
|
5月前
|
C语言
指针进阶(回调函数)(C语言)
指针进阶(回调函数)(C语言)
|
5月前
|
存储 C语言 C++
指针进阶(函数指针)(C语言)
指针进阶(函数指针)(C语言)
|
5月前
|
编译器 C语言
指针进阶(数组指针 )(C语言)
指针进阶(数组指针 )(C语言)
|
5月前
|
搜索推荐
指针进阶(2)
指针进阶(2)
47 4
|
5月前
指针进阶(3)
指针进阶(3)
37 1
|
5月前
|
C++
指针进阶(1)
指针进阶(1)
42 1
|
5月前
|
存储 安全 编译器
C++进阶之路:何为引用、内联函数、auto与指针空值nullptr关键字
C++进阶之路:何为引用、内联函数、auto与指针空值nullptr关键字
41 2
|
5月前
|
Java 程序员 Linux
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
45 0