指针的概念
系统给虚拟内存的每个存储单元分配了一个编号。从0x00 00 00 00 到0xff ff ff ff
这个编号称为地址。
指针就是地址。
指针变量:是个变量,用来存放一个地址。
1、无论什么类型的地址,都是存储单元的编号,在32位平台下都是4个字节。即任何类型的指针变量都是4个字节大小。
2、对应类型的指针变量,只能存放对应类型的变量的地址。
指针是一个地址,而指针变量是存放地址的变量。
指针变量的定义方法
1、简单的指针变量
数据类型 *指针变量名;
int *p;//定义了一个指针变量p
在定义指针变量的时候*时用来修饰变量的,说明变量p是个指针变量。变量名是p。
2、关于指针的运算符
&取地址、*取值
int a = 0x1234abcd;
int *p;
p = &a; //把a的地址给p赋值,&是取地址符号。
int num = *p;//*p代表取值,就相当于把a的值给了num,和num=a效果一样。
p保存了a的地址,也可以说p指向了a。
int * pointer_1=&a,*pointer_2=&b;
说明:在定义指针变量时要注意:
(1)指针变量前面的“*”表示该变量的类型为指针型变量。指针变量名是pointer_1和
pointer_2,而不是*pointer_1和 *pointer_2。这是与定义整型或实型变量的形式不同的。
上面程序不应写成:“*pointer_1=&a;"和“*pointer_2=&b;”。因为a的地址
是赋给指针变量pointer_1,而不是赋给*pointer_1(即变量a)。
指针的大小
*代表的是该变量是指针型变量。
p1是指针变量,存储的是地址,大小位32位系统4字节大小,固定不变。
前边的类型修饰符表明地址存储的数据类型。
char* p1;
short int* p2;
int* p3;
long int* p4;
float* p5;
double* p6;
通过指针引用数组
一个变量有地址,一个数组包含若于元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。指针变量既然可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个指针变量中),所谓数组元素的相针就是数组元素的地址。
引用
int a[10]={1,3,5,7,9,11,13,15,17,19};//定义a为包含10个整型数据的数组
int * pi;//定义p为指向整型变量的指针变量
p=&a[0];//把a[0]元素的地址赋给指针变量p
p=a; //p的值是数组a首元素(即a[0])的地址
注意:数组名不代表整个数组,只代表教组首元素的地址。上述“p=a;”的作用是“把
a数组的首元素的地址赋给指针变量p”,而不是“把数组a各元素的值赋焓p”。
取值
*p;//是a[0]的值。
*a;//是a[0]的值。
*(p+1);//是a[1]的值。
*(a+1);//是a[1]的值。
如果指针变量p已指向数组中的一个元素,则p+1指向同一数组中的下一个元
素,p-1指向同一数组中的上一个元素
指针运算
做减法的护的结果是两个指针之间有多少个元素。
int a[10] = { 1,3,5,7,9,11,13,15,17,19 };
int* p, * p1;
p = a;
p1 = &a[3];
printf("%d", p1-p);
指针数组
数组中元素位相同类型的指针变量。
定义方式: int *p[5];
int* p[5];
int a = 100;
int b[10] = { 1,2,3,4,5,6,7,8,9,0 };
printf("%d\n", sizeof(p));//指针数组的大小
p[0] = &a;//数组0位置的指针变量指向a
printf("%p\n", p[0]);
printf("%p\n", &a);
printf("%d\n", *p[0]);
p[1] = &b[2];
printf("%d\n", *p[1]);
//hello、china等并不是存放在数组中,而是文字常量区中
//name[0]存放的是hello字符串的首地址。
char *name[5] = {"hello", "china", "beijing", "test"};
for (int i = 0; i < 4; i++)
{
printf("%s\n", name[i]);
}
指针的指针
指针的指针,即指针的地址。
int a = 100;
int *p;
p = &a;
int **p1;//*p1表示是一个指针,存放int*类型。
p1 = &p;
int ***p2;//*p2表示是一个指针,存放int**类型。
字符串和指针
字符串的概念
字符串就是以‘\0’结尾的若干的字符的集合。
字符串的地址,是第一个字符的地址。
字符串的存储形式
1、数组中
char str[11] = "hello world";
str = {'o', 'o'};
strcpy(str, "what");
scanf("%s", str);
2、文字常量区
char *str = "hello world";
str = "how are you";
3、堆区
char *str = (char*)malloc(10);//动态申请10个字节的存储空间。
//首地址给str赋值。
strcpy(str, "hello world");//将字符串拷贝到存储空间中。
scanf("%s", str);
字符串的可修改性
1、数组中
char str[100] = "hello world";
str[0] = 'y';
2、文字常量区
不可修改
3、堆区
#include<stdio.h>
#include<stdlib.h>
int main()
{
char* str = (char*)malloc(20);//动态申请20个字节的存储空间。
char* str1 = "hello world";
//首地址给str赋值。
strcpy(str, str1);//将字符串拷贝到存储空间中。
str[0] = 'o';
*(str + 1) = 'y';
}
数组指针
二维数组
int a[3][5];
printf("%p\n", a);//a是数组起始的地址
printf("%p\n", a+1);//a+1是第二行起始地址
数组指针的定义
指向的数组的类型 (*指针变量名) [指向的数组元素的个数]
int a[3][5];
int(*p)[5];
p = a; //把a的地址赋值给P
printf("%x\n", a);
printf("%x\n", a+1);
printf("%x\n", p);
printf("%x\n", p+1);
数组名字和指针变量的区别
int a[5];
int *p;
p =a;
相同点:
a是数组的名字,是a[0]的地址,p=a即p保存了a[0]的地址,即a和p都指向a[0],所以在引用数组元素的时候(a[2],*(a+2)),a和p等价。
不同点:
1、a是常量,p是变量。
可以用等号给p赋值,但不能用等号给a赋值。
2、对a取地址,和对p取地址结果不同。
因为a是数组的名字,所以对a取地址结果为数组指针。
p是个指针变量,所以对p取地址为指针的指针。
数组指针取*
数组指针取*,并不是取值的意思,而是指针类型发生变化。
一维数组取*,结果为它指向的一维数组第0个元素的地址。他们还是指向同一个地方。
二维数组取*,结果为一维数组指针。他们还是指向同一个地方。
三维数组取*,结果为二维数组指针。他们还是指向同一个地方。
。。。
int a[3][5];
int(*p)[5];
p = a;
printf("%p\n", a);//a是数组的名字,是数组中第0个一位数组的首地址。
printf("%p\n", a + 1);//a+1跳一行
printf("%p\n", *a + 1);//*a+1是&a[0][1]
printf("%p\n", &a + 1);//&a+1跳过整个数组
printf("%p\n", p); //数组第一行的首地址。
printf("%p\n", p+1);//第二行的地址
printf("%p\n", *p);//一维数组第0个元素的地址。
printf("%p\n", *p+1);//是&a[0][1]
指针和函数的关系
1、指针作为函数的参数
1、值传递
将实参的值复制给形参,不会改变实参的值。
void swap(int x, int y)
{
int temp = x;
x = y;
y = temp;
}
int main()
{
int a = 10, b = 20;
swap(a, b);
printf("a=%d,b=%d", a, b);
}
2、地址传递
将地址传递为形参,会修改实参的值。
void swap(int *x, int *y)
{
int temp = *x;
*x = *y;
*y = temp;
}
int main()
{
int a = 10, b = 20;
swap(&a, &b);
printf("a=%d,b=%d", a, b);
}
3、给函数传数组
void func(int a[])
{
a[0] = 3;
printf("%d\n", a[0]);//3
}
void func1(int* p)
{
*p = 10;
printf("%d\n", *p);//10
}
int main()
{
int a[2] = { 1,2 };
func(a);
func1(a);
}
4、指针作为函数的返回值
char* func()
{
static char str[100] = "hello world";//func方法执行完后,防止内存被释放。
char *str = "hello world";//文字常量区也不被释放
return str;
}
int main()
{
char* p;
p = func();
printf("%s\n", p);
return 0;
}
指针保存函数的地址
1、函数指针
函数也有起始地址,函数的名字就是函数的首地址,即函数的入口地址。
2、函数指针的用处
必须创建线程的时候,传递函数作为执行任务。
3、函数指针的定义
返回值类型 (*函数指针变量名)(形参列表)
int max(int x, int y)
{
if (x > y) {
return x;
}
else {
return y;
}
}
int main()
{
int (*p)(int x, int y);
p = max;
printf("%d\n", max(10, 20));
printf("%d\n", (*p)(10, 20));
}
特殊指针
1、空类型指针 void*
void*可以保存任何类型的地址。
void * memset(void *s, int c, size_t n);
这个函数是将s指向的内存的前n个子字节,全部赋值为c。
2、NULL
空指针。
char *p = NULL;
可以任务p哪里都不指向,也可以认为p指向内存编号为0的地址。
通常用NULL来指针初始化。