C语言中的初阶指针详解

目录
  • 1.指针是什么
  • 2.指针和指针类型
  • 3.野指针
    • 3.1野指针成因
    • 3.2如何规避野指针
  • 4.指针的运算
    • 4.1指针±整数
    • 4.2指针-指针
    • 4.3指针的关系运算
  • 5.指针和数组
  • 6.二级指针
  • 7.指针数组
  • ​ 总结

1.指针是什么

​ 初学者都有一个疑问,那就是指针是什么?简单的说,就是通过它能找到以它为地址的内存单元。

地址指向了一个确定的内存空间,所以地址形象的被称为指针。

int main()
{
	int a = 10;
	int* pa = &a;
    return 0;
}
//pa是用来存放地址(指针),所以pa是指针变量。

总结:指针就是变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)。

​ 地址是唯一标识一块空间的。

​ 指针的大小在32位平台是4个字节,在64位平台是8个字节。

2.指针和指针类型

​ 我们知道变量有不同的类型(整型、浮点型、字符型等),其实指针也是有不同类型的。

​ 指针类型的意义1:
指针类型决定了指针解引用操作的时候,一次访问几个字节(访问内存的大小)
char* 指针解引用访问1个字节
int* 指针解引用访问四个字节

int main()
{
	char* pc = &a;
	*pc = 0;
	return 0;
}

​ 指针类型的意义2:

指针类型决定了,指针±整数的时候的步长(指针±整数的时候,跳过几个字节)

int* 指针+1 跳过四个字节

char* 指针+1 跳过一个字节

int main()
{
	int a = 10;
	int* pa = &a;
	char* pc = &a;
	printf("%p\n", pa);
	printf("%p\n", pc);
	printf("%p\n", pa+1);
	printf("%p\n", pc+1);
	return 0;
}

3.野指针

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

3.1野指针成因

1.指针未初始化

int main()
{
    int* p;//局部变量指针未初始化,默认为随机值
    *p = 20;//通过p中存的随机值作为地址,找到一个空间,这个空间不属于我们当前的程序
    //就造成了非法访问,p就是野指针
    return 0;
}

2.指针越界访问

int main()
{
    int arr[10] = 0;
    int i = 0;
    int* p = arr;
    for(i =0; i <= 10; i++)
    {
        *p = i;
        p++;//当指针指向的范围超出数组arr的范围时,p就是野指针
    }
    return 0;
}

3.指针指向的空间释放

int* test()
{
    int a = 10;
    return &a;
}
int main()
{
    int* p = test();
    printf("%d\n",*p);
    return 0;
}

3.2如何规避野指针

1.指针初始化

2.小心指针越界

3.指针指向空间释放即使置NULL

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

5.指针使用之前检查有效性

int main()
{
    int a = 10;
    int* p = &a;//明确地初始化,确定指向
    int* p2 = NULL;//不知道一个指针当前应该指向哪里时,可以初始化为NULL
    return 0;
}

4.指针的运算

4.1指针±整数

#define N_VALUES 5
float values[N_VALUES];
float* vp;
for(vp = &values[0]; vp < &values[N_VALUES];)
{
    *vp++ = 0;
}
int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,0};
    int* p = &arr[9];
    printf("%p\n",p);
    printf("%p\n",p-2);
    return 0;
}

4.2指针-指针

​ 指针-指针 得到的数字的绝对值是指针和指针之间元素的个数

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	printf("%d\n", &arr[9] - &arr[0]);
	printf("%d\n", &arr[0] - &arr[9]);
	return 0;
}

指针-指针 的前提是两个指针指向同一块区域

int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,0};
    char ch[5] = {0};
    printf("%d\n",&arr[9] - &ch[0]);//err
    return 0;
}

应用 求字符串长度

int my_strlen(char* s)
{
	int count = 0;
	char* start = s;
	while(*s!='\0')
	{
		s++;
	}
	return s - start;
}
int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

4.3指针的关系运算

#define N_VALUES 5
float values[N_VALUES];
float *vp;
for(vp = &values[N_VALUES]; vp > &values[0];)
{
    *--vp = 0;
}

