跑哥教你掌握C语言指针最根本的问题

简介: 指针指针变量用来记录地址数据。指针变量的用途就是用来找另外一个变量的,只有记录了有效地址的指针才能使用。

指针


指针变量用来记录地址数据。

指针变量的用途就是用来找另外一个变量的,只有记录了有效地址的指针才能使用。

既然指针是变量,那么就应该跟变量一样使用前需要声明变量,指针变量的演示:


#include <stdio.h>
int main(){
    int num = 0;
    int *p_num = NULL;//指针变量的声明
    p_num = &num;//将num变量的地址赋值给指针变量
    num = 2;
    printf("num = %d\n", num);
    printf("*p_num = %d\n", *p_num);
    printf("p_num = %p\n", p_num);
    return 0;
}

20180731233559873.png

只要在指针变量名称前使用 * 操作符就可以表示捆绑存储区。

指针也分类型,不同类型的指针适合与不同存储区捆绑,也就是不同类型的指针存放的变量地址的变量类型应该一致。可以在一条语句里声明多个同类型的指针,必须在每个指针变量名称前单独加 * 号。

所有没指向地址的指针分为两类:

1、空指针记录固定地址(空地址,用NULL表示,这个地址的数值就是0);

2、除了空指针以外没有指向或者捆绑过的指针都叫野指针。

程序中禁止出现野指针,所有指针变量必须初始化

可以在声明指针变量的时候同时初始化的时候,在初始化的时候 * 没有参与赋值。

指针练习:


#include <stdio.h>
int main(){
    int num = 0, num1 = 0, num2 = 0, tmp = 0;
    int *p_num = &num, *p_num1 = &num1, *p_num2 = &num2, *p_tmp = &tmp;
    printf("请输入三个数字:");
    scanf("%d%d%d", &num, &num1, &num2);
    if(num > num1)
    {
        tmp = num;
        num = num1;
        num1 = tmp;
    }
    if(*p_num > *p_num2)
    {
        *p_tmp = *p_num;
        *p_num = *p_num2;
        *p_num2 = *p_tmp;
    }
    if(*p_num1 > *p_num2)
    {
        *p_tmp = *p_num1;
        *p_num1 = *p_num2;
        *p_num2 = *p_tmp;
    }
    printf("%d %d %d\n", num, num1, num2);
    return 0;
}

20180731233610369.png

可以改变上述代码:


#include <stdio.h>
int main(){
    int num = 0, num1 = 0, num2 = 0, tmp = 0;
    int *p_num = &num, *p_num1 = &num1, *p_num2 = &num2, *p_tmp = &tmp;
    printf("请输入三个数字:");
    scanf("%d%d%d", &num, &num1, &num2);
/*  
    if(num > num1)
    {
        tmp = num;
        num = num1;
        num1 = tmp;
    }
    if(*p_num > *p_num2)
    {
        *p_tmp = *p_num;
        *p_num = *p_num2;
        *p_num2 = *p_tmp;
    }
    if(*p_num1 > *p_num2)
    {
        *p_tmp = *p_num1;
        *p_num1 = *p_num2;
        *p_num2 = *p_tmp;
    }
    */
    if(*p_num > *p_num1){
        p_tmp = p_num;
        p_num = p_num1;
        p_num1 = p_tmp;
    }
    if(*p_num > *p_num2){
        p_tmp = p_num;
        p_num = p_num2;
        p_num2 = p_tmp;
    }
    if(*p_num1 > *p_num2){
        p_tmp = p_num1;
        p_num1 = p_num2;
        p_num2 = p_tmp;
    }
    printf("%d %d %d\n", *p_num, *p_num1, *p_num2);
    return 0;
}

20180731233621544.png

程序执行过程中指针和存储区的捆绑关系可以随时改变。可以把指针看做是存储区的某种身份。


如果用指针记录数组里的第一个存储区的地址就可以用这个指针表示数组里的每个存储区。这个时候就可以认为指针间接捆绑了数组里的所以存储区。在指针后使用下标就可以用来表示数组里的某个存储区。

例如:

#include <stdio.h>
int main(){
    int arr[5] = {1, 2, 3, 4, 5};
    int num = 0;
    int *p_num = arr;
    for(num = 0; num <= 4; num++)
    {
        printf("%d ", arr[num]);
        printf("%d ", p_num[num]);
    }
    printf("\n");
    return 0;
}

20180731233653544.png

地址数据可以参与如下计算:

地址 + 整数、 地址 - 整数、 地址 - 地址

地址加减整数 n 实际上加减的是 n 个捆绑存储区的大小。

例如:


