四、指针
6、指针的大小
int a = 10;
int *p1 = &a;//8
char ch = 'a';
char *p2 = &ch; //8
sizeof(指针变量名)
#include<stdio.h> int main() { int a = 10; int *p1 = &a; printf("%d", sizeof(p1)); char ch = 'a'; int *p2 = &ch; printf("%d", sizeof(p2)); return 0; }
64位操作系统:指针大小8字节
32位操作系统:指针大小4字节
linux优化:把16位优化掉几个0,只显示12位
Windows系统:有16位就显示16位
指针总结
1、32位操作系统:指针大小4字节,64位操作系统:指针大小8字节
2、内存地址是固定的,但是变量的地址是固定的,因为栈区变量随机分配
3、指针类型根据指针指向空间的数据类型不同而不同
7、段错误
Segeentation fault (core dumped)——核心以转储
产生原因:访问不存在的内存地址,访问系统保护的地址,访问只读的内存地址,空指针废弃(eg:malloc 与 free 释放后,继续使用),栈堆溢出,内存越界(数组越界,变量类型不一致等)
7、指针修饰
1、const 常量化
int const a= 10; const修饰后面的内容
1)const int a = 10;
int const a= 10;
a = 20; // 错误,a不能改变,可以通过指针间接修改
int *p = &a; // 警告,a被const修饰,运行的化a的值会改变
*p = 20; //因为 const 修饰的是a,没有修饰*p
2)const int *p; //修饰*p,指针指向的内容不能修改,但指针指向可以修改
int const *p;
int a = 10;
int const *p;
*p = 20; // 错误,因为*p被修饰
int b = 20;
p = &b; // 指针指向被修改了
3)int * const p;
int a = 10;
int b = 20;
int *const p = &a; // 错误
*p = 30; //正确,指向不能改变,指针内容可以变
p = &b; // 错误,修饰的是p,指针指向不能改变
2、void
void a; // 不允许修饰普通变量
void *p; // 允许修饰地址变量(8字节)
使用场景:函数参数或者函数的返回值
注意:通过void *类型的指针变量进行取内容的时候,需要对地址进行强转
转换方式:void * p = NULL;强转:(int *)p 取内容:*(int *)p
int a = 10;
void *p = NULL;
p = &a;
int *q = (int *)p;
printf("%d %d\n",*q,*(int *)p); // 10, 10
3、大小端
在计算机进行超过1字节数据进行存储时,会出现存储数据顺序不同的情况即大小端存储
Big-Endian(大端字节序)大端:在低地址存放高字节数据,高地址存放低字节数据
Little-Endian(小端字节序)小端:在低地址存放低字节数据,在高地址存放高字节数据
举例:存储数据:0x12345678
大端 小端 0x4000 0x12 0x78 0x4001 0x34 0x56 0x4002 0x56 0x34 0x4003 0x78 0x1
int a = 0x12345678
char b;
b = (char) a;
printf("%x\n", b);
电脑是小端,网络是大端
电脑向网络传输东西,数据要从小端转成大端,传出去
网络向电脑传输东西,数据要从大端转成小端,接收数据
二级指针
一级指针:存放变量的地址
二级指针:存放一级指针的地址(指针变量的地址)
格式
存储类型 数据类型 **指针变量名
int a = 10;
int *p = &a;
int **q = &p;
访问a的值:
a *p **q
访问a的地址:
&a p *q
访问p的地址:
&p q
指针和数组
直接访问:按变量的地址存取变量的值(通过数组名访问)
间接访问:通过存放变量的地址取访问元素(通过指针访问)
1、指针和一维数组
int a[5] = {1, 2, 3, 4, 5};
int *p = a;
直接访问
#include <stdio.h>
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int *p = a;
printf("%p %p %p\n",a, a+1, a+2);
return 0;
}
间接访问
#include <stdio.h>
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int *p = a;
printf("%p %p %p\n",p, p+1, p+2);
return 0;
}
a和p本质上不同,a是地址常量,p是变量,a不能执行++操作,但是p可以
访问数组元素:a[i]的值:
直接访问:a[i] *(a+i)
间接访问:p[i] *(p+i)
访问数组元素地址:a[i]
直接访问:&a[i] a+i
间接访问:&p[i] p+i
int a[3] = {3, 2, 1};
int *p = a;
printf("%d\n", *p++); // 3 再打印一次*p就是2
printf("%d\n", *a++); // a不能++,a是地址常量
运算方法:
1)++和*都是单目运算符,优先级相同
2)运算顺序从右向左进行运算
*(p++) // 3 实际上指针指向第二个元素的地址
(*p)++ // 3 实际上第一个元素变成了4
++*p // 4 自加之后的值,从右开始,*p为3,++后变成了4,++在前先赋值再打印
++*(p) // 同上
*++p // 2 先++p,++在前,++p之后指针指向为第二个元素,再*(取地址内容)为2
*(++p) // 同上
指针和二维数组
int a[2][3] = {1, 2, 3, 4, 5, 6}; // a 数组名,表示第一行首地址,a+1表示第二行首地址
在a前面加*,表示将行地址降级为列地址
*a:第一行第一列的地址 a[0]
*a+1:第一行第二列的地址
*(a+1):第二行第一列的地址
*(a+1)+1:第二行第二列的地址
*(*(a+1)+1):第二行第二列的值
直接访问:
*(*(a+i)+j):第i+1行第j+1列的元素
*(a[i]+j):第i+1行第j+1列的元素
a[i][j]:第i+1行第j+1列的元素
间接访问:
数组指针
定义:本质上是指针,指向数组的指针,行指针
格式:存储类型 数据类型 (*指针变量名)[列数];
int a[2][3] = {1, ,2 ,3 ,4 ,5 ,6};
int (*p)[3] = a;
p:int (*)[3]:运算时三个三个算的
p 可以代替a进行元素访问,但是本质不同
访问a[i][j]的地址:
&p[i][j] == &a[i][j]
p[i]+j == a[i]+j
*(p+i)+j == *(a+i)+j
访问a[i][j]的值:
p[i][j]
*(p[i]+j)
*(*(p+i)+j)
大小:
sizeof(p);64位,8字节
地址:
a+i
a:第一行的地址
a+1:第二行的地址
a[i]+j
a[0]: 第一行第一列的地址
a[0]+1: 第一行第二列的地址
a[1]: 第二行第一列的地址
a[1]+1: 第二行第二列的地址
*(a+i)+j
元素:
a[i][j]
a[0][0]: 第一行第一列
a[0][1]: 第一行第二列
*(a[i]+j)
*(*(a+i)+j): 第i+1行第j+1列的内容