1 指针的概述
1.1 指针的意义
1.1.1 指针的概念
程序在计算机中运行的过程,称为进程;在Linux系统中的进程是资源管理的最小单位。在32位的系统中构建一个4G的虚拟内存空间,用于存储程序运行的所有数据,包含程序和变量、常量等数据,并且数据存储的最小单位是以字节(8位数据)为单位。每一个字节都设置序号:0 ~ 4G(0XFFFF FFFF),此时的序号就称为地址,也称为指针。
1.1.2 指针访问的意义
- 通过指针提高对内存空间的访问效率;
- 通过指针引用连续存储空间可以简化代码逻辑。
- 指针作为函数的返回值和参数的时候,实现多个数据的服务器托管网输入和输出等;
- 使用指针实现对物理寄存器进行访问,实现硬件设备编程。
1.2 指针的认识
1.2.1 指针的理解
1.2.2 指针的定义
所谓的指针,指的是数据在内存空间存储的起始地址,也就是数据的指针;此时的指针指向整个数据的内存空间
对于一个变量可以使用取地址运算符&得到整个变量的地址(指针),此时的指针是指针常量,作为指针常量可以定义相同类型的指针变量存储其指针常量的值
1.指针变量定义的语法格式
存储类型 数据类型 * 指针变量名;
数据类型:不表示指针变量的数据类型,表示的是指针指向空间的数据类型。
存储类型:修饰的是指针变量本身的存储属性,可以用于存储其它任意属性的变量的地址。
*:在定义指针变量的时候,是一个特殊的修饰符号,表示所定义的变量为指针变量。
2.指针的使用:
运算符:
&:取地址运算符,得到变量(存储空间)的地址;
*: 作为运算符的时候,地址引用访问运算符,得到指针指向空间的数据;
指针的值:空间起始地址编号
指针指向的数据类型:数据的存储空间的大小和数据的表示形式。
#include
int main()
{
int a = 0x12345678;
/* 在定义int类型的指针,并将指针的值初始化为&a; */
int *p = &a; /* *是特殊说明符号,表示的是指针变量的定义 */
printf("a = %x, *(&a) = %x, *p = %xn", a, *(&a), *p);
*p = 0x87654321; /* *是地址引用访问符,引用指针目标空间的数据 */
printf("a = %x, *(&a) = %x, *p = %xn", a, *(&a), *p);
char *q = (char *)&a;
printf("p = %p, q = %pn", p, q); /* 指针变量p和指针变量q的值相同,访问的是同一个地址 */
printf("*q = %x, *p = %xn", *q, *p); /* 访问的数据内容不同 */
/* 结论:由于指针变量p和指针变量q指向的数据类型不同,相同指针值访问到的数据不相同 */
return 0;
}
2. 指针的运算
对于指针运算的前提:只有在连续存储空间中的相同数据类型的指针之间运算才有意义,其余情况下的指针运算是没有意义的。
2.1 指针的算术运算
1.指针的加法运算
指针 + 整型数据n:指针向高地址方向偏移n个元素,地址值的差值:sizeof(指针指向空间的数据类型) * n;
#include
int main()
{
int a = 123;
char c = 'a';
int *p = &a;
char *q = &c;
printf("p : %p, p+4 : %pn", p, p+4); /* int类型指针+4 ,差值相差16 */
printf("q : %p, q+4 : %pn", q, q+4); /* char类型指针+4,差值相差4 */
}
2.指针的减法运算
a.指针 – 整型数据n:指针向低地址方向偏移n个元素,地址差值:sizeof(指针指向空间的数据类型) * n;
b.指针-指针:表示两个指针之间相差元素的个数;
3.++运算符
a.后加加(p++):先取指针值,再对指针自加1运算(向高地址方向偏移1个元素);
*(p++) => *p++ :先去地址引用 *p,再对指针自加1运算 p++
b.前加加(++p):先对指针自加1运算,再取指针值
4. –运算符
a.后减减(p–):先取指针值,再对指针减1运算(向低地址方向偏移1个元素);
b.前减减(–p):先对指针自减1运算,再取指针的值
2.2 指针的关系运算和逻辑运算
对于两个指针的关系运算,可以用来判断两个指针空间的位置
>、>=、
!、&&、||
if(p) /* p != NULL 结果为true,否则p == NULL为false*/
if(!p) /* p == NULL 结果为true,否则p != NULL为false */
3. 数据在内存存储
3.1 数据存储结构图
数据在内存中的存储,在不同的系统中存储方式不同:由于内存的地址空间从低地址到高地址连续的,数据有高低位之分;根据数据在内存中存储顺序主要分为小端存储和大端存储两种方式:
1.小端存储:数据的低位存储在内存的低地址端,数据的高位存储在内存的高地址端。
2.大端存储:数据的低位存储在内存的高地址端,数据的高位存储在内存的低地址端。
数据存储实例图
3.2 计算机内存存储方式判断
#include
int main()
{
int i;
int a = 0x12345678;
char *p = &a;
for (i = 0; i
4. 指针和数组
对于数组在内存中开辟连续存储空间顺序存储数组中的每个元素;每一个元素的存储空间都有起始地址。所以使用指针对数组元素的访问。
4.1 一维数组和指针
4.1.1 一维数组和指针分析
#include
int main()
{
int i;
int a[5] = {1,2,3,4,5};
int *p = a;
printf("a[i] : ");
for (i = 0; i
4.1.2 一维数组和指针练习
1.练习1
以下程序运行结果为8,8
2.练习2
4.2 二维数组和指针
4.2.1 行指针的理解
在二维数组中,数组名表示的是数组首行元素的地址,指针指向的空间是一维数组,将其称为行指针。
所谓的行指针,指的是在二维数组中,指向一行元素的指针,就称为行指针。一行元素是一个数组,所以也称为数组指针。
行指针的定义:
int a[3][4];
由于a是二维数组的数组名,表示的二维数组首行元素的地址(是指针常量);
此时可以定义相同类型的指针变量存储指针常量的值:
int (*p)[4] = a; /* 采用右左原则分析:指针,数组(有4个元素,元素类型为int) ==> p是一个指向有4个int类型元素的数组的数组指针 */
数组指针(行指针)的定义
存储类型 数据类型 (* 指针变量名)[常量表达式];
存储类型:修饰指针变量
数据类型:修饰数组元素的数据类型
4.2.2 列指针的理解
#include
int main()
{
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
int *q = &a[0][0];
int i;
int j;
for (i = 0; i
4.3 指针数组
4.3.1 指针数组的理解
所谓的指针数组,指的是有多个相同数据元素类型指针的数组集合,称为指针数组。
实质是数组(满足数组定义的规则),数据元素类型为指针。
定义的语法规则:
存储类型 数据类型 * 数组名[常量表达式]
存储类型:修饰数组存储空间的属性;
数据类型:修饰数组数据元素指向空间的数据类型;
指针数组元素的访问:满足数组元素访问规则。
数组名[下标] 逐一元素访问
4.3.2 指针数组元素的初始化
1.先定义,在初始化
a.全局变量和static修饰的局部变量,在定义的时候未设置初始值默认初始值为0值(NULL);
b.未被static修饰的局部变量,在定义的时候未设置初始值默认初始值为随机值。
2.在定义的时候设置初始值
a.全部初始化
所有数组元素都是设置的初始地址值。(初始化数据个数等于常量表达式;省略常量表达式)。
b.部分初始化
初始元素部分未初始值,未初始化的元素部分为0值(NULL);
#include
int a = 345; /* 静态存储区 */
//int *ptr[3];
int main()
{
int i;
int b = 123; /* 栈区 */
int arr[3] = {1,2,3};
//static int *ptr[3];
int * ptr[3] = {&a, &b, arr};
for (i = 0; i
3.在实现指针数组元素值设计的时候注意:
数据元素指针指向的空间大小尽可能具有相同的属性。要么是相同类型的变量,也可以是同样大小的数组空间。
5. 多级指针
所谓的多级指针,指的是指针的地址,在引用的时候,也需要多次地址引用访问。
一般情况,二级指针就是多级指针的一种,指针的级数可以在递增。
#include
int main()
{
int a = 123;
printf("a = %dn", a);
int *p = &a;
printf("a = %d, *p = %dn", a, *p);
int **q = &p;
printf("a = %d, *p = %d, **q = %dn", a, *p, **q); /* 数据变量直接访问、一级指针访问、二级指针访问 */
**q = 111;
printf("a = %d, *p = %d, **q = %dn", a, *p, **q);
return 0;
}
6.特殊指针
6.1 void指针
在C语言中,有一个特殊的数据类型关键字void,不能用来修饰变量
1.可以修饰函数的参数和返回值的数据类型:表示函数没有参数和返回值;
2.可以用来修饰指针:有重要的意义,用来表示指针指向任意的数据类型。
void * 指针变量名;
意义:对于void类型指针是指向任意数据类型的指针,可以是同一个指针在不同时间指向不同的数据类型。
解决不同数据类型数据在完成相同逻辑运算功能。
使用注意:
在对于void类型指针访问,需要根据时间的数据类型访问(在读写访问的时候需要根据时间数据类型强制转换实现)。
void指针实例
#include
int main()
{
int a = 23;
char c = 'a';
void *p;
p = &a; /* void指针指向int类型的空间 */
printf("%dn", *(int *)p);
p = &c; /* void指针指向char类型的空间 */
printf("%cn", *(char *)p);
}
6.2 NULL指针和野指针
6.2.1 NULL指针
所谓的NULL指针,其实质是指针的零值:在标准C库中所定义的标识符常量,其值为(void *)0,也称为空指针。
意义:
1.可以用于指针的初始化,在定义的时候,如果指针的指向不明确的时候,可以将指针的初始值设置为NULL,表示指针没有指向。
2.用于函数的返回值,表示函数异常返回;
3.确保指针的正常访问,对于没有明确指向的指针,使用NULL进行判断。
对于指针p和零值的比较:
if(p == NULL) { /* 相反的比较:if(p != NULL) p不为零值条件成立;否则p为零值条件不成立 */
/* 指针p为零值,执行该部分 */
} else {
/* 指针p不为零值,执行该部分 */
}
if(p) { /* 相反的比较:if(!p) p为零值条件成立;否则p不为零值条件不成立 */
/* 指针p不为零值,执行该部分 */
} else {
/* 指针p为零值,执行该部分 */
}
6.2.2 野指针
所谓的野指针,所定义的指针变量所指向的空间没有开辟
1.一般情况下野指针产生:
a.在定义指针的时候,没有给指针设置初始值的时候,所定义的指针就是野指针;
b.指针在使用的过程中,将指针的值做了随意的修改,指向的空间并没有开辟;
c.如果指针指向的空间是动态开辟的空间,在空间释放后,指针的值没有修改。
2.对于野指针的访问会产生未知的异常:
a.程序出现段错误,野指针所指向的空间不具备读写访问的权限;
b.程序能够正常运行,数据发生异常。
c.整个程序的运行没有任何异常。
3.避免野指针的出现:
a.少使用或者不使用指针。
b.在定义指针的时候,需要初始化指针指向的空间(开辟存储空间)。
c.在指针指向不明确的时候,将指针的值设置为NULL。
#include
int main()
{
int *p = NULL; /* 在指针定义的时候,由于指向不明确,初始设置为NULL */
if (p == NULL) { /* 在访问指针之前,对其判断指针是否有指向,有指向才访问指针,否则不访问指针 */
return -1;
}
printf("*p = %dn", *p);
return 0;
}
6.3 const指针
所谓的const指针,指的是使用const修饰的指针变量;
1.const变量
所谓的const变量,指的是在C语言中,定义的变量使用const修饰,那么此时的变量为只读变量,不能被直接修改。
#include
int main()
{
const int a = 123; /* const修饰的变量a为只读变量,不能直接修改 */
int *p = &a;
printf("a = %d, *p = %dn", a, *p);
//a = 321; /* error,只读变量a的值不能直接修改 */
*p = 321;
printf("a = %d, *p = %dn", a, *p);
}
2.const修饰指针指向空间的内容
const int *p;
int const *p;
const修饰的指针的目标(指向空间的数据,指针的引用值)不能被修改,但是指针的指向(指针的值)可以被修改;
在定义的时候,需要初始化指针的目标内容;指针的指向可以使用之前设置。
int main()
{
const int a = 123;
const int b = 22;
const int *p = &a; /* const修饰的指针目标空间的数据,不能修改指针引用的值 */
// int const *p = &a; /* 等价于:const int *p = &a */
printf("p: %p, &a = %p, &b : %p, a = %d, b = %d, *p = %dn", p, &a, &b, a, b, *p);
//*p = 321; /* error,指针目标为cons服务器托管网t修饰,不能被修改 */
p = &b;
printf("p: %p, &a = %p, &b : %p, a = %d, b = %d, *p = %dn", p, &a, &b, a, b, *p);
}
3.const修饰指针的指向
int * const p;
const修饰的是指针的指向,也就是指针指向空间的起始地址不会改变。但是指针的目标(指向空间的数据内容)可以改变。
在定义的时候,需要初始化指针变量的指向(指针的值)。
int main()
{
int a = 123;
int * const p = &a; /* const修饰的指针变量p的指向,指针的起始地址值不能被修改 */
printf("a = %d, *p = %dn", a, *p);
// p = &a; /* error,指针的指向由const修饰,不能被修改 */
*p = 321;
printf("a = %d, *p = %dn", a, *p);
}
4.const修饰指针的指向和指针的目标
const int * const p;
第1个const修饰的指针的目标,指针指向空间的内容不能被修改,在定义的时候需要初始化指针目标内容。
第2个const修饰的指针的指向,指针指向空间的起始地址值不能被修改,在定义的时候需要初始化指针变量的指向。
int main()
{
const int a = 123;
const int b = 333;
const int * const p = &a;
printf("a = %d, *p = %dn", a, *p);
//p = &b; /* error,指针的指向是有const修饰,不能改变指针变量值 */
//*p = 111;
/* error, 指针的目标是由const修饰,不能改变指针目标空间的数据 */
}
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
0. RabbitMQ不可靠原因 消息从生产者到消费者的每一步都可能导致消息丢失: 发送消息时丢失: 生产者发送消息时连接MQ失败 生产者发送消息到达MQ后未找到Exchange 生产者发送消息到达MQ的Exchange后,未找到合适的Queue 消息到达MQ…