指针(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;
 }
相关推荐
是苏浙2 小时前
零基础入门C语言之C语言实现数据结构之顺序表经典算法
c语言·开发语言·数据结构·算法
5967851542 小时前
C# 弹出框DialogForm
开发语言·c#
FnTop2 小时前
实用教程:打造支持参数配置的 Git Bash 文件清理脚本
开发语言·git·bash
提娜米苏2 小时前
Bash Shell脚本学习——唇读数据集验证脚本
开发语言·学习·bash
lkbhua莱克瓦242 小时前
Java基础——集合进阶5
java·开发语言·集合·泛型
Jerry丶Li3 小时前
二十七、通信接口
c语言·stm32·单片机·嵌入式硬件
太理摆烂哥3 小时前
数据结构之红黑树
数据结构
聪明努力的积极向上3 小时前
【C#】System.Text.Encoding.Default 属性在framework和.netcore中的区别
开发语言·c#·.netcore
hnjzsyjyj3 小时前
洛谷 B4241:[海淀区小学组 2025] 统计数对 ← STL map
数据结构·stl map