07:指针

指针

  • 1、什么是指针
    • 1.1、地址的定义
    • 1.2、指针的作用
  • 2、指针的分类
    • 2.1、基本类型指针
    • 2.2、指针和数组
      • 2.2.1、指针和一维数组
        • 2.2.1.1、一维数组名
        • 2.2.1.2、下标和指针的关系
        • 2.2.1.3、确定一维数组需要几个参数
        • 2.2.1.4、指针变量的运算
        • 2.2.1.5、指针变量占用几个字节
      • 2.2.2、动态内存分配
    • 2.3、多级指针
  • 3、跨函数使用内存

1、什么是指针

…指针简单来说就是地址(内存单元的编号)
例如:

#include 

int main(void)
{
	int i = 3;
	int * p;//定义指针变量p,p只能存放整型变量的地址。
	p = &i;
	printf("%d\n",*p);
	return 0;
}

3

1、int * p表示定义一个指针变量p,类型为int *代表存放整型变量的地址,p用来存放整型变量的地址。
2、p = &i表示把i的地址存放在p变量里面,则p指向i。这就是指针。
3、当p指向i时,则*p代表取值,指取出p指向的地址的变量i的值(所有出现*p的地方都可以用i替换)。

1.1、地址的定义

什么是地址?
    内存单元的编号,从0开始的非负整数。
07:指针_第1张图片

范围:
一个地址单元就是一个字节(8位二进制)下面是32位的
07:指针_第2张图片则一共有2^32个字节。
1kB = 2^10B(字节)
1MB = 2^10KB
1GB = 2^10MB
所以1GB = 2^30B(字节)
2^32B = 4GB

1.2、指针的作用

(1)表示一些复杂的数据结构快速的传递数据
(2)使函数返回一个以上的值能直接访问硬件
(3)能够方便的处理字符串
(4)是理解面向对象语言中引用的基础
  总结:指针是C语言的灵魂

2、指针的分类

2.1、基本类型指针

什么的列子就是基本类型的指针。
下列是常见的一些错误
代码①:

# include

int main(void)
{
	int* p;
	int i = 5;
	*p = i;
	printf("%d\n",*p);
	return 0;
}

我们定义了一个存放整型变量的指针变量p,但是p没有指向,所以里面存放的是垃圾值(即p指向的是一个不知名的单元,我们将整数5存放在这个不知名的单元里面,这样是错误的,因为这个不知名的单元不是我们控制的,类似于病毒)

代码②:

# include

int main(void)
{
	int* p;
	int* q;
	int i = 5;
	 p = &i;
	 p =q;
	printf("%d\n",*p);
	return 0;
}

错误和上面一样,q里面的地址是垃圾值,把垃圾值给了p。
改为:

   q =p;
printf("%d\n",*q);

经典指针程序互换
代码①:

# include

void HuHuang(int m,int n)
{
	int t;
	
	t = m;
	m = n;
	n = t;
}

int main(void)
{
	int a = 5,b = 3;
    HuHuang(a,b);
	printf("a = %d,b = %d\n",a,b);
	return 0;
}
a = 5,b = 3

调用互换函数时,将a的值赋给了m,将b的值赋给了n。在函数里面实现只是形式参数m和n的值的互换,但printf打印出来的值依然是主函数里面a和b的值。

改进代码②:

# include
/*void HuHuang1(int* m,int* n)
{
	 int* t;

	 t = m;
	 m = n;
	 n = t;
}*/
//将a的地址赋给m,b的地址赋给n,函数里面交换的只是变量m和n里面的值。

void HuHuang2(int* m,int* n)
{
	int t;
	
	t = *m;
	*m = *n;
	*n = t;
}
/*将a的地址赋给m,b的地址赋给n,*m指向a等同于实参a里面的值(假设a地址为F,I*m代表以F为地址的变量的值)
*n指向b等同于实参b里面的值。*/
int main(void)
{
	int a = 5,b = 3;
	 // HuHuang1(&a,&b);
    HuHuang2(&a,&b);
	printf("a = %d,b = %d\n",a,b);
	return 0;
}
a = 3,b = 5

07:指针_第3张图片

2.2、指针和数组

2.2.1、指针和一维数组

2.2.1.1、一维数组名

    是指针常量,他存放的是一维数组第一个元素的地址。

# include 

int main(void)
{
	int a[3] = {1,2,3};
	printf("%#X\n",&a[0]);
	printf("%#X\n",a);
	return 0;
}
0X19FF24
0X19FF24
2.2.1.2、下标和指针的关系

    如果a是一个指针变量,则a[ i ]等价于*(a+i)。

# include 

int main(void)
{
	int a[3] = {1,2,3};
	printf("%d\n",a[1]);
	printf("%d\n",*(a+1));
	return 0;
}
2
2
2.2.1.3、确定一维数组需要几个参数

    需要数组第一个元素的地址
    需要数组的长度
代码①:

# include 

void f(int* m,int len)//第一个元素的地址和长度
{
	int i;
	for(i=0 ;i<len ;i++)
		printf("%d",*(m+i));//也可以写为m[i],等价于a[i]/b[i],也等价于*(a+i)/*(b+i)
	printf("\n");
}

int main(void)
{
	int a[3] = {1,2,3};
	int b[5] = {1,5,6,8,90};

	f(a,3);
	f(b,5);
	return 0;
}
123
156890

代码②:

# include 

void f(int* m,int len)
{
	*(m+2) = 11;
}

