C语言指针详解(示例及代码及概念)

【知识点1】指针的概述

1 指针的理解

1.1 虚拟内存的认识

程序中的数据,都会在内存中存储。在Linux 系统中程序运行构造一个虚拟内存空间;

1) 在32位系统中虚拟内存空间的大小为4G

在32位系统中,寄存器所占位数为32位,最大所能表示的数据为4G,也就说最大能够存储的地址编号为4G内存编号。

2) 在64位系统中,虚拟内存空间大小大于4G。

1.2 指针的认识

对于数据存储在虚拟内存中,而虚拟内存以字节为单位实现数据的存储,每一个字节都有对应序号,整虚拟内存空间的的地址编号是连续编号。在虚拟内存空间的数据可以通过地址编号实现数据的访问,也就是通过指针访问。

总结:

对于一个指针,需要关注两个点:

1) 指针的值:指针所指针空间的起始地址;

2) 指针的类型:指针解引用空间的大小和数据形式。

1.3 指针的定义

存储类型 数据类型 *指针变量名;

数据类型:表示指针指向空间(目标)数据类型;

存储类型:表示指针变量本身存储空间的存储属性;

*:类型修饰符,表示所定义的变量为指针变量;

在定义指针变量的同时设置初始值,需要保证初始值的数据类型和指针本身的数据类型保持一致。

#include <stdio.h>

int main() { int a = 34; /* 定义一个int类型变量 */

int *p = &a; /* 定义一个int类型指针,并初始化为变量a的地址 */

*p = a; /* 对指针p解引用访问修改为变量a的值 */

printf("a = %d, *p = %d\n", a, *p); }

1.4 指针的运算

对于指针的运算,只相同类型且连续空间的指针之间的运算才有意义。

  1. 指针的算术运算:
    1. 指针 + 常量 向高地址方向偏移常量个元素,偏移字节数 = 常量*元素空间的大小;
    2. 指针 - 常量 向低地址方向偏移常量个元素,偏移字节数 = 常量*元素空间的大小;
    3. 指针 - 指针 相差元素的个数;
    4. 指针的++和--
  2. 指针关系运算

指针的关系运算,用来判断两个指针之间的位置关系。

  1. 指针的逻辑运算
  2. 数据的大小存储
  3. 对于多字节数据,由于数据是以字节为单位存储,需要连续的多字节存储空间存储,并且连续内存空间有高低地址之分,而数据以字节有高低位之分,数据在内存中有两种存储方式:
  4. 小端存储:数据的低位存储在内存的低地址端,数据的高位存储在内存的高地址端;
  5. 大端存储:数据的低位存储在内存的高地址端,数据的高位存储在内存的低地址端。

#include <stdio.h> int main() {

int i;

int a = 1; //数据由高到低位数据分别为:0x00、0x00、0x00、0x01

char *p = &a; //使用char类型指针,可以取存储空间中低地址空间数据

/* 利用指针判断数据的大小端存储 */

if (*p != 0) {

/* 所取的数据为0x01(低地址数据为数据的低位)说明为小端存储 */

printf("lit\n");

} else {

printf("big\n");

}

a = 0x12345678;

for (i = 0; i < sizeof(int); i++) {

printf("%x ", *(p++));

//*p;

p++;

}

printf("\n");

}

【知识点2】 数组和指针

1 一维数组和指针

        #include <stdio.h> int main() {

int i;

int arr[5] = {1,2,3,4,5}; /* 定义了一个一维数组:开辟连续内存空间存储多个相同类型的数据元素 */

int *q = arr; /* 定义指针存储首元素的地址 */

int (*p)[5] = &arr; /* 定义数组指针存储整个数组存储空间的地址 */

printf("%p-%p\n", p, p+1); /* 地址值的差值为整个数组空间的大小 */

printf("%p-%p\n", q, q+1); /* 地址值的差值为数组元素空间的大小 */ for (i = 0; i < 5; i++) { printf("%d ", arr[i]); /* 数组名[下标],其实质就是通过基地址+偏移量解引用访问 */

}

