《重生到现代之从零开始的C语言生活》—— 指针2

const

const修饰变量

指针可以解引用修改变量,如果我们不想让它被修改可怎么办啊
这个就是const的作用

int main()
{
    int a = 0;
    a = 10;
  printf("%d",a)
    const int b = 0;
    b = 11;
  printf("%d",b);
  
    return 0;
}

在此代码中,a是可以被修改的,b是不能被修改的,因为b被const修饰从语法规则(记住是语法规则,不是把他变成了常量)上入手,给他做了限制,修改就不符合语法规则,导致没有办法直接修改变量

但是我们还是可以绕过语法规则,从地址出发改变变量

int main()
{
    const int a = 0;
    int * n = &a;
    *n = 10;
 
return 0;
}

此举就是绕过语法规则,从地址上更改变量,那怎么样可以不让他从地址改变变量呢

const修饰指针

const修饰指针有三种情况

int const * a = 0;//const在*左边
int * const a = 0;//const在*右边
int const * const a  = 0;//const在*左边和右边

那么他们分别代表什么呢?

int const * a = 0;

const在*的左边,修饰的是指向的内容,就代表着指向的内容不能通过指针来改变,但是指针变量本身可以改变(可以这么理解,const的后面是解引用a,就代表了修饰的是解引用a,也就是指向变量,所以可以修改储存在指针变量里面的地址,但是不能通过解引用来修改指向的变量

int * const a = 0;

const在*的右边,修饰的是变量,就代表着指针变量本身不能修改,但是指向的内容可以被修改(const后面是a,就是修饰指针变量,所以不能修改变量本身)

如果指针变量和指向内容都不想被修改,那简单,*左右两边都写一个const不就行了(滑稽)

指针运算

指针基本运算分为三种

  • 指针加减整数
  • 指针减指针
  • 指针的关系运算

指针加减整数

指针加减整数,就是将指针的往后或往前移动整数倍的指针变量类型的字节

指针减指针

指针减指针所得的是两个指针之间的元素个数

#include
int main()
{
int arr [10] = {1,2,3,4,5,6,7,8};
int * a1 = &arr[0];
int * a2 = &arr[4];
printf("%d",a1 - a2);
return 0;//运行结果为4
}

指针的关系运算

指针之间的关系也可以理解为地址和地址之间的关系
比如指针大小的比较

运用如下

#include
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8 };
    int* p = &arr[0];
    while (p < arr + 10)
    {
        printf("%d", *p);
        p++;

    }

    return 0;
}

野指针

野指针就是指针指向的位置不可知(随机的,不正确的,没有明确限制的)

野指针的形成有下面几种原因

  • 指针未初始化(未初始化,默认随机值)
  • 指针越界访问(指针指向的范围超出了原规定指向的范围)
  • 指针指向的空间释放(指函数中的变量或局部函数的空间释放,指针无法指向,造成野指针)

想要规避野指针,我们就要从根源出发

  • 指针初始化(知道指向哪里就赋值,不知道指向哪里就给指针赋值NULL
  • 小心指针越界
  • 避免返回局部变量的地址
  • 指针变量不再使用时,及时置NULL,指针使用之前检查有效性

NULL是C语言中定义的一个标识符常量,值是0,0也是地址,但这个地址是无法使用的,读写该地址会报错,就能很好的避免野指针的情况发生

assert

assert.h头文件定义了宏assert( ),用于在运行时确保程序符合指定条件,如果不符合,就报错终止运行。这个宏常常被称为**“断言”**

assert(p != NULL)

在代码运行到这一行的时候,会验证变量p是否等于NULL,如果不等于,程序会继续运行,否则的话会报错
assert()不仅能自动标识文件和出问题的行号,还无需修改代码就能关闭。当确定程序没问题时,在#include语句的前面,定义一个宏NDEBUG

#define NDBUG
#include 

一般可以在Debug版本使用,在Relesse版本中禁用就可以

传值调用和传址调用

写一个函数,交换a和b的值

#include
void swap(int a, int b)
{
	int t = 0;
	t = a;
	a = b;
	b = t;
}
int main()
{
	int a = 2;
	int b = 3;
	printf("%d ,%d\n",a, b);
	swap(a, b);
	printf("%d ,%d",a, b);

	return 0;
}

我们在main函数的内部,创建了a和b,在调用swap时将a和b传递给swap函数中的形式参数,函数中形式参数确实接受了a和b的值,但是形式参数有他们自己的地址,意思是,形式参数只是有了a和b值的参数,因为地址不一样,所以怎么改变形式参数的值和main函数中的a和b都没有关系

这就是传值调用

那么直接将a和b的地址传给swap函数不就行了

#include
void swap(int * a, int * b)
{
	int t = 0;
	t = *a;
	*a = *b;
	*b = t;
}
int main()
{
	int a = 2;
	int b = 3;
	printf("%d ,%d\n",a, b);
	swap(&a, &b);
	printf("%d ,%d",a, b);

	return 0;
}

这就是传址调用
把地址传给函数就能从根本实现函数值的改变


今天的知识讲解完啦,如果觉得有用可以点一下赞和关注,也可以先收藏以防需要时找不到哦,当然如果作者写的哪里有问题欢迎指出,我们一起进步!!!

祝看到这里的人天天开心哦(笔芯)

你可能感兴趣的:(c语言,生活,开发语言)