C语言——指针与函数

本文详细介绍了C语言中的指针,包括指针的内存编号、变量地址与值的关系、系统如何处理未初始化的指针以及指针运算和数组的关联。文中通过实例解释了指针如何存储地址、如何进行算术运算,并探讨了指针在数组和函数调用中的应用,如传值与传址。同时,文章通过函数指针和地址调用来说明如何修改函数入口。
摘要由CSDN通过智能技术生成

指针

  • 单元内存的编号
  • 可直接访问计算机内存

变量地址和变量的值

  • 计算机为存储空间中的每一个字节(8个二进制位)分配一个编号,称“编址”,这个编号就是内存地址(地址)
  • 变量以它所占据的那块内存的第一个字节的地址(即起始地址)来表示该变量在内存中的地址

注意

  1. 每个变量有唯一的地址
  2. 每个变量的值存储在变量地址表示的那段内存空间中
  3. 不同操作系统地址的分配方式、结果是不同的(经典windows的32位和64位各不相同,在给int指针分配空间时,用sizeof()能发现)
  • 基类型决定了编译器对该地址指向的空间的处理方式,即处理时以什么数据类型来进行
  • 这有一点,我个人最开始认为,指针变量和普通变量一样,系统会给赋值(这里赋值的意思是,指针指向的这块内存空间),现在看来是大错特错。
    如下图。
    我并没有给*e指针变量初始化,系统自动给分配的地址:0x39(随机分配的地址)。但是系统划分的内存,39这个地址,对于windows来说(我觉得可以了解、学习下windows注册表:注册表),39对应的内存空间,用户肯定是不能够使用或者说windows不允许用户修改、访问(未查到相关资料,这里是主观认为,0x00000039为系统存储数据的地方。有问题感谢各位纠正,在今后学习到会回来修改的)
    附上一篇关于野指针的详解:转载野指针
#include<stdio.h>
void main(){
	int *a, *e = NULL;
	double *c;
	double d = 7.8;
	int b = 10;			//int型变量内存分配4Byte空间,而int指针分配8Byte空间 
	a = &b;
	c = &d;
	printf("%d\n", *a);
	printf("0x%x\n", a);
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(b));
	printf("%d\n", sizeof(c));
	printf("%d\n", sizeof(d));
	printf("%x\n", e);
	
	/** 
	 * 其实仅让他指向这块内存空间(仅记录这块地址) 
	 * 这块内存空间的访问、修改、管理等一系列
	 * 是不被允许的,
	 * 所以我理解为
	 * 系统不会自动为指针变量分配内存空间 
	 * 当没有初始化一个指针变量的值时,
	 * 系统会随机给指针变量一个值(得不到这块空间) 
	 */ 

其实仅让他指向这块内存空间(仅记录这块地址)
这块内存空间的访问、修改、管理等一系列
所以我理解为
系统不会自动为指针变量分配内存空间
当没有初始化一个指针变量的值时,
系统会随机给指针变量一个值(得不到这块空间)

在这里插入图片描述

算术运算

  1. 指针变量是用于存储地址值的变量,两个指针变量即使类型相同,也不能进行相加减操作,因为是地址值在相加。
  2. 指针变量的增减是以指针变量的基类型所占字节的大小为单位的。例如是int型,每次增加1,就是指针的地址空间变化一个4Byte空间。
  3. 两个同类型的指针变量之间还可以做减法。

指针与数组

  1. 数组名实质上是一个指针常量(地址常量),也就是说数组名代表该数组的首地址
  2. 数组元素的访问可以通过移动下标或移动指针进行。
    1. 移动下标是,指针始终是以数组名(即其实地址开始,像内存位置栈顶偏移标记位置一样)
    2. 移动指针,指针变量做自增运算,每次自增后,指针就跳到下一个元素的位置,这种访问效率是高于下标方式的。
  • 拿简单的出入栈举例
#include<stdio.h>
#include<malloc.h>
#define MAXSIZE 1024

/**
 * 
 * malloc方法:
 * void *malloc(long NumBytes):该函数分配了NumBytes个字节,并返回了指向这块内存的指针。
 * 如果分配失败,则返回一个空指针(NULL)。
 * 分配失败的原因,应该有多种比如说空间不足就是一种。 
 *
 * void free(void *FirstByte): 
 * 该函数是将之前用malloc分配的空间还给程序或者是操作系统,也就是释放了这块内存,让它重新得到自由。
 */ 
