C--初识指针

目录

前言:

一.指针的初识

1.解引用操作符和取地址操作符

2.指针的类型

3.特殊类型

4.const修饰指针

5指针运算

1)指针+-整数

2)指针-指针

3)指针的关系运算

6.野指针

成因:

预防方法:

7.assert 函数     

8. 指针的使用和传址调用


前言:

学好指针是学好c语言的一个好的起点,也是一个重要的基础,想要学好c语言,指针必不可少。


一.指针的初识

通俗的讲:内存单元的编号==地址==指针

我们口语上常说的指针,其实指的就是指针变量。

规定:一个内存单元就是一个字节,每一个内存单元都对应着一个地址。

1.解引用操作符和取地址操作符

取地址操作符(&)

例:&a取出的是a所占4个字节中地址较小的字节的地址。

解引用操作符(*)

取地址操作符(&)拿到的地址是⼀个数值,这个数值有时候也是需要存储起来,方便后期再使用的,而我们将这个数值存放在指针变量中。                                                                                    指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址。

#include <stdio.h>
int main()
{
    int a = 10;
    int* pa = &a;//取出a的地址并存储到指针变量pa中
    return 0;
}

2.指针的类型

像int double float char 类型一样,指针也有自己相应的类型,

而指针的类型就是指针所指向元素是什么类型,

指向int的指针就是整型指针指向char类型的就是字符型指针等等。

指针的大小都是确定的

在32位的编译器下指针变量的大小都是4个字节,在64位编译器下指针的大小都是8个字节

意义:

1)决定了在解引用时访问几个字节如果是 int* 类型的就访问4个字节

2) 决定了对指针解引用时有多大的权限(⼀次能操作几个字节)

3.特殊类型

void* 指针为无具体类型的指针(或者叫泛型指针),这种类型的指针可以用来接受任意类型地址。但是也有局限性,void* 类型的指针不能直接进行指针的+-整数和解引用的运算。

作用:

⼀般 void* 类型的指针是使用在函数参数的部分,用来接收不同类型数据的地址,这样的设计可以 实现泛型编程的效果。

4.const修饰指针

#include <stdio.h>
//代码1 
void test1()
{
    int n = 10;
    int m = 20;
    int* p = &n;
    *p = 20;//ok?
    p = &m; //ok?
}

//代码2 

void test2()
{
    int n = 10;
    int m = 20;
    const int* p = &n;
    *p = 20;//ok?
    p = &m; //ok?
}
//代码3
void test3()
{
    int n = 10;
    int m = 20;
    int* const p = &n;
    *p = 20; //ok?
    p = &m;
}

结论:

const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。但是指针变量本身的内容可变

const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变

5指针运算

指针的基本运算有三种

指针+-整数

指针-指针

指针的关系运算

1)指针+-整数

因为数组在内存中是连续存放的,只要知道第⼀个元素的地址,就能找到后⾯的所有元素

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = &arr[0];
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));//p+i这⾥就是指针+整数
	}
	return 0;
}

2)指针-指针

得到的是指针之间元素的个数,并不是所有的指针都能相减,只有位于同一空间的两个指针才能相减。

指针+指针 与 地址+地址 == 没有意义

#include <stdio.h>
int my_strlen( char *s)
{
	char* p = s;
	while (*p != '\0')
		p++;
	return p - s;
}
int main()
{
	printf("%d\n", my_strlen("abc"));
	return 0;
}

3)指针的关系运算

本质是比较指针的大小

指针和指针比较大小==地址和地址比较大小

6.野指针

成因:

1. 指针未初始化、

如 int* p;

*p = 10;

没有初始化,意味着没有明确地指向

一个局部变量不初始化,存放的是随机值,如0x000ff00a

此时这个地址是一个非法的地址。

然后 *p = 10;非法访问内存,此时p就是野指针。

2. 指针越界访问

当指针指向的范围超出数组的范围时,该指针就是野指针。

3. 指针指向的空间释放

#include <stdio.h>
int* test()
{
	int a = 0;
	return &a;
}
int main()
{
	int* p = test();
	return 0;
}

因为a是局部变量,离开自己的作用域就被销毁,*p记住了a的地址,但不可以使用,a的空间已经被释放了,所以p成为野指针。

预防方法:

1.指针初始化:

在我们使用指针时应明确知道自己应该给他赋上一个什么值

若不知道赋什么值 则 int* p = NULL;

2.避免返回局部变量的地址。

3.小心指针越界

⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问

7.assert 函数     

assert()函数被包含在<assert.h>头文件中

用于在运行时确保程序符合指定条件,如果不符合,就报错终止运行。

常被称为“断言”。

assert(p != NULL);

上面代码在程序运行到这⼀行语句时,验证变量p 是否等于 NULL 。如果确实不等于 NULL ,程序继续运行,否则就会终止运行,并且给出报错信息提示。 

好处:

它不仅能自动标识文件和出问题的行号,还有⼀种无需更改代码就能开启或关闭 assert() 的机制。

就在 #include 语句的前⾯,定义⼀个宏

#include NDEBUG 

#include < assert.h >

缺点:因为引入了额外的检查,增加了程序的运行时间。

8. 指针的使用和传址调用

传值调用和传址调用

例如:写⼀个函数,交换两个整型变量的值

#include <stdio.h>
void swap1(int x, int y)
{
	int tmp = x;
	x = y;
	y = tmp;
}
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	printf("交换前:a = % d b = % d\n", a, b);
	swap1(a, b);
	printf("交换后:a = % d b = % d\n", a, b);
	return 0;
}

但是我们发现运行结果并没有发生变化

解释:相当于x和y是独立的空间,那么在swap1函数内部交换x和y的值, 自然不会影响a和b。

结论:实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实参。

#include <stdio.h>
void swap2(int* px, int* py)
{
	int tmp = 0;
	tmp = *px;
	*px = *py;
	*py = tmp;
}

int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	printf("交换前:a = % d b = % d\n", a, b);
	swap2(&a, &b);
	printf("交换后:a = % d b = % d\n", a, b);
		return 0;
}

运行结果是正确的。

这里调用Swap2函数的时候是将变量的地址传递给了函数,这种函数调用方式叫:传址调用。

传址调用,可以让函数和主调函数之间建立真正的联系,在函数内部可以修改主调函数中的变量。

  • 24
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值