C语言指针
指针
指针也就是内存地址,指针变量是用来存放内存地址的变量
所有实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,对应指针的值的类型都是一样的,都是一个代表内存地址的长的十六进制数
int *p=&a
申明p指针,储存的是a的地址
p
引用地址,*p
反引用,改变的是指针所指地址的值
C语言引用数组时,得到的数组第一个元素的地址
NULL 指针是一个定义在标准库中的值为零的常量
%p是打印地址(指针地址)的,是十六进制的形式,但是会全部打完,即有多少位打印多少位
1 |
|
打代码时一定注意需要的是指针还是需要里面存储的东西
命名
int *p
; //首先从P 处开始,先与*结合,所以说明P 是一个指针,然后再与int 结合,说明指针所指向的内容的类型为int 型.所以P是一个返回整型数据的指针int *p[3]
; //首先从P 处开始,先与[]结合,因为其优先级比高,所以P 是一个数组,然后再与结合,说明数组里的元素是指针类型,然后再与int 结合,说明指针所指向的内容的类型是整型的,所以P 是一个由返回整型数据的指针所组成的数组int (*p)[3]
; //首先从P 处开始,先与*结合,说明P 是一个指针然后再与[]结合(与”()”这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.所以P 是一个指向由整型数据组成的数组的指针int **p
; //首先从P 开始,先与结合,说是P 是一个指针,然后再与结合,说明指针所指向的元素是指针,然后再与int 结合,说明该指针所指向的元素是整型数据.由于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针.int p(int)
; //从P 处起,先与()结合,说明P 是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数,然后再与外面的int 结合,说明函数的返回值是一个整型数据int (*p)(int)
; //从P 处开始,先与指针结合,说明P 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以P 是一个指向有一个整型参数且返回类型为整型的函数的指针int *(*p(int))[3]
; //可以先跳过,不看这个类型,过于复杂从P 开始,先与()结合,说明P 是一个函数,然后进入()里面,与int 结合,说明函数有一个整型变量参数,然后再与外面的结合,说明函数返回的是一个指针,,然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,然后再与结合,说明数组里的元素是指针,然后再与int 结合,说明指针指向的内容是整型数据.所以P 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数.
1 |
|
指针的类型
这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?
指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。
在32 位程序里,所有类型的指针的值都是一个32 位整数,因为32 位程序里内存地址全都是32 位长。指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为si zeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX 为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。指针所指向的内存区和指针所指向的类型是两个完全不同的概念
指向多维数组的指针
1 |
|
程序的运行
程序内存空间的分配
内存中的每个空间都是可以寻址的
从大到小:
堆Heap(动态内存):更自由,可自由分配大小
栈Stack: 存储函数执行时的信息 ,局部变量(程序启动时空间固定)
静态或全局变量: (不在函数里定义的就是全局变量),在任何地方都能访问修改
代码:存储程序的指令
运行时的空间调用
在栈中先执行main函数,给予栈帧,每当调用函数时,当前函数会在调用的那行暂停,机器转而去执行被调函数,被调函数被赋予新的栈帧,如果一个函数无限次调用另一个函数,就会造成栈溢出
实参与形参
实参:主函数中调用其他函数,
形参:被调函数中的参数
堆heap的使用
在堆里的内存只能被引用
#include<stdlib.h>
malloc:
在heap里找到内存然后返回地址 int *p=(int*)malloc(n*·sizeof(int));
返回的是void指针,指向分配发给我们的内存块的第一个地址,需要将其转换为特定的指针
注意malloc括号内的数字是bytes数,一个int占4bytes
calloc :
与mallloc相似,需要两个参数:返回特定类型的元素的数量,第二个参数是类型的大小
int *p=(int*)calloc(3,sizeof(int));
realloc :
修改已经定义的内存
int *pr=(int*)calloc(p,sizeof(int));
free:free(p)
释放队中的内存(不会自动释放)
C++
new delete
1 |
|
指针的算术运算
一个指针ptrold 加(减)一个整数n 后,结果是一个新的指针ptrnew,ptrnew 的类型和ptrold 的类型相同,ptrnew 所指向的类型和ptrold所指向的类型也相同。ptrnew 的值将比ptrold 的值增加(减少)了n 乘sizeof(ptrold 所指向的类型)个字节。就是说,ptrnew 所指向的内存区将比ptrold 所指向的内存区向高(低)地址方向移动了n 乘sizeof(ptrold 所指向的类型)个字节。指针和指针进行加减:两个指针不能进行加法运算,这是非法操作,因为进行加法后,得到的结果指向一个不知所向的地方,而且毫无意义。两个指针可以进行减法操作,但必须类型相同,一般用在数组方面
1 |
|
指针ptr 的类型是int*,它指向的类型是int,它被初始化为指向整型变量a。接下来的第3句中,指针ptr被加了1,编译器是这样处理的:它把指针ptr 的值加上了sizeof(int),在32 位程序中,是被加上了4,因为在32 位程序中,int 占4 个字节。由于地址是用字节做单位的,故ptr 所指向的地址由原来的变量a 的地址向高地址方向增加了4 个字节。由于char 类型的长度是一个字节,所以,原来ptr 是指向数组a 的第0 号单元开始的四个字节,此时指向了数组a 中从第4 号单元开始的四个字
取值符与地址符
&
运算符 :用于取一个对象的地址
*
运算符:作用于指针时表示访问指针所指向的对象
地址与数据的关系相当于x与y
- 处于不同维度
- 一个数据可对应多个地址
- 一个地址有且只有一个数据
实参与形参
可以把指针作为函数的形参。在函数调用语句中,可以用指针表达式来作为实参。
实参
函数被调用时给出的参数包含了实实在在的数据,会被函数内部的代码使用
- 必须有确定的值s
形参
在函数定义中出现的参数可以看做是一个占位符,它没有数据,只能等到函数被调用时接收传递进来的数据
- 只在函数内部有效(只有在函数被调用时才会分配内存,调用结束后,立刻释放内存
- 函数调用中发生的数据传递是单向的,只能把实参的值传递给形参,而不能把形参的值反向地传递给实参
函数与调用指针
1 |
|
函数的调用其实就是将执行程序的地址跳转到函数的第一条指令
1 |
|
NULL
NULL is the constant that is defined by the standard
library
• It is equivalent to zero for a pointer
• It guarantees that the pointer does not point to any location in the memory and prevents accidental overwriting of a memory
• You need to add #include directive for stddef.h to your source fileNever dereference an uninitialized pointer
int * pt; // uninitialized pointer
*pt = 5; // store the value 5 to a location where pt points
Problem : pt has a random value and there is no knowing where pt will be placed
It might go somewhere harmless, it might overwrite data or code, or might cause the program to crash
Creating a pointer only allocated memory to store the pointer itself and it does not allocate memory to store the data itself.
pointers to arrays
Remember: array names are themselves pointers and
therefore you do not use & operator when pointing towards
an arrayname
• valuesPtr = values;
When specifying the array name without a subscript, this
has an effect of producing a pointer to the first element of
the values or
• valuesPtr = &values[0];
To reference values[3] via pointer, you can simply add 3 to values Ptr such as *(valuesPtr+3)
To set the valuesPtr to point the second element of the array, you do the following
valuesPtr = &values[1];
Or
valuesPtr +=1;