ywrby

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

所谓指针,本质就是标识内存中某个位置的数据

在日常学习中,我们需要了解的只是:

  1. 内存中每个位置都是由独一无二的地址标识
  2. 内存中的每个位置都包含一个值

基本概念

运算符 ‘*’

通过指针访问指向的值的过程称为间接访问(indirection)或者叫做解引用指针(dereferencing the pointer),执行间接访问的操作符就是单目运算符’*’

这里一定要注意,在变量声明时使用的’’,与解引用时使用的’'有着本质上的区别,不可以将二者作用混淆

int a = 50;
int* b = &a;
//这里的'*'主要起声明的作用,告知程序当前变量b是一个指针型变量
printf("%d",*b);
//这里的'*'是单目运算符,对指针型变量b执行取值操作

编写程序过程中我们往往会定义诸多的变量名实现对值的操控,在这个过程中,变量名与内存地址的关联并不是硬件提供的,它是由编译器为我们实现的,所有这些变量名给了我们一种更方便的方法记住地址,但是–++硬件仍然通过地址来访问内存位置++

#include<stdio.h>

int main() {
	int a = 50;  //a的本质是值
	int* b = &a;  //b的本质是指针变量
	printf("b=%d\n", b);  //这里直接打印出b的值
	//b作为一个指针变量,直接打印出来的就是a的内存地址,而不是a的值
	printf("*b=%d\n", *b);  //对b表示的内存地址进行*操作(取值操作)
	//利用地址取值后,得到的就是存储于这个地址的a的值
	printf("&b=%d\n", &b);  //对b变量进行取地址操作
	//b虽然是存储地址,但其作为指针变量,本质还是变量
	//变量就必然需要内存来存储,也必然有自己的内存地址
	//所以,取地址后的数据其实就是b的内存地址
}

运行结果

b=17824784
*b=50
&b=17824772

未初始化和非法的指针

int main() {
	int* a ;
	*a = 50;
	/*
	上述程序看起来符合逻辑,但实际上是十分严重的错误
	 *我们并没有对a指针进行初始化,所以它所指向的地址是未知的
	 *一般情况下,a的初始值会是一个非法地址,进而在程序执行过程中
	 *Windows出现一般保护性异常阻止程序继续进行
	 *最坏情况可能导致a指向一个合法的地址,进而修改合法地址内的值
	 *造成严重错误,且不容易被察觉
	 */
	return 0;
}

NULL指针

NULL作为一个特殊的指针变量,表示不指向任何地址

int main() {
	int* a = NULL;
	if (a == 0) {
		printf("True\n");
	}
	else {
		printf("False\n");
	}
	return 0;
}

观察上述程序不难发现,在程序编译过程中,NULL指针和0值是完全等价的,这是一种基于源代码的约定,有助于我们测试一个指针变量是否为NULL ,而NULL指针的实际值不一定是0

对一个NULL指针进行解引用操作是违法的,因为NULL指针并没有指向任何内容,所以在对指针进行解引用之前,必须要确定指针是否为空

指针变量可以作为左值也可以做为右值

int a=10;
int* b=&a;
*b=20-*b;
b=20-*b;

上述第三条语句是合法的,第一个表示取到该内存地址内的变量,对它进行重新赋值,第二个表示,取出该内存地址内变量的值,利用值进行计算

第四条语句是非法的,指针变量不能直接作为左值进行赋值操作(K&R C)

指针的指针

int a=50;
int* b=&a;
int** c=&b;

利用指针获取字符串长度

int MyStrLen(char* str) {
    int length = 0;
    //依次访问字符串内容,直到遇到NUL终止符
    while (*str++ != '\0') {
        length++;
    }
    return length;
}

int main()
{
    char* str = "HelloWorld";
    int len = MyStrLen(str);
    printf("%s\nlength=%d\n", str, len);
    return 0;
}

指针与数组

利用指针遍历数组

#include<stdio.h>
void main() {
	int score[7] = { 95,89,56,78,45,59,77 };
	int sum = 0;
	int* p1;
	for (p1 = score; p1 < &(score[7]); p1++) {  //这里第一个位置还可以用p1=&(score[0])
		printf("%d\n", *p1);
	}
}

ps:数组名就是数组首位元素的地址

指针运算

int main()
{
    int* p[5] = { 1,2,3,4,5 };
    int* num = &p[0];
    int* pnum = num + 2;
    ptrdiff_t n = (pnum + 1) - num;  //两个指针变量之间做差,得到结果是一种有符号整型
    //表示两个指针在内存中的距离(以数组元素长度为单位,而不是字节长度)
    printf("*num=%d,*pnum=%d\n", *num, *pnum);
    printf("(pnum+1)-num=%d\n", n);
    return 0;
}

注意:上述指针之间相减求距离的前提是位于同一个数组内,否则结果无意义

PS:同一个数组内的指针可以进行’<’,’<=’,’>’,’>='运算,此类比较表达式可以对指针在数组中的位置做出比较

运算结果

*num=1,*pnum=3
(pnum+1)-num=3

除了NULL指针外,再也没有任何内建的记法用来表示指针常量,因为程序员通常无法预测编译器会把变量放到内存中哪个位置,当出现极特殊情况我们需要自定义指针常量时,一般采取将整形数据强制类型转换为指针常量的方式

posted on 2020-04-05 18:29  ywrby  阅读(151)  评论(0编辑  收藏  举报