指针
- 单元内存的编号
- 可直接访问计算机内存
变量地址和变量的值
- 计算机为存储空间中的每一个字节(8个二进制位)分配一个编号,称“编址”,这个编号就是内存地址(地址)
- 变量以它所占据的那块内存的第一个字节的地址(即起始地址)来表示该变量在内存中的地址
注意
- 每个变量有唯一的地址
- 每个变量的值存储在变量地址表示的那段内存空间中
- 不同操作系统地址的分配方式、结果是不同的(经典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);
/**
* 其实仅让他指向这块内存空间(仅记录这块地址)
* 这块内存空间的访问、修改、管理等一系列
* 是不被允许的,
* 所以我理解为
* 系统不会自动为指针变量分配内存空间
* 当没有初始化一个指针变量的值时,
* 系统会随机给指针变量一个值(得不到这块空间)
*/
其实仅让他指向这块内存空间(仅记录这块地址)
这块内存空间的访问、修改、管理等一系列
所以我理解为
系统不会自动为指针变量分配内存空间
当没有初始化一个指针变量的值时,
系统会随机给指针变量一个值(得不到这块空间)
算术运算
- 指针变量是用于存储地址值的变量,两个指针变量即使类型相同,也不能进行相加减操作,因为是地址值在相加。
- 指针变量的增减是以指针变量的基类型所占字节的大小为单位的。例如是int型,每次增加1,就是指针的地址空间变化一个4Byte空间。
- 两个同类型的指针变量之间还可以做减法。
指针与数组
- 数组名实质上是一个指针常量(地址常量),也就是说数组名代表该数组的首地址
- 数组元素的访问可以通过移动下标或移动指针进行。
- 移动下标是,指针始终是以数组名(即其实地址开始,像内存位置栈顶偏移标记位置一样)
- 移动指针,指针变量做自增运算,每次自增后,指针就跳到下一个元素的位置,这种访问效率是高于下标方式的。
- 拿简单的出入栈举例
#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);
}
传值与传址
函数调用过程中,数据从实参传递给形参,是把实参的值单向复制到形参中。
即实参给形参传递的是地址,则称地址调用,传址。给的值而非地址,则值调用,传值。
这里需要弄清楚两个概念:
-
函数指针:函数指针是指向函数的指针变量。 因此“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。下面会举例,C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上是大体一致的。函数指针有两个用途:调用函数和做函数的参数。
-
指针(型)函数:指针函数是一个函数。函数都有返回类型(如果不返回值,则为无值型),只不过指针函数返回类型是某一类型的指针。
这两者无论是从概念还是具体内部细节,都是天壤地别,不能弄混。
- 修改函数入口函数举例:
#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;
}