C_07_指针

指针

地址编号: 存的是值

指针: 存储的是地址编号值 的数据类型 是地址编号的数据类型,强调的是类型

指针变量: 数据类型为指针的变量,用于保存地址编号

地址编号

概述:

地址编号是内存每一个字节的编号统称。

​ int n = 10;

​ 在内存分配了4个字节

每个进程(运行着的程序)都有寻址范围

32位机的寻址范围时4G,0x00 00 00 00~0xff ff ff ff.

系统会为每一个字节分配一个32位的地址编号。便于系统管理内存

32位机的地址编号占4字节

64位机的地址编号占8字节

指针与指针变量

指针:是地址编号的数据类型,强调的是类型

指针变量:数据类型为指针的变量,用于保存地址编号

注意

在这里插入图片描述

指针变量定义

语法:

数据类型 * 变量名;

注意:

数据类型: 指针变量指向的地址中存储的数据 的数据类型

1,不同的指针只能存储对应的数据类型的地址

​ 如:

​ 整型的指针变量,只能存放整型变量的地址

2,如果在一行中定义多个指针变量,每个指针变量前面都需要加 * 来修饰

如:

int *p1;
char *p2;
float *p3;
double *p4;
void *p5;
...
int nums[] = {1,2,3,4,5};
int *p6 = nums;
int *p1,*p2,*p3;

指针变量的初始化

注意

1,当指针变量为局部变量,并没有为其赋初始值,此时因为是局部变量,所以系统为其赋 值为随机数,此时我们并不知道指针变量存
储的地址是什么.这种指针我们称为野指针
2,指针变量没有指向合法的地址建议初始化为NULL,这种指针我们称为空指针

int  *p  = NULL;      //空指针: 是NULL  不是0  0也有地址 而NULL 没有
int  *p;     //因为没有初始化所有初始化随机  所以就不知道指向哪里了  就是野指针

指针运算符

&

作用:取地址

int a = 10;
int *p = &a;
printf("%p\n",p);    //此时打印的是p的值即a的地址

注意:

​ 栈:静态全局区可取地址
​ 堆,常量池,代码区不可取

*

作用:取值

printf("%d\n",*p);   //将p中存储的地址中的值获取

作用**:改值**

*p =;
注意事项

野指针与空指针取值操作会出现段错误

哪怕用空指针也不用野指针 虽然空必然报错 但是错误是显性的 野指针对程序一定是坏的

因为 空指针 必报错 而野指针可能会在合法地址内也可能不在 所以 就很难排错

int  *p  = NULL;      //空指针: 是NULL  不是0  0也有地址 而NULL 没有
int  *p;     //因为没有初始化所有初始化随机  所以就不知道指向哪里了  就是野指针

指针的数据类型

int *p;
char *p;

口诀:

将指针变量名的变量名去除,剩余的就是指针变量的数据类型。

int *p  去掉变量名p   那就是 int *

指针存储的数据类型

int *p;
char *p;

口诀:

将指针变量的  变量名与最近的一个* 去除,剩余的就是指针变量存储地址对应的数据类型


int *p;
去掉 *p
就是int

作用:

分析等号两边数据类型一致

二级指针

概念:

又名 指针的指针

因为指针变量本质也是一个变量,在32位机中占4字节,64位机占8字节

如:

int a = 10;  //假设此时a在栈内存中的地址编号为0x00 00 00 01,长度4字节
int *p = &a; //将a的地址赋值给变量p,此时p存储的地址为a的地址即0x00 00 00 01 p本身也需要占用内存,其地址假设为 0x 00 00 00 06
int **p = &p; // 将p的地址 0x 00 00 00 06赋值给p2
1,二级指针使用  *取出的是什么?
     取出的是存储的一级指针本身的地址,不是一级指针存储的地址
     int num01=10;
	int num02=1;
	int *pl = &num01;
	int *p2 = &num02;
     *pl = 100;  //通过 * 但核心还是地址来改变值
	printf("num01 = %d\n",num01);    // 100  因为使用取值与改值 *将指针p存储的数据10进行了改值
	printf("num02 = %d\n",num02);    //  1


   int **p3 =&p1;   //将p1本身的地址赋值给p3 此时p3存储的是p1本身的地址
	 *p3 = p2;        // 将 p2 的地址赋值给 p3
   printf("**p3  = %d\n",**p3);  // 1    **p  是* *p3 也就是取*p3地址存的值 还是num02