printf("\n");

for (i = 0; i < 5; i++) {

printf("%d ", *(arr+i));

}

printf("\n");

for (i = 0; i < 5; i++)

{

printf("%d ", q[i]);

} printf("\n");

for (i = 0; i < 5; i++) {

printf{

"%d ", *(q+i));

} printf("\n");

for (i = 0; i < 5; i++) { printf("%d ", *q++); }

printf("\n");

}

2 二维数组和指针

2.1 行指针

在二维数组中,其数组名表示的是首行元素的地址

 

2.2 列指针

数组元素在内存中是线性顺序存储,所有的元素可以看成一列元素。此时可以使用首元素的地址实现整个数组元素的访问。

访问:

1) 可以通过列指针的自加减实现元素的访问;

2) 可以指针+常量方式实现元素的访问;

#include <stdio.h> int main() { int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; int (*p)[4] = arr; int i; int j; printf("%p - %p\n", &arr, &arr+1); //48; printf("%p - %p\n", arr, arr+1); //16; printf("%d\n", arr[2][3]); printf("%d\n", *(*(arr+2)+3)); printf("%d\n", *(arr[2]+3)); printf("%d\n", (*(arr+2))[3]); printf("%d\n", p[2][3]); printf("%d\n", *(*(p+2)+3)); printf("%d\n", *(p[2]+3)); printf("%d\n", (*(p+2))[3]); for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { printf("%d ", *((*p)+j)); } p++; printf("\n"); } int *q = &arr[0][0]; for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { printf("%d ", *(q+i*4+j)); } printf("\n"); } for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { printf("%d ", *q++); } printf("\n"); } }

3 指针数组

所谓的指针数组,其实质是数组,数组中的每一个元素是相同类型的指针。具有多个相同类型的指针所构成的集合。

语法:

存储类型 数据类型 *数组名[常量表达式];

存储类型:表示整个数组的存储属性。

数据类型:表示数组元素所指向的数据类型;

*:特殊类型说明符,表示所定义的数据为指针数据。

数组名:数组集合名称;

常量表达式:数组元素的个数

注意:

由于数组元素为指针,只能表示连续存储空间中起始元素的地址;

1) 所指向的连续存储空间中元素个数相同的时候,可以采用相同的逻辑实现数据元素的访问;

2) 所指向的连续存储空间中元素个数不同的时候,需要避免连续存储空间的访问越界和少访问的问题。

一般用于存储多个字符串数据,可以通过字符串结束表示'\0'判断;还可以在传递连续存储空间起始地址的时候传递元素的个数。

【知识点3】多级指针

所谓的多级指针,指的是指针的指针。

int main() { int a = 3; /* 定义一个普通数据变量 */ int *p = &a; /* 定义一级指针存储普通数据变量存储空间的地址 */ int **q = &p; /* 定义二级指针存储一级指针变量存储空间的地址 */ printf("a = %d, *(&a) = %d\n", a, *(&a)); printf("%d:a = %d, *p = %d\n", __LINE__, a, *p); *p = 4; /* 一级指针的解引用访问 */ printf("%d:a = %d, *p = %d\n", __LINE__, a, *p); printf("%d:a = %d, *p = %d, **q = %d\n", __LINE__, a, *p, **q); **q = 5; /* 二级指针的解引用访问 */ printf("%d:a = %d, *p = %d, **q = %d\n", __LINE__, a, *p, **q); }

【知识点4】 特殊指针

1 void 类型指针

void 是特殊的数据类型关键字,不能修饰数据类型变量;可以用于定义指针变量。

void * 指针变量名; /* 用于修饰指针变量 */

此时的指针变量,可以实现任意数据类型指针的存储。从而实现不同数据类型的数据的运算。

对于void 类型指针不能直接解引用访问,需要强制转换为实际数据类型的指针才能解引用访问。

