C/C++指针详解
基础概念
- 1个字节就会有一个内存首地址,1个字节就是一个内存块.取地址专指取内存块的首地址.内存的最小单位就是1个字节.内存块:存储的就是值,例如20,”1111”等可以让我们直接使用的值;
- 变量定义时即分配内存,按照类型分配内存大小.
char ch;//这个写法就在内存里面分配了1字节的大小,是在运行时已经分配了,但是没有用到而已,分配到哪里也不需要用户关心. int a;//分配4字节
- 变量定义时即分配内存,按照类型分配内存大小.
- 内存首地址:存储的就是类似于一个数组的下标,只不过这个下标被编译器定义了,编译器定义它,不告诉你,但是你要知道它最大(指针的大小)就是32位或者64位,32位机器使用4字节存储指针,64位机器使用8字节存储指针.
- 内存首地址不是存储在内存块里面的,是CPU总线计算出来的,按照十进制表示:一般是 0-2的多少次方大小;可以使用十进制,二进制,八进制,十六进制表示.每次取地址,就计算出来这个内存首地址;内存块里面也可以存储内存首地址(也就是指针),因为内存首地址可以用十进制表示,也就是说指针是个十进制的数字而已,没有特别神奇的存储格式,仅仅是指针的含义与规则比较特殊.
- 可以根据内存首地址找到对应的内存块,叫做寻址;*p(寻址)操作的是内存首地址(指针)所指向的内存块
- 要把 char /int /float 等等 和 char* /int* /float* 等等类型看作是数据类型即可,int* 这个类型的优先级大于 int 的优先级
- ‘ * ‘号的意义
1,定义变量时,’ * ‘ 代表的是类型(例如: int* p = 0x2498;p 就是指针类型 int* ,int是类型,int* 也是类型)
2,在使用变量时,代表操作指针所指向的内存.意思就是 *p 指向的就是p指针变量存储的内存首地址的内存块的值.
3,’ * ‘号在等号左边(int p = 0xxxx;)表示在定义一块内存,’ * ‘号操作必须操作指针变量,类型+’‘表示一个整体
4,’ * ‘号在等号右边,表示在操作内存块里面的值,哪个内存块,取决于指针变量里面存储的指针的值.+指针变量表示一个整体
- ‘ * ‘号的意义
- & 符号表示什么,如果&在指针变量之前表示什么,如果&在变量之前表示什么
- 变量:变量只要定义,就会分配这个变量所对应的内存首地址以及内存块,如果没有赋值,则内存块里面没有值而已,但是内存首地址一定是被分配了而且存在的.
- 指针:指针变量的值,就是内存首地址
- 指针变量:存储指针的一个变量,和普通变量一样
- 野指针:这个指针变量保存了一个没有意义(非法)的内存首地址,对野指针变量本身进行赋值,修改等没有问题,但是一旦对指针变量的值进行取地址操作,就会出现问题,因为是非法的.
- 空指针:给指针变量赋值位NULL,NULL就是个0.
- 万能指针使用的时候,需要将其转换为本身类型才能使用,例如 void* 转为 int* 才能使用
- 指针运算是和其类型相关的,加减乘除的时候都要附加类型大小才可以
- 数组的变量本身就是个内存首地址,int a[10]; a 本身就是个内存首地址 ; 所以 int* p = a; 是没有问题的
- 只要是指针 * 与 [] 对于取值是一个意思. *(p + i) 与 p[i] 是相等的,也就是 *(p+i) = p[i]
套娃
只要记住,内存首地址是 &变量 ,并且只能获取值 ;内存块是 *变量 可以获取值,也可以赋予值;内存首地址本质是纯数字;内存块本质也是纯数字,但是有其他表现形式;
一级套娃
int a; int* p = &a;
二级套娃
int a; int* p = &a; int** q = &p;
* 三级套娃
int a;
int* p = &a;
int** q = &p;
int*** w = &q;
* 多级套娃
int a;
int* p = &a;
int** q = &p;
int* w = &q;
int** e = &w;
# 函数传参
* 1. 函数传参都是将变量内存块里面的值拷贝一份传输过去,指针(内存首地址)拷贝一份,就能连接到函数内与函数外的数据,就会方便很多.
下面是错误例子:
//void printf_Array(int *a) //2种写法等价,对编译器而言,没有任何区别,编译器都是当作 int * 类型处理
void printf_Array(int a[])
{
for (size_t i = 0; i < sizeof(a) / sizeof(a[0]); i++) {
printf("%d,", a[i]);
}
printf("\n");
}
int array[] = { 7, 79, 465, 65, -345, -346, 798, 1, 0, 45 };
printf_Array(array);
//这个地方传输的是a这个变量内存块里面的值,也就是数组第一个元素的内存首地址,而不是整个数组
//形参中的数组根本就不是数组,就是一个指针变量(内存首地址)
正确的例子是:
void printf_Array(int *array,int length)
{
for (int i = 0; i < length; i++) {
printf("%d,", array[i]);
}
printf("\n");
}
int array[] = { 7, 79, 465, 65, -345, -346, 798, 1, 0, 45 };
printf_Array(array,(sizeof(array) / sizeof(array[0])));
>结论:数组在作为函数的形参传递过程中,需要传递首地址,还要传递数组长度,函数才可以正确将数组解析出来.
> 1. 形参中的数组不是数组,是普通的指针变量
> 2. 形参数组: int a[10000],int a[],int * a,对编译器没有区别,编译器都是当作 int * 类型来处理
> 3. 形参中的数组和非形参中的数组的区别:形参中的数组是指针变量,非形参数组就是数组
* 2. 二维数组不是二级指针,指针数组才是二级指针
# 指针数组和数组指针
* 1. 指针数组,是数组,里面每个元素是指针.写法:
char* a; char a[];//这个叫做指针数组,不是叫做二维数组
* 2. 数组指针,是指针,指向数组的指针.
* 3. 指针函数,是函数;错误案例,代码会直接报错,因为pointer_Function执行完毕会释放里面的变量,指针指向的值也被回收了,然后指针就指向了一个空的内存首地址,指针指向了 NULL,变成了空指针:
int * pointer_Function()
{
int a = 200;
return &a;
}
void paFunction3()
{
int* pf = pointer_Function();
printf(“当前指针的内存首地址%p 当前指针内存块的值:%p 当前指针内存块所指向的内存的值:%d\n”,&pf,pf,*pf);
}
* 4. 函数指针,是指针
* 5. 字符指针,这个是比较特殊的
char str[] = “hello”;
printf(“%s\n”,str);//里面是重写了输出方法
//是下面的输出方式
int i = 0;
while(str[i]!= ‘\0’)
{
printf(“c”,str[i]);
i++;
}
//str本身还是首元素的内存首地址,只是输出比较特殊而已.本质还是个指针
char buf[100];
char* p = buf;
strcpy(p,”wqrywegw”);//不是给p本身拷贝内容,而是给p指向的内存块拷贝内容.
# 字符串结束符
0 数字0 与 '\0' 在字符串中是一样的,都是结束符
'\0' accsi 的结束符,与数字0在字符串中是一样的
'0' 字符0 ,在accsi中的数字表示是 48
char a[] = {'a','b'};
printf("%s",a);//乱码,没有结束符
char a[10] = {'a','b'};
printf("%s",a);//正常,自动补0
char a[] = {'a','b',0};
char a[] = {'a','b','\0'};
printf("%s",a);//正常
char a[] = {'a','b''0'};
printf("%s",a);//乱码,没有结束符
char buf[] = "hello";
printf("%s",buf);//正常,以字符串初始化,自动隐藏结束符'\0'
# 字符常量和字符指针数组
void fun()
{
printf("%p\n","aaa");
}
printf("%p\n","aaa");
printf("%p\n","aaa");
fun();
printf("%s\n","aaa"+1);//这种写法是正常的,因为这是将"aaa"当成内存首地址来使用的,类型是 char
printf("%c\n",*("aaa"));//取出首元素
//上面打印的地址都是一样的,所有的"aaa"就是字符常量,放在内存中的data区域,字符常量区
char buf[] = "aaa";//这个字符数组所指向的字符串,并没有放在data区域,而是在栈里面
char *p1 = "hello";//这个是字符常量,值是不可以被修改的
char buf[] = "hello";//这个是字符指针数组,值是可以被修改的
char p1[100]= "hello";//正确
p1 = "hello";//错误
char p2[100];p2="hello";//错误,因为数组名是常量.
char p3[100];strcpy(p3,"hello");等价于 char p1[100]= "hello";
```
数组,指针,符号含义
- 使用 *p , *(p+0), p[0] 都是代表了取出内存块里面的值, *(p+1)表示: *(指针运算),这都是表达的我要直接操作内存块,而不是操作指针(内存首地址)
- 使用 p+0 等运算,都是对指针 p ,内存首地址进行的运算,
一维数组的三种操作方式
//数组有3种操作方式 struct Student s[3] ={}; //直接操作内存块 s[0].age = 15; strcpy(s[0].name, "mike"); s[0].score = 15; //直接操作内存块 (*(s+1)).age = 15; strcpy((*(s+1)).name, "mike"); (*(s+1)).score = 15; //操作内存首地址 (s+2)->age = 15; strcpy((s+2)->name, "mike"); (s+2)->score = 15; //指针的写法 struct Student * p = &s[0];// = s; 2种写法一个意思 //直接操作内存块 p[0].age = 15; strcpy(p[0].name, "mike"); p[0].score = 15; //直接操作内存块 (*(p+1)).age = 15; strcpy((*(p+1)).name, "mike"); (*(p+1)).score = 15; //操作内存首地址 (p+2)->age = 15; strcpy((p+2)->name, "mike"); (p+2)->score = 15;
内存概念以及管理
- 简单的 int a; 这句代码已经被分配了内存首地址与内存块,而不是单纯的什么都没有.
- 简单的 int* p; 这句代码种 int* 合起来是一种类型,也已经分配了内存首地址与内存块,只不过内存首地址是指针变量p的内存首地址,内存块是保存指针地址(内存首地址),无法保存其他类型(int float struct … 的值).
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1487842110@qq.com
Title:C/C++指针详解
文章字数:2.7k
Author:诸子百家-谁的天下?
Created At:2020-05-08, 11:41:32
Updated At:2021-03-28, 02:59:27
Url:http://yoursite.com/2020/05/08/Pointer/%E6%8C%87%E9%92%88/Copyright: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。