目录
1 指针的基本概念
1 地址:用来区分内存中不同字节的编号
2 指针:地址就是指针,指针就是地址
3 指针变量:一个存储地址的变量
4 &:这个符号是用来获得一个变量在内存空间中的首地址,并且让表达式类型升级。 例如int型--->>int*型。
5 *:这个符号如果在表达式=右边,表示取指针指向空间的值(所取得的空间大小取决于指针的类型)。如果在表达式=左边,表示将右边的值放入指针指向的空间。也有让表达式类型降级的作用。例如int*型--->>int型。
如 下图,四个字节来存储整型100,八个字节来存储指向这个整型变量首地址的指针。
2 二级指针
我们称作指针为存储一个变量地址的变量,但是我们要存储指针的指针呢?那就要用到二级指针了。
int num = 0;
int *pnum = #
int **ppnum = &pnum;
下面用一个程序来解释
#include <stdio.h>
int main(int argc, char const *argv[])
{
int num = 100;
int *pnum = #
int **ppnum = &pnum;
printf("num = %d\n",num);
printf("*pnum = %d\n",*pnum);
printf("**ppnum = %d\n",**ppnum);
printf("&num = %p\n",&num);
printf("pnum = %p\n",pnum);
printf("*ppnum = %p\n",*ppnum);
printf("&pnum = %p\n",&pnum);
printf("ppnum = %p\n",ppnum);
return 0;
}
然而二级指针在C语言中两个地方使用:
1 函数体内部想修改函数体外部指针变量值的时候,传指针变量的地址也就是二级指针。
2 指针数组传参时,数组的数组名是指向数组第一个指针元素的指针。
3 指针的算术运算
+ - ++ --
指针偏移量为指向的数据类型大小个字节空间
int* :4
char* :1
double* :8
int** :8
4 void 指针
void *p;
常用来指向一个存储内存地址,没有指向空间大小的含义。
void*类型的指针不要用来使用取*或者++ --的相关操作。
void *型指针和其余类型指针的转换,不需要做强制类型转换。
用途
用在作为函数参数,或者函数返回值表示兼容所有类型的指针。
1 memcpy(void *dest,const void *src, size_t n);内存拷贝函数的参数定义
2 memcmp(const void *s1,const void *s2,size_t n);
一个例题:将内存地址为0x2000的空间赋值一个整型100 (0x2000是一个void *类型的地址)
(*(int *)((void*)0x2000)) = 100; 或者(* (int *)0x2000) = 100;
5 const 指针
1. const int *p;
2. int const *p;
3. int *const p;
4. const int *const p;
5. int const *const p;
1和2是等价的,const修饰的是*p,及p指向的空间不能改变(只读),但是p可以修改。(比如strcpy函数的const char *src参数,及我们不能修改源)。可以用,不能改。
3中const修饰的是p,及p不能改变,但是*p可以改变,所以一定要初始化,因为在程序中p只能指向初始化时的空间,无法指向其他空间,但可以利用指针修改p指向空间的值。(比如数组的数组名,永远指向第一个元素的首地址,我们可以修改第一个元素的值)。
4和5是等价的,const修饰的是p和*p,地址和指针指向的空间都不能改变。必须初始化。
6 指针数组和数组指针
1 指针数组
指针数组:
指针数组是一个数组,也就是指针的数组,这个数组里面每一个元素都是指针。
int *p[5];
比如上面,定义一个数组p,数组里面有五个元素,每个元素8个字节,总共40个字节,每个元素都是指向整型变量的指针。
整型的指针数组一般用的较少,在C语言中一般用的多的是字符型指针数组(char *pstr[5])
代码理解一下
#include <stdio.h>
int main(int argc, char const *argv[])
{
char *pstr[5] = {"hello","world","how","are","you",};
for(int i = 0;i < 5;i++)
{
printf("str[%d] = %s\n",i,pstr[i]);
}
return 0;
}
一般情况,存储字符串用二维数组,操作字符串用指针数组。
比如我们用冒泡排序来排序字符串数组,其中交换的是地址,而不是改变字符串。
如下例代码说明
in(int argc, char const *argv[])
{
char *pstr[5] = {"hello","world","how","are","you",};
int i = 0;
int j = 0;
char *ptmp = NULL;
for(i = 0;i < 5 -1;i++)
{
for(j = 0;j < 5 -1 - i;j ++)
{
if(strcmp(pstr[j],pstr[j + 1]) > 0)
{
ptmp = pstr[j];
pstr[j] = pstr[j + 1];
pstr[j +1] = ptmp;
}
}
}
for(i = 0; i < 5; i++)
{
printf("%s\n",pstr[i]);
}
return 0;
}
2 数组指针
数组指针是一个指针,指针指向数组,指针占八个字节。
int (*a)[5];
int a[5];
&a:int (*)[5]
1 对一维数组数组名&操作,值不变类型升级为数组指针。
2 对数组指针取*操作,值不变类型降级为指向数组第一个元素的指针
代码说明一下
#include <stdio.h>
int main(int argc, char const *argv[])
{
int (*p)[5] = NULL;
int a[5] = {1,2,3,4,5,};
p = &a;
printf("a = %p\n",a);
printf("&a = %p\n",&a);
printf("*a = %d\n",*a);
printf("*&a = %p\n",*&a);
printf("========================\n");
printf("p = %p\n",p);
printf("p + 1 = %p\n", p+1);
printf("*p = %p\n",*p);
printf("*p + 1 = %p\n", *p+1);
return 0;
}
7 指针和数组的关系
1 指针和一维数组的关系
int a[5] = {1,2,3,4,5};
数组的数组名是指向数组第一个元素的指针。
a = &a[0]
访问数组元素的方式:a[n] = *(a + n)
2 指针和二维数组的关系
二维数组的数组名a是指向数组第一行元素的数组指针。
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a[2][3] = {1,2,3,4,5,6};
printf("================================\n");
printf("&a[0][0] = %p\n",&a[0][0]);
printf("&a[0][1] = %p\n",&a[0][1]);
printf("&a[0][2] = %p\n",&a[0][2]);
printf("&a[1][0] = %p\n",&a[1][0]);
printf("&a[1][1] = %p\n",&a[1][1]);
printf("&a[1][2] = %p\n",&a[1][2]);
printf("================================\n");
printf("a = %p\n",a);
printf("a + 1 = %p\n",a + 1);
printf("================================\n");
printf("a[0] = %p\n",a[0]);
printf("a[0] + 1 = %p\n",a[0] + 1);
printf("a[1] = %p\n",a[1]);
printf("a[1] + 1 = %p\n",a[1] + 1);
printf("================================\n");
printf("*a[0] = %d\n",*a[0]);
printf("*(a[0] + 1)= %d\n",*(a[0] + 1));
printf("*a[1] = %d\n",*a[1]);
printf("*(a[1] + 1) = %d\n",*(a[1] + 1));
printf("================================\n");
printf("*a = %p\n",*a);
printf("*(a + 1) = %p\n",*(a + 1));
return 0;
}
3 访问数组元素的方式
a[m][n] = *(a[m] + n) = *(*(a + m) + n)
*(p + m * N + n)