目录
一,指针本质分析
定义:指针就是内存地址;指针变量就是用来存放内存地址的变量。
分析:只要是变量就有地址和占用内存,所以指针变量也有地址,并且占用内存。
注意:指针是不占用内存地址的,其本质是地址,地址不占用空间。指针变量才占用内存空间。
指针变量内存所占空间大小sizeof(type*)或sizeof(p)取决于计算机的位数:
32位数占用4字节;
64位数占用8字节;
128位数占用16字节;
。。。
int i = 1;
int* p = &i;
p //指针变量,是i的地址。
*p //去内存地址中保存的值。
&p //指针变量占用的地址,是p的地址。
&和*是一对相反操作符,可以相互抵消。*&p=p
%p 打印地址。
1,传值调用和传址调用:
只有通过地址,才可以在函数内部修改函数外部的变量:
#include <stdio.h>
int swap(int* a, int* b)
{
int c = *a;
*a = *b;
*b = c;
}
int main()
{
swap(&aa, &bb);
printf("aa = %d, bb = %d\n", aa, bb);
return 0;
}
通过指针进行传址调用,交换了aa和bb的值。
2,常量与指针:
const int* p ; //p可变,p指向的内容不能变。
int const* p ; //p可变,p指向的内容不能变。
int* const p; //p不可变,p指向的内容能变。
const int* const p ; //p和p指向的内容均不能变。
口诀:左数又指(数:地址保存的数据;指:指向的地址)
const p :常量指针
二,数组本质分析
数组是相同变量的有序集合。
在数组a[ ]中:
-
a :数组第一个元素地址。
-
&a :数组名的地址
-
&a[0] : 数组第一个元素地址
1,数组名
数组名可以看作一个常量指针,意思就是指针的起始地址不能改变。在表达式中也就只能作为右值使用。
数组名不包含长度信息。
在下面场合数组名不能看作常量指针:
-
数组名作为sizeof操作符的参数;sizeof(a)表示数组a[ ]的大小;
-
数组名作为&运算符的参数;&a表示数组a[ ]的起始地址;
数组名其实并不是指针,不能等同于指针。
例如:int a[ ] ; int* p ;
&a表示数组a[ ]的起始地址;
&p表示指针p所占的内存地址;
2,数组参数
数组作为函数参数时,编译器将其编译为对应的指针。在这个过程中,数组的大小信息会丢失。所以一般情况下,会再定义一个参数表示数组大小。
void f(int a[]); <-> void f(int* a);
void f(int a[5]): <-> void f(int* a);
#include <stdio.h>
void func1(char a[5])
{
printf("In func1: sizeof(a) = %d\n", sizeof(a));
*a = 'a';
a = NULL;
}
void func2(char b[])
{
printf("In func2: sizeof(b) = %d\n", sizeof(b));
*b = 'b';
b = NULL;
}
int main()
{
char array[10] = {0};
func1(array);
printf("array[0] = %c\n", array[0]);
func2(array);
printf("array[0] = %c\n", array[0]);
return 0;
}
三,指针和数组本质分析
-
数组名可看作(但不是指针)指向数组第一个元素的常量指针。
-
数组名并不是数组地址,而是数组首元素的地址。
-
函数的数组参数会退化为指针。
1,指针和数组的运算:
1-1,指针运算:
p+n ;<-- >(unsigned int)p + n*sizeof(*p);
注意:当用数组地址对指针p赋值时,p加减n就是指向当前元素前后变化n个位置的元素。
——指针之间的运算:
指针之间之支持减法运算,参与减法运算的指针同类型必须相同。
p1-p2;<-->((unsigned int)p1 - (unsigned int)p2) / sizeof(type);
注意:只有两个指针指向同一数组时,指针相减才有意义,意义为指针所指元素的下标差。当不指向同一数组时结果未定义。
——指针的比较:
指针可以进行关系比较(<, >, <=, >=)
比较的前提是指向同一数组。
任意两个指针之间进行(==, !=)无限制,但两者类型需要相同。
1-2,数组运算:
首先我们知道了a和&a意义不同;真正体现出实际的不同就是在它们两者的运算。
a+1;<-->(unsigned int)a + sizeof(*a);
&a+1;<-->(unsigned int)(&a) + sizeof(*&a);
<-->(unsigned int)(&a) + sizeof(a);
sizeof(*a)代表数组元素的大小。
sizeof(a)代表整个数组的大小。
#include <stdio.h>
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int* p1 = (int*)(&a + 1);
int* p2 = (int*)((int)a + 1);
int* p3 = (int*)(a + 1);
printf("%d, %d, %d\n", p1[-1], p2[0], p3[1]);
return 0;
}
// p1[-1]将输出5,p2[0]输出乱码(有理由的乱码), p3[1]输出3
2,指针和数组之间的相互转换
数组名可以看作常量指针;
同样的,指针也可以看作数组名使用。
例如:int a[ ] = {0}; int* p = a; 那么a[i]<->p[i];
下标形式VS指针形式:
下标形式转换成指针形式:
a[i]<->*(a+i)<->*(i+a)<->i[a];
i[a]看起来很怪异,但是p[i]的另一种写法。
#include <stdio.h>
int main()
{
int a[5] = {0};
int* p = a;
int i = 0;
for(i=0; i<5; i++)
{
p[i] = i + 1;
}
for(i=0; i<5; i++)
{
printf("a[%d] = %d\n", i, *(a + i));
}
printf("\n");
for(i=0; i<5; i++)
{
i[a] = i + 10;
}
for(i=0; i<5; i++)
{
printf("p[%d] = %d\n", i, p[i]);
}
return 0;
}
值得注意的是,指针能看作数组名,但并不是数组名:
#include <stdio.h>
int main()
{
int a[]={1, 2, 3, 4, 5};
extern int* a;
printf("&a = %p\n", &a);
printf("a = %p\n", a);
printf("*a = %d\n", *a);
return 0;
}