第七章 指针

指针的概念

系统给虚拟内存的每个存储单元分配了一个编号。从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来指针初始化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七号公园的忧伤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值