目录
数组
int arr[] = { 1,2,3,4,5 };
(1)数组名 arr 表示数组首元素的地址
(2)数组首元素的地址还可以用下标的方法表示: &arr[0]
(3)由(1)和(2)可得 arr = &arr[0],都表示数组首元素的地址
数组首元素的地址在内存中的存储如下图:
只有下面两种情况表示整个数组的地址(整个数组的地址和数组首元素的地址是有区别的):
(1)sizeof(arr) 中的arr,sizeof计算的是整个数组的大小,单位是字节
(2)在数组名前加取地址符& ,即 &arr
数组的地址在内存中的位置如下图:
指针
地址与指针的联系
地址是指针类型的值,它表示内存中某个特定位置的位置标识。在 C/C++ 中,指针类型用于存储内存中某个对象或变量的地址。
指针类型可以与相应的数据类型相关联。例如,int*
是一个指向整数类型的指针,char*
是一个指向字符类型的指针,等等。指针类型定义了指针所指向对象的数据类型。
在 C/C++ 中,地址通常以十六进制表示,表示为一个无符号整数。这个无符号整数值表示内存中的位置。
当你使用 &
运算符获取一个变量的地址时,得到的值是指向该变量的指针,其类型与变量的类型相对应。例如,&n
表示变量 n
的地址,是一个 int*
类型的指针。
通过使用指针类型,我们可以在程序中引用和操作内存中的特定位置,从而实现对变量和对象的间接访问。
指针变量
指针的知识没这么难,有的人觉得难是因为没有搞清楚里面的逻辑,搞清楚后你就会发现东西也就这么点,没自己想象中的难。
int a = 10;
int* p;
p = &a;
对上述代码的解释如下:
(1)* 表示 p 是指针变量 ,前面的 int 表示 p 指向的是整型类型的对象,只能将整型类型的对象赋给 p
(2)指针变量就是存放地址的,而且只能存放地址,p = &a 表示将 a 的地址赋给指针变量 p
上述代码也可写成如下:
int a = 10;
int* p = &a;
表示直接初始化指针变量 p 的值为 a 的地址。
在细致一点,指针在内存中的存储如下:
变量会在内存中申请一块空间,这块空间会有相应的地址,无论是指针变量还是普通变量都会在内存中申请一块空间。
假设变量 a 在内存中的地址为0x0012ff40,指针变量的地址为0x0012ff50
由上图可看出,变量 a 空间中存放的是10,指针变量 p 存放的是 a 的地址。
解引用操作符
* 除了在定义指针变量 p 时表示 p 是指针变量之外,它还具有解引用功能,*p 的意思就是通过p中存放的地址找到指向的空间,而 a 的地址赋给了 p ,所以 *p 可通过a的地址找到 a 即 *p = a
比如以下代码:
int a = 10;
int* p = &a;
printf("%d\n", *p);
运行的结果如下:
以上结果就表明了 *p = a ,而 a = 10 所以 *p = 10
我们可以通过 *p 改变 a 的值
int a = 10;
int* p = &a;
*p = 20;
printf("%d\n", a);
运行结果如下
* p的运作过程如下图所示:
解引用的对象是一个地址,地址表示在内存中一块空间的位置,解引用地址就是根据地址找到相应的空间,这个空间里存放着数据,通俗的说就是将这个地址下的空间中的数据直接展现出来。
例如 a=10;&a=0x12ff40 ; *a = 10 ;(可以看出*(&a)== a==10,&与*可互相抵消) 或者:a=10; p=&a ; *p = 10 (指针变量p存放了a的地址,通过解引用操作符可以找到a中存放的数据10)
指针变量的大小
32位平台下地址是32个bit位,指针变量的大小是4个字节
64位平台下地址是64个bit位,指针变量大小是8个字节
注意指针变量的大小和类型无关,只要是指针类型的变量,在相同平台下,大小都是相同的
指针变量类型的意义
指针的类型决定了对指针解引用的时候有多大的权限(一次能操作几个字节)。
比如:char* 的指针解引用就只能访问一个字节,而int*的指针的解引用就能访问4个字节。
指针的运算
指针+-整数
一、指针在内存中加一个整数
#include<stdio.h>
int main()
{
int n = 10;
char* pc = (char*)&n;//将变量n的地址转换为指向字符型数据的指针类型后赋值给pc。
int* pi = &n;
printf("&n = %p\n", &n);
printf("pc = %p\n", pc);
printf("pc+1 = %p\n", pc+1);
printf("pi = %p\n", pi);
printf("pi+1 = %p\n", pi+1);
return 0;
}
上述代码中注意 char* pc = (char*)&n;
将 n
的地址赋给 pc
,而不进行强制类型转换,编译器会发出类型不匹配的警告或错误。因为 n
是一个整数变量,其地址是一个 int*
类型的指针,而将其直接赋给 char*
类型的指针 pc
是不兼容的。
如果你想以字符型数据的方式访问整数变量 n
的内存表示,可以使用类型转换来实现。正确的做法是将 n
的地址强制转换为 char*
类型的指针。
运行结果:
可得结论:char*类型的指针变量+1跳过1个字节,int*类型的指针变量+1跳过了4个字节。
这就是指针变量的类型差异带来的变化。
指针的类型决定了指针向前或向后走一步有多大(距离)。
二、指针在数组中加一个整数
指针加减整数还是指针
数组在内存中的存储是连续存放的,只要知道第一个元素的地址,顺腾摸瓜就能找到后面的所有元素。
int arr[] = {1,2,3,4,5,6};
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6 };
int* p = &arr[0];//数组首元素地址放入指针中
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i++)
{
printf("%d ", *(p + i));//p + i 指针加整数
}
return 0;
}
运行结果:
数组首元素的地址放在了指针变量 p 中,所以指针就是这个首元素的地址,指针加减整数相当于地址加减整数
上面的数组 arr 是int类型的数组,所以指针加1表示跳过4个字节来到下一个元素的地址(int类型的数组每个元素在内存中占4个字节)
如果是 char 类型的数组,指针加 1 跳过 1 个字节来到下一个元素的地址(char类型的数组每个元素占1个字节)。
跳过几个字节取决于这个数组是什么类型
如上图,因为数组的首元素地址给了指针变量 p ,所以可得指针 p + 1 跳过 4 个字节 等于第二个元素的地址,p +3 跳过 3 * 4 个字节表示第4个元素的地址
通过解引用即可得这个地址的值,即 *(p+1) = 2 、*(p+3) = 4
同理地址可用 arr 来表示
arr 表示首元素的地址, arr + 1就表示第二个元素的地址,arr + 2 表示第三个元素的地址
由于 arr+1 = &arr[1] ,所以 *(arr+1) = 2
数组的地址加1如下图所示:
&arr 加 1 表示来跳过整个数组
指针 - 指针
在C语言中,两个指针相减的结果是两个指针之间的偏移量(以元素为单位)。具体来说,如果 ptr1
和 ptr2
是两个指针,它们指向同一数组中的不同元素(或是指向同一块内存的不同位置),那么 ptr2 - ptr1
的结果将是 ptr2
相对于 ptr1
的偏移量。
int arr[5];
int* ptr1 = &arr[1];
int* ptr2 = &arr[4];
在这个例子中,ptr1 指向 arr
数组的第二个元素,ptr2
指向 arr
数组的最后一个元素。ptr2 - ptr1
的结果将是 3
,因为 ptr2
相对于 ptr1
偏移了 3
个元素。
需要注意的是,两个指针必须指向同一块内存或同一数组中的元素才能执行减法操作。
指针的关系运算
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
while (p < (arr + sz)-1)
{
printf("%d ", *p);
p++;
}
return 0;
}
指针的大小可以比较, (arr+sz)-1 表示数组中最后一个元素的地址,p 表示第一个元素的地址
一维数组传参的本质
数组是可以传递给函数的,我们讨论一下数组传参的本质。
我们验证一下数组能不能全部传入函数中去:
#include<stdio.h>
void test(int arr[])
{
int sz1 = sizeof(arr) / sizeof(arr[0]);
printf("%d\n", sz1);
}
int main()
{
int arr[5] = { 1,2,3,4,5 };
test(arr);
return 0;
}
运行结果:
结果显示传入的数组只有1个元素,但数组 arr 有5个元素
我们发现在函数内部是没有正确获得数组元素的个数。
数组名是数组首元素的地址,那么在数组传参的时候,传递的是数组名,也就是说本质上数组传参传递的是数组首元素的地址。
所以函数形参的部分理论上应该使用指针变量来接收首元素的地址。
则代码可改为:
#include<stdio.h>
void test(int* arr)
{
int sz1 = sizeof(arr);
printf("%d\n", sz1);
}
int main()
{
int arr[5] = { 1,2,3,4,5 };
test(arr);
return 0;
}
sizeof(arr) 计算的是一个地址的大小(单位是字节)。
上述代码的形参部分改为了指针。
总结:一维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。
冒泡排序
以下代码都是函数参数部分为指针的形式:
#include<stdio.h>
void bubble(int* arr, int sz)
{
for (int i = 0; i < sz - 1; i++)//排的趟数等于sz-1
{
int flag = 0;//结束标志,如果一趟就排序完成则无需再进行第2趟,提高了效率。
for (int j = 0; j < sz - 1 - i; j++)//sz个数两两交换共sz-1对 ,每趟找出一个最大的排到末尾,所以每趟少排一个,所以减去i
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = 1;
}
}
if (flag == 0)
{
return;
}
}
}
void print(int* arr, int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", *(arr + i));
}
}
int main()
{
int arr[] = { 13,20,15,11,23,5,10 };
int size = sizeof(arr) / sizeof(arr[0]);
bubble(arr, size);//冒泡排序
print(arr, size);//打印
return 0;
}
创作不易,喜欢的话点个赞,谢谢