#include <stdio.h>
int main(){
    int arr[5] = {1, 2, 3, 4, 5};
    printf("arr = %p, arr+3 = %p\n", arr, arr+3);
    printf("arr = %p, arr-3 = %p\n", arr, arr-3);   
    printf("&arr[3] - &arr[1] = %d\n", &arr[3] - &arr[1]);
    return 0;
}

20180731233706594.png

地址 - 地址的结果是两个地址间包含的捆绑存储区个数。

所以数组名称加下标可以得到下标对应的存储区的地址。

数组名称不可以被赋值,但是指针可以被赋值

对数组名称做sizeof得到结果是整个数组的字节个数,而对指针做sizeof计算得到的只是这个指针变量所占字节个数

例如:


#include <stdio.h>
int main(){
    int arr[5] = {1, 2, 3, 4, 5};
    int *p_num = NULL;
    printf("sizeof(arr) = %d\n", sizeof(arr));
    printf("sizeof(p_num) = %d\n", sizeof(p_num));
    printf("&arr = %p\n", &arr);
    printf("&p_num = %p\n", &p_num);
    return 0;
}

20180731233716957.png

另外,对数组名称和指针做取地址操作结果也不同。

对一维数组名称取地址是可以当做二维数组进行使用的。


所以跨函数使用的存储区都是通过指针进行实现的。数组做形式参数的时候真正的形式参数就是一个指针。

函数可以把一个存储区的地址作为返回值传递给调用函数,这个时候需要提供一个指针类型存储区记录这个作为返回值的地址,调用函数可以通过这个返回值使用被调用函数提供的存储区。

例如:

#include <stdio.h>
int *read(void){
    static int num = 0;
    printf("请输入一个数字:");
    scanf("%d", &num);
    return &num;
}
int main(){
    int *p_num = NULL;
    p_num = read();
    printf("%d\n", *p_num);
    return 0;
}

20180731233727292.png

不可以把非静态局部变量的地址作为返回值使用。

相关文章
|
2月前
|
存储 编译器 C语言
【C语言】【指针1】指针难?看这个就够了!
【C语言】【指针1】指针难?看这个就够了!
|
21天前
|
存储 C语言
【C语言基础】一篇文章搞懂指针的基本使用
本文介绍了指针的概念及其在编程中的应用。指针本质上是内存地址,通过指针变量存储并间接访问内存中的值。定义指针变量的基本格式为 `基类型 *指针变量名`。取地址操作符`&`用于获取变量地址,取值操作符`*`用于获取地址对应的数据。指针的应用场景包括传递变量地址以实现在函数间修改值,以及通过对指针进行偏移来访问数组元素等。此外,还介绍了如何使用`malloc`动态申请堆内存,并需手动释放。
|
24天前
|
存储 人工智能 C语言
C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
本文详细讲解了C语言中的指针,包括指针变量的定义与引用、指向数组及字符串的指针变量等。首先介绍了指针变量的基本概念和定义格式,随后通过多个示例展示了如何使用指针变量来操作普通变量、数组和字符串。文章还深入探讨了指向函数的指针变量以及指针数组的概念,并解释了空指针的意义和使用场景。通过丰富的代码示例和图形化展示,帮助读者更好地理解和掌握C语言中的指针知识。
|
2月前
|
C语言
C语言------指针
这篇文章是关于C语言中指针的实训,通过示例代码展示了指针的基本概念、定义、赋值、使用和传递,以及指针运算和指针在函数参数中的应用,如交换两个变量的值和找出两个数中的较小值。
C语言------指针
|
1月前
|
存储 安全 C语言
C语言 二级指针应用场景
本文介绍了二级指针在 C 语言中的应用,
|
2月前
|
存储 编译器 C语言
【C语言篇】深入理解指针2
代码 const char* pstr = "hello world."; 特别容易让初学者以为是把字符串 hello world.放 到字符指针 pstr ⾥了,但是本质是把字符串 hello world. 首字符的地址放到了pstr中。
|
2月前
|
存储 程序员 编译器
【C语言篇】深入理解指针1
assert.h 头⽂件定义了宏 assert() ,⽤于在运⾏时确保程序符合指定条件,如果不符合,就报错终⽌运⾏。这个宏常常被称为“断⾔”。
|
2月前
|
存储 搜索推荐 C语言
C语言中的指针函数:深入探索与应用
C语言中的指针函数:深入探索与应用
|
2月前
|
存储 编译器 C语言
【C语言】指针练习题目
【C语言】指针练习题目
|
2月前
|
C语言 Python
C语言指针(2)
C语言指针(2)
25 5