void与指针

void的作用:

万能指针的作用 核心作用就是作为函数的形参。

1 void 作为函数的返回值类型,表示没有返回值或返回值为NULL2 void 与指针结合称为万能指针 ,可有存储任何一种类型变量的地址。
			但是不能直接对 void  *p 中的p 直接取值 因为 无法确定p的取值宽度 。

int num = 10;
int *p1 = #
char c = 'a';
char *p2 = &c;
void *p3 = #
void *p4 = &c;
p3 = &c;		//修改p3存储的地址

万能指针的作用:

  • 核心作用就是作为函数的形参。

万能指针的注意事项:

  • 不能直接对 void *p 中的p 直接取值 因为 无法确定p的取值宽度 。
    在这里插入图片描述

const与指针

常量指针

可以修改其存储的地址 ,无法修改

常量指针 是指针 可以修改其存储的地址,但是无法修改其存储的值。

语法:

数据类型 const *指针名称;
或者
const  数据类型 *指针名称;

示例:

#include <stdio.h>
int main(int argc, char const *argv[])
{
	int a = 10;
	int b = 1;
	//常量指针,本质是指针,可以修改其存储的地址,但是无法修改其存储的地址对应的值
	//const int *p = &a;  两种写法   写法a
	int const *p = &a;  //写法b
	p = &b;//改变p存储的地址
	//*p = 100;//改变p存储的地址对应的值,此处报错
	return 0;
}

在这里插入图片描述

指针常量

可以修改其存储的 ,无法修改存储地址

指针常量是常量 该指针存储的地址无法被修改,但是可以修改其存储的地址对应的值。

语法:

数据类型 * const 指针名称;
 int *  const p;    

在这里插入图片描述

常量指针常量

无法修改其存储的 ,也无法修改存储地址

概念: 常量指针指向常量

语法:

const 数据类型 * const 指针名;
或
数据类型 const * const 指针名;

示例

#include <stdio.h>
int main(int argc, char const *argv[])
{
	int a = 10;
	int b = 1;
	//常量指针常量
	//const int * const p = &a;
	int const * const p = &a;
	p = &b;//不可以改变p存储的地址
	*p = 100;//也不可以改变p存储的地址对应的值
	printf("a = %d\n",a);
	return 0;
}

在这里插入图片描述

数组与指针

数组名的本质

数组名 就是 数组中第一个元素的首地址

printf("arr的地址:  %p\n",arr);   // 求数组名地址
printf("arr[0]地址:%p\n",arr[0]);  //求地址首地址
                                                                       // 发现结果一样

数组名 就是 数组中第一个元素的首地址

在这里插入图片描述