#include <stdio.h> int main() { int a = 3; float f = 3.14; void *p; /* 定义void 类型指针 */ p = &a; /* 使用void类型指针存储int类型指针变量 */ printf("%d\n", *(int *)p); /* 对于void类型指针的解引用不能直接解引用访问,需要转换为实际数据类型指针解引用访问 */ p = &f; /* 使用void类型指针存储float类型指针变量 */ printf("%f\n", *(float *)p); }

2 NULL指针

NULL 是标识符常量,表示指针的零值。 具体值表示:((void *)0)。常用表示指针没有具体的指向。

3 野指针

所谓的野指针,指的是指针所指向的内存空间未做开辟,在对指针解引用访问会出现未知异常(程序正常运行且数据正常、程序正常运算数据异常、段错误异常)。

野指针的产生:

1) 只做定义,未作初始化的指针为野指针;

2) 强制修改指针变量的值,可能产生野指针;

3) 对于指针变量所指向的动态开辟的内存在做内存动态释放后,此时的指针也称为野指针。

避免野指针的产生:

1) 在定义的时候对指针指向初始化设置,没有指向设置为NULL;

2) 在指针修改的时候,可以保证新的指针指向有开辟,如果没有开辟设置为NULL

3) 在动态释放,将其值设置为NULL。

在对指针访问的时候,需要先做判断在做访问。

【知识点5】 const指针

1 const修饰变量

此时的变量为只读变量,在C语言中,不能直接修改,可以通过指针间接修改。

#include <stdio.h> int main() { const int a = 3; int *p = &a; printf("%d: a = %d\n", __LINE__, a); // a = 4; /* 语法问题:const修饰的只读变量,不能直接修改 */ *p = 5; /* const修饰的只读变量,可以通过指针实现间接修改 */ printf("%d: a = %d\n", __LINE__, a); }

2 const修饰指针

2.1 修饰指针的目标

也就是修饰指针所指向目标内存空间的内容,

int main() { const int a = 3; const int b = 4; const int *p = &a; /*const修饰指针的目标, 指针指向空间的内容不能被修改(在定义的时候需要初始化) 指针的指向可以修改(在定义的时候可以不做初始化) */ printf("%d: a = %d, *p = %d\n", __LINE__, a, *p); // *p = 5; /* 语法问题:指针的目标不可以修改 */ p = &b; /* 指针的指向可以修改 */ printf("%d: a = %d, *p = %d\n", __LINE__, a, *p); }

2.2 修饰指针的指向

也就是修饰指针的值

int main() { int a = 3; int b = 4; int * const p = &a; /* const 修饰的指针的指向,也就是指针的值; 1) 指针指向空间的内容可以修改, 2) 指针的值(指针指向的空间)不能修改,在定义指针的时候需要初始值设置。 */ printf("%d: a = %d, *p = %d\n", __LINE__, a, *p); *p = 5; // p = &b; printf("%d: a = %d, *p = %d\n", __LINE__, a, *p); }

2.3 修饰指针的目标和指向

int main() { int a = 3; int b = 4; const int * const p = &a; /* 1) 第1个const修饰的指针的目标,指针指向空间的内容不能被修改,需要定义同时初始值设置 2) 第2个const修饰的指针的指向,指针的值不能被修改,需要定义同时初始值设置。 */ printf("%d: a = %d, *p = %d\n", __LINE__, a, *p); // *p = 5; /* 语法问题 */ // p = &b; /* 语法问题 */ printf("%d: a = %d, *p = %d\n", __LINE__, a, *p); }

【知识点6】零值比较

bool数据类型变量、char数据变量、整型数据变量、float数据类型变量和指针类型变量和零值的比较

/* bool数据类型和零值比较 */ bool mybool; if (mybool == false) { printf("mybool is 0\n"); } else { printf(" mybool != 0"); } if (!mybool) { printf("mybool is 0\n"); } else { printf(" mybool != 0"); } int a; if (a == 0) { printf("a == 0\n"); } if (!a) {printf("a == 0\n") } float f; if ( f > -0.000001 && f < 0.000001) printf("f is 0\n"); char *p; if (p == NULL) printf("p is NULL\n"); if (!p) printf("p is NULL\n");

图解:

 

 

0

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值