【C语言】深入理解指针(2)

C语言指针深度解析与应用

1.数组名的理解

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

可以看出数组名是数组首元素(第一个元素)地址。

在两种情况下数组名不代表数组首元素地址,而是代表整个数组:

  • sizeof(数组名):sizeof中单独放置数组名,数组名表示整个数组,计算的是整个数组的大小,单位是字节。

  • &数组名:这里的数组名表示整个数组,取出的是整个数组的地址(与数组首元素地址的值相同,但数组地址+1是跳过整个数组)。

数组名是数组首元素地址时,类型是type*,+1就是跳过一个sizeof(type)。

数组名是数组地址时,类型是type*[],+1就是跳过整个数组。

除了这两种情况,其余的所有情况下的数组名都表示数组首元素地址。

2.使用指针访问数组

数组的特点:在内存中连续存放。所以可以使用指针访问数组。

#include<stdio.h>
int main()
{
        int arr[10] = { 0 };
        int i = 0;
        int sz = sizeof(arr) / sizeof(arr[0]);
        int* p = arr;
        // 输入
        for (i = 0; i < sz; i++)
        {
                scanf("%d", p + i);
                //scanf("%d",arr+i); // 也可以这样写
        }
        // 输出
        for (i = 0; i < sz; i++)
        {
                printf("%d ", *(p + i));
        }
        return 0;
}

以上代码可以输入数组元素并输出。

在代码中,数组名arr是数组首元素地址,可以赋值给p,p的初始值也是数组首元素地址,说明arr和p在这里是等价的。我们可以用arr[i]访问数组,也可以用p[i]。

#include<stdio.h>
int main()
{
        int arr[10] = { 0 };
        int i = 0;
        int sz = sizeof(arr) / sizeof(arr[0]);
        int* p = arr;
        // 输入
        for (i = 0; i < sz; i++)
        {
                scanf("%d", p + i);
                //scanf("%d",arr+i); // 也可以这样写
        }
        // 输出
        for (i = 0; i < sz; i++)
        {
                printf("%d ", p[i]);
        }
        return 0;
}

在输出语句中,将*(p+i)换成了p[i],也是可以正常打印的,所以本质上*(p+i)等价于p[i],同理arr[i]也等价于*(arr+i)。数组元素的访问在编译器处理的时候,也是转换成首元素地址+偏移量求出元素的地址,然后解引用来访问的。

arr[i] == *(arr+i) == *(i+arr) == i[arr](可以用但不推荐)。

[]是下标引用操作符

3.一维数组传参的本质

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

运行结果显示sz和sz2的值不相等,说明在函数内部没有得到正确的数组大小。因为数组名本质上是数组首元素的地址,所以在传参时传递数组名,本质上传递的是数组首元素的地址。

所以如果函数需要使用数组大小,应该再定义一个形参用来接受数组大小。

/#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] = { 0,1,2,3,4,5,6,7,8,9 };
        test1(arr);
        test2(arr);
        return 0;
}

总结:一维数组传参,形参的部分可以写成数组的形式也可以写成指针的形式。本质上还是指针变量。

4.冒泡排序

冒泡排序的核心思想就是:两两相邻的元素进行比较

#include<stdio.h>
void bubble_sort(int arr[], int sz)
{
        int i = 0;
        for (i = 0; i < sz-1; i++) // 趟数,冒泡排序一共要走(sz-1)趟
        {
                int j = 0;
                for (j = 0; j < sz - i - 1; j++) // 每趟要比较(sz-i-1)次
                {
                        if (arr[j] > arr[j + 1]) // 相邻两个元素进行比较
                        {
                                // 若前面的数大于后面的数,就交换顺序
                                int temp = arr[j];
                                arr[j] = arr[j + 1];
                                arr[j + 1] = temp;
                        }
                }
        }
}
int main()
{
        int arr[10] = { 5,3,4,6,7,2,1,0,9,8 };
        int sz = sizeof(arr) / sizeof(arr[0]);
        bubble_sort(arr, sz);
        for (int i = 0; i < sz; i++) // 打印数组
        {
                printf("%d ", arr[i]);
        }
        return 0;
}

冒泡排序的优化:可以减少程序运行的趟数(在数组有序的情况下)

#include<stdio.h>
void bubble_sort(int arr[], int sz)
{
        int i = 0;
        for (i = 0; i < sz-1; i++) // 趟数,冒泡排序一共要走(sz-1)趟
        {
                int flag = 1; // 定义flag,若本趟发生交换就置为0
                int j = 0;
                for (j = 0; j < sz - i - 1; j++) // 每趟要比较(sz-i-1)次
                {
                        if (arr[j] > arr[j + 1]) // 相邻两个元素进行比较
                        {
                                flag == 0; // 若发生交换,说明本趟是无序的
                                // 若前面的数大于后面的数,就交换顺序
                                int temp = arr[j];
                                arr[j] = arr[j + 1];
                                arr[j + 1] = temp;
                        }
                }
                if (flag == 1) // 如果一趟结束后,flag == 1,说明这一趟没有发生交换,数字是有序的
                {
                        break;
                }
        }
}
int main()
{
        int arr[10] = { 5,3,4,6,7,2,1,0,9,8 };
        int sz = sizeof(arr) / sizeof(arr[0]);
        bubble_sort(arr, sz);
        for (int i = 0; i < sz; i++) // 打印数组
        {
                printf("%d ", arr[i]);
        }
        return 0;
}

5.二级指针

所有变量都有地址,指针变量也是变量,指针变量的地址存放在二级指针中。

#include<Stdio.h>
int main()
{
        int a = 0;
        int* pa = &a; // 一级指针
        int** ppa = &pa; // 二级指针
        return 0;
}

int * pa:int说明pa指向的类型为int型,*说明pa是指针变量

int ** ppa:int*说明ppa指向的类型为int*,第二个*说明ppa是指针变量

int**是二级指针类型,用来存储一级指针的地址

对于二级指针的运算:

  • *ppa:对ppa中存放的地址进行解引用,找到的是一级指针pa

*ppa = &a:相当于pa = &a

  • **ppa:先通过ppa找到pa,再通过对pa进行解引用找到a

**ppa = 30:等价于*pa = 30,a = 30

6.指针数组

int arr[] 是整型数组

Char arr[] 是字符数组

int* arr[] 是指针数组

指针数组是存放指针的数组,每个元素存放的都是地址(指针)。

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

#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[] = { 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]);
                }
                printf("\n");
        }

        return 0;
}


感谢观看,欢迎各位大佬批评指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值