#include <stdio.h>
int main(int argc, char const *argv[])
{
    // 证明数组名就是数组中第一个元素的地址
    int arr[] = {1, 3, 5, 7, 10};
    printf("arr的地址:  %p\n", arr);
    // 使用  (void *) 强制转换为 十六进制地址数
    printf("arr[0]地址:%p\n", (void *)&arr[0]);

    // 一维数组怎么操作  二维完全可以复刻   先定义指针赋值
    // 指针可以通过下标访问数组元素,这与数组名的使用方式相同:
    // 定义一个指向int的指针p,初始化为数组arr的首地址
    int *p = arr;
    printf("arr[1] = %d\n", arr[1]);   // 3  下标获取数组下标为1的元素
    printf("p[1]   = %d\n", p[1]);     // 3  指针访问数组下标为1的元素
    printf("*(p + 1)=%d\n", *(p + 3)); // 7   *(p+1) 等价于 p[1]
    printf("*P+1= %d\n", *p + 3);      //   4    这里是对*p  也就是数字取值后加1  不是位移单位
    printf("p[0]= %d\n", p[0]);        // 1  p[0] 等价*p

    printf("数组arr的存储首地址为   %p\n", &arr);  //   0x7ffd49bad8b0
    printf("p的地址为:             %p\n", p);     //   0x7ffd49bad8b0
    printf("p[0]的地址为:          %p\n", &p[0]); //   0x7ffd49bad8b0
    printf(" p+1的地址为:          %p\n", p + 1); //      0x7ffd49bad8b4
    // 这里的+1是加步长 步长取决数组数据类型的大小  比如是int 那就是加一步长  而此时的一步长就是4字节
    printf("*p+1=%d\n", *p + 1); //     2   核心思想  *   取值符  就是将此地址存储的数值拿出来
    // *p 就是将p存储的值拿出来了  也就是1  再后移一位 所以是2
    printf("*(p+1)=%d\n", *(p + 1)); //  3
    // 将当前位置+1个步长,取的是数组下标为1的元素
    return 0;
}

在这里插入图片描述

事例2:

#include <stdio.h>
int main(int argc, char const *argv[])
{
    char str[] = "hIc";
    char *p = str;
    // 此时数组中存储的元素为char,一个char的长度为1字节,顾此时1个步长=1字节
    printf("%c\n", *(p + 1));
    printf("%c\n", *(p + 2));
    printf("%c\n", *(p + 3));
    printf("%c\n", *p + 1); //*p获取的是其地址对应的值,其地址的值为h,h+1=i
    printf("%c\n", (*p) + 1);
    return 0;
}

指针数组与数组指针

概述

数组指针(数组的指针):指向数组,本质是指针
指针数组(存储指针的数组):本质是一个数组

示例1

#include <stdio.h>
int main(int argc, char const *argv[])
{
    int nums[] = {1, 2, 3};
    // 数组指针(数组的指针,指向数组,本质是指针)
    int *p = nums;
    int a = 1;
    int b = 2;
    int c = 3;
    int *p11 = &a;
    int *p12 = &b;
    int *p13 = &c;
    // 指针数组(存储指针的数组,本质是一个数组)
    int *p2[] = {p11, p12, p13};
    return 0;
}

示例2

#include <stdio.h>
int main(int argc, char const *argv[])
{
     int nums01[] = {1, 2, 3, 4, 5};
     int nums02[] = {11, 22, 33, 44, 55};
     int nums03[] = {111, 222, 333, 444, 555};
       // 二维数组
     int nums04[][5] = {
           {1, 2, 3, 4, 5},
           {11, 22, 33, 44, 55},
           {111, 222, 333, 444, 555}};
     // 指针数组
       int *ps[] = {nums01, nums02, nums03};
     printf("nums04[0][1] = %d\n", nums04[0][1]);
     printf("ps[0][1] = %d\n", ps[0][1]);
     printf("nums04[1][1]的=%d\n", nums04[1][1]);
     printf("(*(ps+1))[1]=%d\n", (*(ps + 1))[1]);
     printf("*(*(ps+1)+1)=%d\n", *(*(ps + 1) + 1));
     return 0;
}

示例3

#include <stdio.h>
int main(int argc, char const *argv[])
{
     // char strs[][50] = {"GaoLei","WangChenHui","YueZheng"};
     char *strs[] = {"GaoLei", "WangChenHui", "YueZheng"};
     printf("strs[0] = %s\n", strs[0]);       // GaoLei
     printf("strs[1] = %s\n", strs[1]);       // WangChenHui
     printf("strs[2] = %s\n", strs[2]);       // YueZheng
     printf("*(strs+0) = %s\n", *(strs + 0)); //
     printf("*(strs+1) = %s\n", *(strs + 1));
     printf("*(strs+2) = %s\n", *(strs + 2));
     printf("(*(strs + 0))+3 = %s\n", (*(strs + 0)) + 3);
     printf("(*(strs + 0))[3] = %c\n", (*(strs + 0))[3]);
     return 0;
}

