指针知识(四) 数组与指针、动态分配内存、指针函数与函数指针

一、指针常见错误解析

案例:

#include <stdio.h>
#include <stdlib.h>   
//经典指针程序,互换两个数字

int main(void)
{
	int i = 5;
	int *p;
	int *q;
	*假如q是垃圾值,不过还是可以访问q的空间,因为内存单元已经分配给你了;
	  不过*q是不能被读写的,因为*q代表的是另一个不知道,而且其所代表的内存单元的控制权限没有给你。
	*/
	p = &i;
	//*q = p;   //error 语法上面没有错误,但是编译是会有错误的。原因是*q是int型,P是int*型号。
	p = q;      //这样写的话q是垃圾之赋值给p这样造成p也是垃圾值
	printf("%d\n", *q);
	system("pause");
	return 0;
}
可以看 指针相关知识(二)指针相关知识(三) 关于内存等的讲解。

二、数组与指针

1. 一维数组名是个指针常量(其值是不能改变的,它存放的是一维数组第一个元素的地址

#include <stdio.h>
#include <stdlib.h> 

int main(void)
{
	int a[5];
	int b[3];

	printf("a = %#X\n", &a[0]);
	printf("a = %#X\n", &a);
	system("pause");
	return 0;
}
输出的结果:


2. 下标与指针的关系:如果p是个指针变量,则p[i] 永远等价于*(p+i)  也与arr[i]等价 也等价于*(arr+i);(我们知道数组名arr也是数组首地址元素,因此也是可以使用arr+i指向第i个数组元素,该数组元素即可以表示为*(arr+i)( arr[i]等价于*(arr+i);

3. 确定一个一维数组需要几个参数? --- 两个参数: {如果一个函数需要处理一个一维数组,需要接收概数组的哪些信息 -- int mrr(int *a, int b), 数组的首地址以及数组的长度b};

    数组第一个元素的地址;

    数组的长度;                   

4. 指针变量的运算

    指针变量不能相加,不能相乘,也不能相除。可以相减(如果两个指针变量指向的是一块连续空间中的不同存储单元---其实就是两个变量之间的间隔了)  

int main(void)
{
	int a[5];
	int *p;
	int *q;
	p = &a[1];
	q = &a[4];

	printf("p和q所指向的单元相隔%d的个单元\n", q - p);     //一般会是在数组里面进行这些

	system("pause");
	return 0;


}
5. 一个指针变量,无论它指向的变量占几个字节, 该指针变量本身只占4个字节;一个变量的地址是用该变量首字节的地址来表示的。( 指针变量本身所占的内存是静态分配的,指针变量指向的内存空间是动态分配的

三、动态内存分配

1. 传统数组的缺点:

    1.1 数组长度必须事先制定,且只能是长整数,不能是变量

int a[5];   //ok
int len = 5;
int a[len];  //error
    1.2 在一个函数运行期间,系统为该函数中数组所分配的空间会一直存在,直到改函数运行完毕时,数组的空间才会被系统释放
void aaa(int *arr, int len)
{
	arr[2] = 22;
}

void bbb(void)
{
	int a[5] = {1,2,3,4,5};
	aaa(a, 5);
	printf("%d\n", a[2]);
}
    1.3 数组的长度不能在函数运行的过程中动态的扩充或者缩小。 

    1.4 A函数定义的数组,在A函数运行期间可以被其他函数使用,但A函数运行完毕后,A函数中的数组将无法再被其他函数使用;

          传统方式定义的数组不能跨函数使用 ;

void aaa(int *arr, int len)
{
	arr[2] = 22;
}

void bbb(void)
{
	int a[5] = {1,2,3,4,5};    
	aaa(a, 5);           //运行期间可以被aaa使用
	printf("%d\n", a[2]);
}

int main(void)
{
	bbb();

	system("pause");
	return 0;
}

2. 为什么要动态分配内存:

    解决传统的缺陷

3. 动态内存分配的举例,动态数组的构造:

int main(void)
{
	bbb();

	int i = 5;       //分配四个字节,静态分配
	int *p = (int *)malloc(4);     //分配的八个字节,p变量占四个字节,p变量指向的内存也占四个字节;
	/*
	1. 要使用malloc函数需要导入头文件,必须添加malloc.h
	*/
        *p = 5;   //*p代表的就是一个int变量,只不过*p这个整型变量的内存分配方式与int i = 5的内存分配方式不一样
	system("pause");
	return 0;
}

指针与结构体的运用:(学生管理系统)

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

struct Student
{
	int age;
	float score;
	char name[100];

};

void Input(int len, struct Student *pArr);
void paixu(int len, struct Student *pArr);
void Output(int len, struct Student *pArr);

int i, j;
struct Student t;

int main(void)
{
	int len;
	//int *pArr;
	struct Student * pArr;
	//int i; 
	//int j;
	//struct Student t; 

	printf("请输入学生人数: \n");
	printf("len = ");
	scanf("%d", &len);


	//动态构造一维数组
	//pArr = (int*)malloc(len * sizeof(int));
	pArr = (struct Student *)malloc(len * sizeof(struct Student));

	Input(len, pArr);
	paixu(len, pArr);
	Output(len, pArr);

	system("pause");
	return 0;

}

void Input(int len, struct Student *pArr)
{
	//输入
	for (i = 0; i < len; i++)
	{
		printf("请输入地%d个学生的信西:\n", i + 1);
		printf("age = ");
		scanf("%d", &pArr[i].age);

		printf("name = ");
		scanf("%s", pArr[i].name);  //注意的是name局势数组名,本身就已经是数组首元素的地址

		printf("score = ");
		scanf("%f", &pArr[i].score);

	}
}

void paixu(int len, struct Student *pArr)
{
	//排序  毛泡排序升序
	for (i = 0; i < len; ++i)
	{
		for (j = 0; j < len-1-i; ++j)
		{
			//if (pArr[j] > pArr[j+1])   //这是由于元素里面有三个成员,没有指定比较什么
			if (pArr[j].score > pArr[j+1].score)
			{
				t = pArr[j];
				pArr[j] = pArr[j+1];
				pArr[j+1] = t;
			}
		}
	}

}

void Output(int len, struct Student *pArr)
{
	printf("\n\n学生的信息是");
	//输出
	for (i = 0; i < len; i++)
	{
		printf("第%d个学生的信西是:\n", i + 1);
		printf("age = %d\n", pArr[i].age);
		printf("name = %s\n", pArr[i].name);
		printf("score = %f\n", pArr[i].score);
		printf("\n");

	}

}

4. 指针的优点

     表示一些复杂的数据结构; 快速的传递数据,减少了内存的耗用; 使函数返回一个以上的值; 能直接访问硬件;能够方便处理字符串; 是理解面向对象语言中引用的基础

四、指针函数与函数指针

指针函数

1. 定义:

函数返回值指针类型的函数称为指针函数。指针函数的定义与其他函数的定义有点区别,需要在函数名前面加上 “*”。一般格式如下:

数据类型 * 函数名(形参表)
{
    函数体
}

它是一个函数,只不过这个函数的返回值是一个地址(当一个函数声明其返回值为一个指针时,实际上就是返回一个地址给调用函数)。函数返回值必须用同类型的指针变量来接受,也就是说,指针函数一定要有函数返回值,而且,在主调函数中,函数返回值必须赋给同类型的指针变量。

案例:

int *GetDate();
int * aaa(int,int);
// 函数返回的是一个地址值,经常使用在返回数组的某一元素地址上。

分析-例子1:

int* fun(int, int);   // 指针函数
// 返回值是一个int类型的指针,fun只是一个函数名

由于“*”的优先级低于"()",因而fun首先和后面的()结合,也就是意味着,fun是一个函数。即int *(fun(int, int)); 接着再和前面的“*”结合, 说明函数的返回值是一个指针。由于前面还有一个int,也就是说,fun是一个返回值为整型的函数。我们在看看下面这个:

例子2:

int (*pfun)(int, int);   // 函数指针

通过括号强行将pfun首先与“*”结合,也就是意味着,pfun是一个指针,接着与后面的“()”,说明指针指向一个函数,然后在与前面的int结合,也就是说,该函数的返回值是int。由此可见,pfun是一个指向返回值为int的函数的指针。

虽然它们只有一个括号的差别,但是表示的意义截然不同。函数指针的本身是一个指针,指针指向的是一个函数。指针函数的本身是一个函数,其函数的返回值是一个指针。

实例:

// 指针函数,返回一个地址给调用者
int *fun(int a, int b);

int main(int argc, char* argv[])
{
	printf("................ \n");

	int *p1 = NULL;
	printf("the memeory address of p1 = 0x%x \n", p1);

	p1 = fun(1, 2);
	printf("the memeory address of p1 = 0x%x \n", p1);
	printf("*p = %d \n", *p1);

	printf("................ \n");
	getchar();

	system("pause");
	return 0;
}

int *fun(int a, int b)
{
	// malloc:分配一块长度为size字节的连续空间,并将该空间的首地址作为函数的返回值
	int *p;
	p = (int *)malloc(sizeof(int));
	printf("the memeory address of p = 0x%x \n", p);

	memset(p, 0, sizeof(int));
	*p = a + b;
	printf("*p = %d \n", *p);

	return p;
}

函数指针

1. 函数指针是指向函数指针变量,即本质是一个指针变量。顾名思义,函数指针说的就是一个指针,但是这个指针指向的函数,不是普通的基本数据类型或者类对象。

int (*f) (int x); // 声明一个函数指针

f = func; // 将func函数的首地址赋给指针f

2. 区别:函数指针与指针函数的最大区别就是函数指针函数名是一个指针,即函数名前面有一个指针类型的标志符号 *。

3. 函数指针与指针函数的最大区别是函数指针的函数名是一个指针,即函数名前面有一个指针类型的标志型号 “*”。

实例:

// 声明函数指针,指向返回值类型为int,有两个参数类型都是int的函数 
int (*f)(int, int);  
int max(int a, int b) {  
	return a > b ? a : b;  
}  
int min(int a, int b) {  
	return a < b ? a : b;  
}  
int main(int argc, char* argv[])
{
	printf("------------------------------ Start\n");  
	f = max; // 函数指针f指向求最大值的函数max  
	int c = (*f)(1, 2);  
	printf("The max value is %d \n", c);  

	f = min; // 函数指针f指向求最小值的函数min  
	c = (*f)(1, 2);  
	printf("The min value is %d \n", c);  
	printf("------------------------------ End\n");  
	getchar();  
	return 0; 
 }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值