指针和数组
数组名
数组名字是数组的首元素地址,但它是一个常量,不允许被赋值
具体的说:
在C中,在几乎所有使用数组名的表达式中,数组名的值是一个指针常量,也就是数组第一个元组的地址。
它的类型取决于数组元素的类型:
如果数组是int类型,那么 数组名的类型 就是“指向int的常量指针”;
如果数组是其他类型,那么 数组名的类型 就是“指向其他类型的常量指针”。
注意这个值是指针常量,而不是指针变量。你不能修改常量的值。
这很好理解,指针常量所指向的是内存中数组的起始位置,如果修改这个指针常量,唯一可行的操作就是把整个数组移动到内存的其他位置。但是,在程序完成链接之后,内存中数组的位置是固定的,所以当程序运行时,再想移动数组就为时已晚了,因此,数组名的值就是一个指针常量。
#include<stdio.h>
int main(void)
{
int arr[] = { 10,4,5,1,2,3,8,9,13};
//【注意:数组名是一个常量,不允许赋值】
// 数组名 是 数组首元素地址
// arr = 100; //报错!!!!这个操作是相当于修改首元素的地址
int* p;
p = arr;
printf("%p\n", p); // 输出:006FFCAC
printf("%d\n", *p); // 输出:10
printf("%p\n", arr); // 输出:006FFCAC
printf("%d\n", *arr); // 输出:10
return 0;
}
指针数组
指针数组,它是数组,数组的每个元素都是指针类型。
#include <stdio.h>
int main()
{
//指针数组
int *p[3];
int a = 1;
int b = 2;
int c = 3;
int i = 0;
p[0] = &a;
p[1] = &b;
p[2] = &c;
for (i = 0; i < sizeof(p) / sizeof(p[0]); i++ )
{
printf("%d, ", *(p[i]));
}
printf("\n");
return 0;
}
#include <stdio.h>
int main()
{
int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* p2 = &a[2]; //第2个元素地址
int* p1 = &a[1]; //第1个元素地址
printf("p1 = %p, p2 = %p\n", p1, p2);
int n1 = p2 - p1; //n1 = 1
int n2 = (int)p2 - (int)p1; //n2 = 4
printf("n1 = %d, n2 = %d\n", n1, n2);
return 0;
}
指针操作数组元素
直接看代码和注释吧!
#include<stdio.h>
int main(void)
{
int arr[] = {10,4,5,1,2,3,8,9,13};
//【注意:数组名是一个常量,不允许赋值】
// 数组名 是 数组首元素地址
// arr = 100; //报错!!!!这个操作是相当于修改首元素的地址
int* p;
p = arr;
printf("%p\n", p); // 输出:006FFCAC
printf("%d\n", * p); // 输出:10
printf("%p\n", arr); // 输出:006FFCAC
printf("%d\n", *arr); // 输出:10
printf("%d\n", sizeof(arr) ); //输出:36
printf("%d\n", sizeof(arr[0])); //输出:4
printf("%d\n", sizeof(arr) / sizeof(arr[0])); //输出:9
for (int i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
{
//printf("%d\t", arr[i]); //输出:10 4 5 1 2 3 8 9 13
printf("%d\t",p[i]); //输出:10 4 5 1 2 3 8 9 13
}
//问题:对于用地址取值来说,到底加多少?
printf("%d\n",*(arr+1)); //输出:4
printf("%d\n",*(arr+4)); //输出:2
//原因:
//指针类型变量 +1 操作,等同于内存地址 + sizeof(int) , 因为我们这里指针类型为 int* p; 或 int arr[];
//如果是一个int *,+1的结果是增加一个int的大小
//如果是一个char *,+1的结果是增加一个char大小
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
//这是地址,我们转成10进制输出;可以看出每个地址是相加了 4 的,也就是一个int类型大小
printf("%d\t", (p+i)); //输出:5241048 5241052 5241056 5241060 5241064 5241068 5241072 5241076 5241080
//printf("%d\t", *(p+i)); //输出:10 4 5 1 2 3 8 9 13
//printf("%d\t", p[i]); //输出:10 4 5 1 2 3 8 9 13
//先算 *p 再算 p++
printf("%d\t",*p++); //输出:10 4 5 1 2 3 8 9 13
}
//这是地址,我们转成10进制输出;
printf("%d\n",arr); // 输出:5241048
printf("%d\n", arr + 1); //输出:5241052
//经过上面 *p++ 操作后,p的地址和arr不一样了,【而且,这个时候指针指向是不是最后一个元素地址,而是下一个,可以说是下标越界了一个,也就是野指针了】
printf("%d\n",p); // 输出:5241084
return 0;
}
指针和数组区别
#include<stdio.h>
int main(void)
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//指向数组的指针
int* p = arr;
//区别:
//p是变量;arr是常量
//p是一个指针占4字节大小;arr是一个数组占40字节大小
printf("指针类型大小:%d\n", sizeof(p)); //输出:4
printf("数组大小:%d\n", sizeof(arr)); //输出:40
return 0;
}
冒泡排序为什么要传入长度len
#include<stdio.h>
void BubbleSort(int arr[], int len)
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main(void)
{
int arr[] = { 10,4,5,1,2,3,8,9,13};
int len = sizeof(arr) / sizeof(arr[0]);
BubbleSort(arr, len);
for (int i = 0; i < len; i++)
{
printf("%d\n", arr[i]);
}
return 0;
}
对于一个冒泡排序算法,我们需要传入一个len,那,既然传入了arr,我们为什么不在排序函数里面直接计算 出len呢,省去一个参数传递。
但是,下面程序证明这是不行的。
#include<stdio.h>
void BubbleSort(int arr[]) //这里 arr 相当于传入的是一个指针(类型)
{
//主要原因:数组作为函数参数传递时,实际上传递的是数组的 首地址 。相当于是一个指针
//那么,一个指针类型,是有固定大小的,比如在32位操作系统就是4字节大小
int len = sizeof(arr) / sizeof(arr[0]);
printf("%d\n", len); // 输出:1 这相当于是 4/4=1
int len1 = sizeof(arr); // arr 相当于是一个指针
printf("%d\n", len1); // 输出:4
}
int main(void)
{
int arr[] = { 10,4,5,1,2,3,8,9,13};
BubbleSort(arr);
return 0;
}
也就是说,下面代码的对 arr 的一系列操作并不是对数组的,而是对指针的操作
void BubbleSort(int arr[], int len)
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
即之前展示的这种方式:
#include<stdio.h>
int main(void)
{
int arr[] = { 10,4,5,1,2,3,8,9,13};
int* p;
p = arr;
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
//这是地址,我们转成10进制输出;可以看出每个地址是相加了 4 的,也就是一个int类型大小
printf("%d\t", (p+i));
//printf("%d\t", *(p+i)); //输出:10 4 5 1 2 3 8 9 13
//printf("%d\t", p[i]); //输出:10 4 5 1 2 3 8 9 13
//先算 *p 再算 p++
printf("%d\t",*p++); //输出:10 4 5 1 2 3 8 9 13
}
return 0;
}
参考:
[1]C基础讲义2018修订版(黑马程序员)
[2]C和指针/(美)里科(Reek,K.A.)著;徐波译.——北京:人民邮电出版社,2008.4(2011.10重印)