示例4

#include <stdio.h>
int main(int argc, char const *argv[])
{
    // char strs[][50] = {"GaoLei","WangChenHui","YueZheng"};
    char *strs[] = {"GaoLei", "WangChenHui", "YueZheng"};
    printf("strs[0]的地址=%p\n", &strs[0]);
    printf("strs的地址=%p\n", strs);
    printf("&strs的地址=%p\n", &strs);
    char *p;
    p = strs;
    // char **p2 = &p;
    // char **p2 = &strs;
    char **p2 = strs;
    printf("*p2 = %s\n", *p2);
    printf("**p2 = %c\n", **p2);
    printf("*(p2+1) = %s\n", *(p2 + 1));
    printf("*(p2+2) = %s\n", *(p2 + 2));
    printf("(*(p2+1))+4 = %s\n", (*(p2 + 1)) + 4);
    return 0;
}
#include <stdio.h>
int main(int argc, char const *argv[])
{
      char arr[] = "xxz";
      char *p = arr;
      // ASCII码值 是 97-122  也就是  小写   a-z
      //  大写字母的 ASCII 码值范围是 65 到 90
      printf("*p+1== %c\n", *p + 1); //*p得到的是 p[0] 也就是 x的ASCII 码值 120  然后加一
      return 0;
}

步长:

通常指的是数组中元素的内存间隔。

步长 = 数组中存储的元素的数据类型所占的字节数 当为int 那加1 就是加一步长也就是加4 当为char 一步长就是1 加1 就是加1

函数与指针

函数名的本质

函数名本质上就是函数在代码区存储的首地址

指针作为形参

函数指针

作用:记录函数的地址
		语法:
		返回值类型 (*指针名称)(指向的函数的形参列表的数据类型) = 函数名;
			注意:
				形参列表的数据类型可有可无
函数指针调用函数
			指针名 (实参列表);
			变量名 = 指针名(实参列表);

事例:

#include <stdio.h>
extern void add(int a, int b);
extern void sub(int a, int b);
extern void test(void (*p)(int, int));
int main(int argc, char const *argv[])
{
    printf("main函数的地址=%p\n", main);
    int (*p)(int, char *[]) = main;
    // 返回值类型 (*指针名称)(指向的函数的形参列表的数据类型) = 函数名;
    void (*p2)(int, int) = add;
    // 函数指针调用函数
    // 指针名(实参列表);
    // 变量名 = 指针名(实参列表);
    //  p2 = sub;
    p2(1, 2);
    test(p2);
    return 0;
}
void test(void (*p)(int, int))
{
    p(10, 12);
}
void add(int a, int b)
{
     printf("%d+%d=%d\n", a, b, a + b);
}
void sub(int a, int b)
{
    printf("%d-%d=%d\n", a, b, a - b);
}

指针作为形参

#include <stdio.h>
extern void showArray(int nums[],int len);
int main(int argc, char const *argv[])
{
	int nums[] = {1,2,3,4,5};
	showArray(nums,5);
	return 0;
}
void showArray(int *nums,int len)
{
	for (int i = 0; i < len; i++)
		{
		// printf("%d\t",nums[i]);
		printf("%d\t",*(nums+i));
		}
			printf("\n");
}

指针作为返回值

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// extern int* getNums();
extern void getNums(int nums[],int len);
int main(int argc, char const *argv[])
{
	srand(time(NULL));
	// int *p = getNums();
	// for (int i = 0; i < 10; i++)
	// {
	// printf("%d\t",p[i]);
	// }
	// printf("\n");
	int nums[10] = {0};
	getNums(nums,10);
for (int i = 0; i < 10; i++)
{
	printf("%d\t",nums[i]);
}
	printf("\n");
 return 0;
}
// int* getNums()
// {
// static int nums[10] = {0};
// for (int i = 0; i < 10; i++)
// {
// nums[i] = rand() % 100;
// }
// return nums;
// }
void getNums(int nums[],int len)
{
for (int i = 0; i < len; i++)
	{
	nums[i] = rand() % 100;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值