typedef int datatype;
typedef struct {		//栈定义,拥有存储元素、栈顶位置 
	datatype data[MAXSIZE];
	int top;
}SeqStack;
SeqStack *Init_SeqStack(){			//初始化栈方法 
	SeqStack *s;			 
	s = malloc(sizeof(SeqStack));	//通过malloc方法,获得Seqstack对应空间所占字节数的内存大小的空间。  
	s -> top = -1;					//栈顶向上移动一个位置 
	return s;			//得到这个初始化的空栈 
}
	/** 
	 *	Parameters:SeqStack *s,datatype x
	 *	 
	 */ 
int Push_SeqStack(SeqStack *s, datatype x){		//入栈 
	if(s -> top == MAXSIZE)			//对现在的栈空间大小判断 
		return 0;
	else{		//空间内存还足够的时候 
		s -> top++;			//栈顶向下增加一个位置 
		s -> data[s -> top] = x;	//将数据存入这个位置 
		return 1;		//返回存入成功返回值 
	}
}
int Pop_SeqStack(SeqStack *s, datatype *x){
	if(s -> top == -1)		//当栈为空时,不能出栈 
		return 0;
	else{	
		*x = s -> data[s -> top];		//传入的操作栈,此栈栈顶的元素被一个指针x指向,用于返回你出栈元素记录 
		s -> top--;			//栈顶指针自减 
		return 1;
	}
}
void main(){
	SeqStack *s1;
	int i, x;
	s1 = Init_SeqStack();
	Push_SeqStack(s1, 2);
	Push_SeqStack(s1, 3);
	Push_SeqStack(s1, 5);
	Push_SeqStack(s1, 6);
	Push_SeqStack(s1, 7);
	i = s1 -> top;
	while(i >= 0)
		printf("%5d", s1 -> data[i--]);
	Pop_SeqStack(s1, &x);
	printf("\npop=%d\n", x);                     
	i = s1 -> top;
	while(i >= 0)
		printf("%5d", s1 -> data[i--]);
	printf("\nx = %5d", x);
}

在这里插入图片描述

传值与传址

函数调用过程中,数据从实参传递给形参,是把实参的值单向复制到形参中
即实参给形参传递的是地址,则称地址调用,传址。给的值而非地址,则值调用,传值。
这里需要弄清楚两个概念:

  1. 函数指针:函数指针是指向函数的指针变量。 因此“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。下面会举例,C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上是大体一致的。函数指针有两个用途:调用函数和做函数的参数。

  2. 指针(型)函数:指针函数是一个函数。函数都有返回类型(如果不返回值,则为无值型),只不过指针函数返回类型是某一类型的指针。
    这两者无论是从概念还是具体内部细节,都是天壤地别,不能弄混。

  • 修改函数入口函数举例:
#include<stdio.h>
#include<stdlib.h>

void SwapByAddress(int*, int*); 
int add(int*, int*);			//函数名前加了*号意思 
int run(int a, int b);
//void fun 
void main(){
	int a, b;
	int (*fun)(int, int);		//定义函数指针 
//	int *fun;			//函数指针 
	a = 3;
	b = 4;
	SwapByAddress(&a, &b);
	printf("a = %d, b = %d\n\n", a, b);
	printf("&a = %x, &b = %x\n\n", &a, &b);
	//第二个函数测试 
//	printf("Answer:%d\n", add(&a, &b));
	
	//函数指针
//	fun = &run();
//	int (*pmax)(a, b) = run;
	fun = (void*)run;		//初始化函数指针,使其指向int run(int a, int b)函数入口
	int r = fun(a, b);		//执行函数
	printf("r = %d\n", r); 
	
}

/** 
 *	形址形参:px为地址,py为地址。传入形参目的为取得这个地址的值	
 *	两者皆在地址指向上操作,而不是普通函数的覆盖值 
 */ 
void SwapByAddress(int *px, int *py){	
	int temp;
	temp = *px;			//temp指向该地址的值 
	*px = *py;			//px的值,指向py的值 
	*py = temp;			//py的值,指向temp值。完成值交换 
	printf("%d\n\n", temp); 
	printf("*px = %d, *py = %d\n\n", *px, *py);
	printf("px = %d, py = %d\n\n", px, py);
	printf("&px = %x, &py = %x\n\n", &px, &py);
	


}

int add(int *a, int *b) {
	return *a + *b;
} 

/**
 *	对函数指针总结 
 *	普通替换:覆盖替换
 *	指针替换:交换指向替换 
 */
 
int run(int a, int b){
	printf("Here we go!");
	return a + b;
}

 

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值