上述程序也可以写成这样

for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
    *vp = 0;
}

实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这么写,因为标准并不保证它可行。

标准规定

​ 允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

5.指针和数组

数组 - 是一块连续的空间,放的是相同类型的元素

数组大小和元素类型,元素个数有关系

指针(变量) - 是一个变量,放地址

指针变量的大小 是4(32bit)/8(64bit)个byte

数组名确实是首元素地址

但是有两个例外:

1.sizeof(数组名) - 这里的数组名不是首元素的地址,是表示整个数组的,这里计算的是整个数组的大小,单位还是字节。

2.&数组名 - 这里的数组名不是首元素的地址,是表示整个数组的,拿到的是整个数组的地址

int main()
{
	int arr[10] = { 0 };
	int sz = sizeof(arr);
	printf("%d\n", sz);
	return 0;
}
int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0;i < sz;i++)
	{
		*(p + i) = i;
	}
	for (i = 0;i < sz;i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

6.二级指针

​ 我们都知道,指针变量是变量,是变量就有地址,那么指针变量的地址存放在哪里呢?

这就是我们要了解的二级指针。

int main()
{
    int a = 10;
    int* p = &a;
    int** pp = &p;//pp就是二级指针
    **pp = 20;
    printf("%d\n", a);//a = 20
    return 0;
}

7.指针数组

​ 从名字上来看,大家觉得指针数组是指针还是数组?

答案是数组,是存放指针的数组。

整型数组 - 存放整型的数组就是整型数组

字符数组 - 存放字符的数组就是字符数组

指针数组 - 存放指针的数组就是指针数组

int* 整型指针的数组

char* 字符指针的数组

int main()
{
	int arr[10];
	char ch[5];
	int* parr[5];
	char* pc[6];
	return 0;
}
int main()
{
	int a = 10;
	int b = 20;
	int c = 30;
	int* parr[3] = { &a,&b,&c };
	for (int i = 0;i < 3;i++)
	{
		printf("%d\n", *(parr[i]));
	}
	return 0;
}

​ 总结

以上就是我们初始C语言指针的全部内容了,后续我还会更新C语言指针的进阶版本,希望大家能够对C语言的指针能够有更深层次的了解。

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • C语言进阶:指针的进阶(3)

    目录 数组传参和指针传参 一维数组传参 二维数组传参 一级指针传参 二级指针传参 总结 数组传参和指针传参 实践之中不免会碰到数组和指针作函数参数而如何设计形参的问题. 一维数组传参 一维数组传参,下列接收方式是否可行呢? //1. void test(int arr[]) {} //2. void test(int arr[10]) {} //3. void test(int* arr) {} int main() { int arr[10] = { 0 }; test(arr); retur

  • C语言进阶:指针的进阶(1)

    目录 指针进阶 字符指针 字符指针的作用 字符指针的特点 指针数组 指针数组的定义 指针数组的使用 总结 指针进阶 我们在初阶时就已经接触过指针,了解了指针的相关内容,有: 指针定义:指针变量,用于存放地址.地址唯一对应一块内存空间. 指针大小:固定32位平台下占4个字节,64位8个字节. 指针类型:类型决定指针±整数的步长及指针解引用时访问的大小. 指针运算:指针解引用,指针±整数,指针-指针,指针关系运算. 本章节在此基础上,对C语言阶段指针进行更深层次的研究. 字符指针 字符指针,存入字符

  • C语言进阶:指针的进阶(5)

    目录 函数指针数组 函数指针数组的定义 函数指针数组的使用 转移表 回调函数 指向函数指针数组的指针 总结 函数指针数组 //整型数组 - 存放整型变量 int arr[10]; //字符数组 - 存放字符变量 char ch[5]; //指针数组 - 存放指针变量 int* arr[10]; //函数指针数组 - 存放函数指针 int(*pfar[10])(int, int); 指针数组存放指针变量,函数指针数组存放函数指针,故元素类型为函数指针类型. 函数指针数组的定义 int Add(in

  • C语言进阶:指针的进阶(4)

    目录 函数指针 函数指针的定义 函数指针的类型 函数指针的使用 Example 总结 函数指针 函数指针的定义 整型指针存放整型的地址:数组指针存放数组的地址:那么类比可得,函数指针存放函数的地址. 显然,函数指针指向函数,存放函数的地址.搞懂函数指针,先了解函数的地址. &函数名或函数名代表函数地址,与&数组名和数组名略有不同,&函数名和函数名完全一致. 函数的地址必然要放到函数指针里,函数指针的类型该如何写呢?(以Add函数为例) //整型指针 int* pa = &a

  • C语言进阶:指针的进阶(2)

    目录 数组指针 数组指针的定义 &数组名和数组名 数组指针的使用 反面用例 正面用例 Example 类型辨别方法 总结 数组指针 由前面的例子,不难得出,数组指针是指向数组的指针,是指针而非数组. 数组指针的定义 char ch = 'w'; char* pch = &ch;//字符地址存放在字符指针中 int a = 10; int* pint = &a;//整型地址存放在整型指针中 float f = 0.0; float* pf = &f;//浮点型地址存放在浮点型

  • C语言进阶学习之指针

    目录 1.指针概念回顾 2.字符指针 3.数组指针和指针数组 3.1数组指针的含义 3.2&数组名vs数组名 3.3数组指针 4.数组传参和指针传参 4.1一维数组传参 4.2二维数组传参 4.3一级指针传参 4.4二级指针传参 5.函数指针 6.函数指针数组 7.指向函数指针数组的指针 8.回调函数 总结 1.指针概念回顾 指针的基本概念: 指针是一个变量,用来存放地址,地址唯一标识一块内存空间.指针的大小是固定的4/8个字节(32位平台/64位平台).指针是有类型,指针的类型决定了指针的±整

  • C语言中的指针新手初阶指南

    目录 1.指针是什么 2.指针和指针类型 3.野指针 3.1野指针成因 3.2如何规避野指针 4.指针的运算 4.1指针±整数 4.2指针-指针 4.3指针的关系运算 5.指针和数组 6.二级指针 7.指针数组 总结 1.指针是什么 ​ 初学者都有一个疑问,那就是指针是什么?简单的说,就是通过它能找到以它为地址的内存单元. 地址指向了一个确定的内存空间,所以地址形象的被称为指针. int main() { int a = 10; int* pa = &a; return 0; } //pa是用来

  • C语言中的初阶指针详解

    目录 1.指针是什么 2.指针和指针类型 3.野指针 3.1野指针成因 3.2如何规避野指针 4.指针的运算 4.1指针±整数 4.2指针-指针 4.3指针的关系运算 5.指针和数组 6.二级指针 7.指针数组 ​ 总结 1.指针是什么 ​ 初学者都有一个疑问,那就是指针是什么?简单的说,就是通过它能找到以它为地址的内存单元. 地址指向了一个确定的内存空间,所以地址形象的被称为指针. int main() { int a = 10; int* pa = &a; return 0; } //pa是

  • C语言中联合体union的实例详解

     C语言中联合体union的实例详解 1.定义: union(int i, short s, char c) un; un.i = 3; printf("i=%d",un.i); printf("length = %d\n",sizeof(un);//==4,有最大的变量来决定 2.相当与java里的List T类型 3.数据交换 void swap(int *p , int *q){ int temp = *p; *p = *q; *q = temp; } 4.打

  • C语言中调用Swift函数实例详解

    C语言中调用Swift函数实例详解 在Apple官方的<Using Swift with Cocoa and Objectgive-C>一书中详细地介绍了如何在Objective-C中使用Swift的类以及如何在Swift中使用Objective-C中的类.在后半部分也介绍了如何在Swift中使用C函数,不过对于如何在C语言中使用Swift函数却只字未提.这里我就为大家分享一下如何在C语言中调用Swift函数. 我们首先要知道的是,所有Swift函数都属于闭包.其次,Swift函数的调用约定与

  • C语言中强制地址跳转详解

    C语言中强制地址跳转详解 #define jump(TargetAddr ) (*((void(*)())(TargetAddr))() 第一个(( void( * )(  )) ,意思为强制类型转换为一个无形参,无返回值的函数指针,(*(TargetAddr))为跳转地址,但是函数指针变量不能为常数所以要加((void( * )(  )) 进行强制类型转换.最后一个()为执行的意思. 整一条指定的目的是为了跳转到一个绝对地址执行函数. 1.在单片机中可以实现软件复位,比如跳转到0地址. 2.如

  • Java语言中的内存泄露代码详解

    Java的一个重要特性就是通过垃圾收集器(GC)自动管理内存的回收,而不需要程序员自己来释放内存.理论上Java中所有不会再被利用的对象所占用的内存,都可以被GC回收,但是Java也存在内存泄露,但它的表现与C++不同. JAVA中的内存管理 要了解Java中的内存泄露,首先就得知道Java中的内存是如何管理的. 在Java程序中,我们通常使用new为对象分配内存,而这些内存空间都在堆(Heap)上. 下面看一个示例: public class Simple { public static vo

  • C语言中的正则表达式使用示例详解

    正则表达式,又称正规表示法.常规表示法(英语:Regular Expression,在代码中常简写为regex.regexp或RE).正则表达式是使用单个字符串来描述.匹配一系列符合某个句法规则的字符串. 在c语言中,用regcomp.regexec.regfree 和regerror处理正则表达式.处理正则表达式分三步: 编译正则表达式,regcomp: 匹配正则表达式,regexec: 释放正则表达式,regfree. 函数原型 /* 函数说明:Regcomp将正则表达式字符串regex编译

  • Go语言中的数据竞争模式详解

    目录 前言 Go在goroutine中通过引用来透明地捕获自由变量 切片会产生难以诊断的数据竞争 并发访问Go内置的.不安全的线程映射会导致频繁的数据竞争 Go开发人员常在pass-by-value时犯错并导致non-trivial的数据竞争 消息传递(通道)和共享内存的混合使用使代码变得复杂且易受数据竞争的影响 Add和Done方法的错误放置会导致数据竞争 并发运行测试会导致产品或测试代码中的数据竞争 小结 前言 本文主要基于在Uber的Go monorepo中发现的各种数据竞争模式,分析了其

  • Kotlin 语言中调用 JavaScript 方法实例详解

    Kotlin 语言中调用 JavaScript 方法实例详解 Kotlin 已被设计为能够与 Java 平台轻松互操作.它将 Java 类视为 Kotlin 类,并且 Java 也将 Kotlin 类视为 Java 类.但是,JavaScript 是一种动态类型语言,这意味着它不会在编译期检查类型.你可以通过动态类型在 Kotlin 中自由地与 JavaScript 交流,但是如果你想要 Kotlin 类型系统的全部威力 ,你可以为 JavaScript 库创建 Kotlin 头文件. 内联 J

  • C语言中递归和排列组合详解

    目录 排列组合三大问题: 1.打印n个数的全排列 2.打印n个数中任意m个数的全排列 3.打印n个数中任意m个数的组合 完整代码如下: 总结 排列组合三大问题: 1.打印n个数的全排列2.打印n个数中任意m个数的全排列3.打印n个数中任意m个数的组合 1.打印n个数的全排列 这个题实际上是可以直接用STL中的next_permutation()函数,代码如下: #include<bits/stdc++.h> using namespace std; int main(){ int data[4

  • C语言中结构体变量私有化详解

    背景介绍 操作系统 : CentOS7.3.1611_x64 gcc版本 :4.8.5 什么是结构体? 在C语言中,结构体(struct)指的是一种数据结构,是C语言中聚合数据类型(aggregate data type)的一类.结构体可以被声明为变量.指针或数组等,用以实现较复杂的数据结构.结构体同时也是一些元素的集合,这些元素称为结构体的成员(member),且这些成员可以为不同的类型,成员一般用名字访问. 问题描述 C语言结构体定义中的变量默认是公有(Public)属性,如果实现成员变量的

随机推荐