指针(2)

目录

  • [1. 数组名的理解](#1. 数组名的理解)
  • [2 . 使用指针访问数组](#2 . 使用指针访问数组)
  • [3. ⼀维数组传参的本质](#3. ⼀维数组传参的本质)
  • [4. 冒泡排序](#4. 冒泡排序)
  • [5. ⼆级指针](#5. ⼆级指针)
  • [6. 指针数组](#6. 指针数组)
  • [7. 指针数组模拟⼆维数组](#7. 指针数组模拟⼆维数组)

1. 数组名的理解

我们知道数组名就是数组首元素的地址,如arr

&数组名就是数组第一个元素的地址,也是数组首元素的地址,如&arr

c 复制代码
#include <stdio.h>

int main()
{	
	char arr[] = {1,2,3};
	printf("arr      =%p\n", arr);
	printf("&arr[0]  =%p\n", &arr[0]);
	return 0;
}

如果数组名是首元素的地址,那么如下代码怎么解释?

c 复制代码
#include <stdio.h>
 int main()
 {
 int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
 printf("%d\n", sizeof(arr));
 return 0;
 }

可以看到输出的是40,那么arr就不是数组首元素的地址了吗

其实数组名就是数组首元素(第一个元素)的地址是对的,但是有两个例外:

  • sizeof(数组名),sizeof中存放数组名,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节
  • &数组名,这里的数组名表示整个数组,取出的是整个数组的地址
    除此之外,任何地方出现数组名,都表示数组首元素的地址
    我们可以来观察如下实验
c 复制代码
#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("&arr[0]   = %p\n", &arr[0]);
	printf("&arr[0]+1 = %p\n", &arr[0] + 1);
	printf("arr       = % p\n", arr);
	printf("arr+1     = % p\n", arr+1);
	printf("&arr      = % p\n", &arr);
	printf("&arr+1	  = % p\n", &arr + 1);
	return 0;
}

我们可以观察到&arr[0],取出首元素的地址,+1(&arr[0]+1)跳过4个字节(跳过一个元素)

arr(首元素的地址),arr+1(首元素的地址+1)跳过4个字节(跳过一个元素)

为什么跳过4个字节呢?因为它们的类型都是一样的,都是int类型,所以是跳过一个整型
那么为什么&arr是跳过40个字节呢(10个元素,每个元素4个字节),因为它的类型不一样,是int(
)[10]类型的,具体的会在下一篇指针(3)里会讲到

2 . 使用指针访问数组

有了前面的知识,我们就可以通过指针访问数组了

c 复制代码
#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;
	size_t sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for ( i = 0; i < sz; i++)
	{
		//printf("%d ", arr[i]);
		//我们知道arr是数组首元素的地址,p里放的也是arr首元素的地址那么p = arr,
		//我们就可以写下如下代码
		printf("%d ", p[i]);
		//printf("%d", *(p + i));//p里放的是arr首元素的地址,p+i跳过i个元素,
		//printf("%d", *(arr + i));//上面的注释我们可知p = arr,那么也可以这样写
		//printf("%d", *(i + arr));//加法时支持交换律的,如:(6+8) =(8+6),那么我们这样写也是可以的,但是不提倡
		//printf("%d", i[arr]);//不提倡,要求简介明了


	}
	return 0;
}

arr[i] 应该等价于*(arr+i),数组元素的访问在编译器处理的时候,也是转换成⾸元素的地址+偏移量求出元素的地址,然后解引⽤来访问的

3. ⼀维数组传参的本质

既然我们可以在函数外部计算数组的元素,那么可以把数组传给一个函数后,在函数内部求一个数组的元素个数吗?

c 复制代码
#include <stdio.h>
void test(int arr[])
{
	int sz2 = sizeof(arr) / sizeof(arr[0]);
	printf("sz2 = %d\n", sz2);
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz1 = sizeof(arr) / sizeof(arr[0]);
	printf("sz1 = %d\n", sz1);
	test(arr);
	return 0;
}

显然是不能的,在test函数内部, sizeof(arr)/sizeof(arr[0]),我们知道sizeof(arr[0])是4,既然结果是1的话,那么sizeof(arr)也是4,说以在传数组时,只传了数组首元素的地址

所以函数形参的部分我们因该使用指针变量来接收首元素的地址,我们在函数内部写sizeof(arr)计算的是一个地址的大小(单元字节),而不是数组的大小(单位字节)

正因为参数部分是指针,说以在函数内部没办法计算数组元素个数。

c 复制代码
#include <stdio.h>

void test1(int arr[])//参数写成数组形式,本质上还是指针
{
	printf("%d\n", sizeof(arr));
}

void test2(int* arr)//参数写成指针形式
{
	 printf("%d\n", sizeof(arr));//计算⼀个指针变量的⼤⼩
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	test1(arr);
	test2(arr);

	return 0;
}

4. 冒泡排序

c 复制代码
#include <stdio.h>
#include <assert.h>
void bubble_sort(int* arr, size_t sz)
{
    assert(arr);
    int i = 0;
    int j = 0;
    int temp = 0;
    for ( i = 0; i < sz - 1; i++)
    {
        int a = 1;
        for ( j = 0; j < sz - 1 - i; j++)
        {
            if (*(arr + j) > *(arr + j + 1))
            {
                temp = *(arr + j);
                *(arr + j) = *(arr + j + 1);
                *(arr + j + 1) = temp;
                a = 0;
            }
        }
        if (a)
        {
            break;
        }
    }
}


int main()
{
    int arr[] = { 3,1,7,5,8,9,0,2,4,6 };
    size_t sz = sizeof(arr) / sizeof(arr[0]);
    bubble_sort(arr, sz);
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

5. ⼆级指针

指针变量也是变量,是变量就有地址,那么指针变量的地址因该存哪里?

存二级指针里

c 复制代码
#include <stdio.h>
int main()
{
	int a = 0;
	int* pa = &a;
	int** ppa = &pa;
	return 0;
  • *ppa通过对二级指针的解引用,找到pa,*ppa其实访问的就是pa
c 复制代码
int b = 20;
*ppa = &b//等价于pa = &b
  • **ppa先通过*ppa找到pa,在通过对pa进行解引用的操作:*pa找到a
c 复制代码
**ppa = 30;
//等价于*pa = 30
//等价于a = 30

6. 指针数组

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

我们类比一下,整型数组是存放整型的数组,字符数组是存放字符的数组

那么指针数组就是存放指针的数组

指针数组的每块区域都是用来存放地址(指针)的

指针数组每个元素都是一个地址,可以指向一块空间

7. 指针数组模拟⼆维数组

c 复制代码
#include <stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	int* parr[3] = { arr1,arr2,arr3 };
	int i = 0;
	int j = 0;
	for ( i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			printf("%d ", parr[i][j]);//parr[i]是访问parr数组的元素,
			//parr里存放的是一维数组首元素的地址
			//parr[i][j]访问的就是一维数组j下标的元素
		}
		printf("\n");
	}
	return 0;
 }
相关推荐
前端小L2 分钟前
回溯算法专题(五):去重与剪枝的双重奏——攻克「组合总和 II」
算法·剪枝
拾贰_C12 分钟前
[python ]anaconda
开发语言·python
TL滕13 分钟前
从0开始学算法——第三天(数据结构的多样性)
数据结构·笔记·学习·算法
V1ncent Chen15 分钟前
人工智能的基石之一:算法
人工智能·算法
VBA633717 分钟前
VBA数据库解决方案第二十五讲:工作表中数据在数据表中批量删除
开发语言
7ioik19 分钟前
新增的类以及常用的方法有哪些?
java·开发语言·python
无限进步_19 分钟前
深入理解顺序表:从原理到完整实现
c语言·开发语言·数据结构·c++·算法·链表·visual studio
芯联智造20 分钟前
【stm32简单外设篇】- 水银开关
c语言·stm32·单片机·嵌入式硬件
繁华似锦respect22 分钟前
C++ 无锁队列(Lock-Free Queue)详细介绍
linux·开发语言·c++·windows·visual studio
兩尛24 分钟前
欢乐周末 (2025B卷
算法