int main(void)
{
	int b[5] = {1,5,6,8,90};
	printf("%d\n",b[2]);

	f(b,5);
	printf("%d\n",b[2]);
	return 0;
}
6
11

总结
可以利用指针,在函数中操作主函数的变量的值,等同于在主函数里面进行操作,进而改变主函数里面变量的值。

2.2.1.4、指针变量的运算

    指针变量不能相加不能相乘也不能相除
    如果两个指针变量指向的是同一块连续空间中的不同存储单元
    则这两个指针变量才可以相减

# include 

int main(void)
{
	int b[5] = {1,5,6,8,90};
	int* m;
	int* n;
	m = &b[1];
	n = &b[3];
	printf("%d\n",&b[3]-&b[1]);
	return 0;
}
2
2.2.1.5、指针变量占用几个字节
# include 

int main(void)
{
	char ch = 'a';
	int i = 9;
	double j = 6.3;
	char* m = &ch;
	int* n = &i;
	double* h = &j;
	printf("%d,%d,%d\n",sizeof(m),sizeof(n),sizeof(h));
	return 0;
}
4,4,4

07:指针_第4张图片

2.2.2、动态内存分配

传统数组的缺点:
(1)数组长度必须事先制定,且只能是常整数,不能是变量
例子:

 int a[5];//OK
 int len = 5; int a[len]; //error

(2)传统形式定义的数组,该数组的内存程序员无法手动释放,在一个函数运行期间,系统为该函数中数组所分配的空间会一直存在,直到该函数运行完毕时,数组的空间才会被系统释放
(3)数组的长度一旦定义,其长度就不能在更改,数组的长度不能在函数运行的过程中动态的扩充或缩小
(4)A函数定义的数组,在A函数运行期间可以被其它函数使用,但A函数运行完毕之后,A函数中的数组将无法在被其他函数使用,传统方式定义的数组不能跨函数使用

为什么需要动态分配内存
动态数组很好的解决了传统数组的这4个缺陷,传统数组也叫静态数组

malloc函数的引入

# include 
# include //调用了malloc函数

int main(void)
{
	int i = 5;//分配了4个字节的静态内存(命名为i)
	int* p = (int*)malloc(4);//动态分配4个字节的空间(只是这个空间没有命名),然p指向这个空间,
	return 0;
	/*注意:1、要使用malloc函数,必须要添加头文件
	        2、malloc函数只能有一个形式参数,并且只能为整型
	        3、4表示请求系统分配4个字节的空间
	        4、malloc函数只能返回第一个字节的地址,前面加int*,
	        表示这个空间是分配给整型变量用的。所以用4个字节
	        5、p本身所占的内存是静态分配的(4个字节,用于存放动态分
	        配的空间第一个字节的编号),p所指向的内存是动态分配的
   */
   *p = 5;//*p代表就是一个整型变量,只是这个空间是动态分配的
   free(p);//表示把p所指向的内存给释放掉,即动态内存释放掉
}

代码①:

# include 
# include 

void f(int* q)
{
	*q = 20;
	free(q);
}

int main(void)
{
	int* p = (int*)malloc(4);
	*p = 10;
	printf("%d\n",*p);//10
	f(p);
	printf("%d\n",*p);//垃圾值:-572662307

	return 0;
}

07:指针_第5张图片

构造动态一维数组

# include 
# include 

int main(void)
{
	int len,i;
	printf("请输入数组元素的个数:");
	scanf("%d",&len);
	int* p = (int*)malloc(4 * len);//类似于int p[len];
	printf("请输入数组的元素,中间以空格隔开:");
	//对数组元素的输入
	for(i=0 ;i<len ;i++)
		scanf("%d",&p[i]);//也可以用*(p+i)
	//对数组元素的输出
	for(i=0 ;i<len ;i++)
		printf("%d ",p[i]);//也可以用*(p+i)
	printf("\n");
	return 0;
}
请输入数组元素的个数:5
请输入数组的元素,中间以空格隔开:3 2 5 6 4
3 2 5 6 4

2.3、多级指针

    纯纯套娃

# include 

int main(void)
{
	int i = 5;
	int* p = &i;
	int** q = &p; //q是int**类型,用于存放指针变量p的地址(二级指针)
	int*** r = &q; //r是int***类型,用于存放二级指针变量的地址(多级指针)
	printf("%d\n",***r);
	printf("%d\n",**q);
	printf("%d\n",*p);
	return 0;
}

3、跨函数使用内存

#include 

void f(int** m)
{
	int i = 5;
	*m = &i;//*m等于p,p指向i
}
	

int main(void)
{
	int* p;
	f(&p);
	printf("%d\n",*p);//此行代码存在逻辑上的问题。
	return 0;
}

调用f函数结束后,f函数里面的变量值将会被释放,所以*p不能在访问i空间里面的内容。这就是静态变量不能跨函数使用。

动态内存跨函数使用

#include 
#include 

void f(int** m)//二级指针
{
	//int i = 5;//静态分配内存
	//*m = &i;//*m等于p,p指向i
	*m = (int*)malloc(sizeof(int));//动态分配4个字节(用于存放整型变量)
	 //的内存,
	//等价于p = (int*)malloc(sizeof(int));
	**m = 5;//等价于*p = 5
}
	

int main(void)
{
	int* p;
	f(&p);//f终止后,空间没有被释放
	printf("%d\n",*p);
	return 0;
}

你可能感兴趣的:(C语言(郝斌版),c语言)