首页 > 其他分享 >C语言初阶6 -指针

C语言初阶6 -指针

时间:2024-09-03 15:53:06浏览次数:10  
标签:arr 初阶 return int C语言 地址 main 指针

指针是什么

在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向 points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以 说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元.

那我们可以这样理解:

内存:

C语言初阶6 -指针_数组

指针 :指针是个变量,存放内存单元的地址(编号)

#include <stdio.h>
int main()
{
int a = 10;//在内存中开辟一块空间
int* p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
  //将a的地址存放在p变量中,p就是一个之指针变量。
return 0;
}

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

思考以下的问题∶

(1)一个小的单元到底是多大 ? (1个字节)

(2)如何编址 ?

经过仔细的计算和权衡我们发现一个字节给一个对应的地址是比较合适的。

对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的是产生一个电信号正电 / 负电(1或者0)

那么32根地址线产生的地址就会是∶

00000000 00000000 00000000 00000000

0000000 00000000 00000000 00000001

…….

11111111 1111111111111111 11111111

这里就有2的32次方个地址。 每个地址标识一个字节,那我们就可以给(2 ^ 32Byte == 2 ^ 32 / 1024KB == 2 ^

32 / 1024 / 1024MB == 2 ^ 32 / 1024 / 1024 / 1024GB == 4GB)4G的空闲进行编址。

同样的方法,那64位机器,如果给64根地址线,那能编址多大空间,自己计算。

这里我们就明白 :

  • 在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。
  • 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。

总结 :

指针是用来存放地址的,地址是唯一标示一块地址空间的。

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

指针和指针类型

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
    printf("%d\n", sizeof(char*));
    printf("%d\n", sizeof(short*));
    printf("%d\n", sizeof(int*));
    printf("%d\n", sizeof(double*));
    return 0;
}

运行后发现不管什么类型的指针,大小都是4,这时候我们心里可能产生疑问,为什么大小都是4,还要区分不同类型的指针,

那这种区分是不是没有意义的?不同类型的指针是不是可以互用?

为了验证这些问题,请看以下代码及运行结果。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
    int a=0x11223344;
    int* pa=&a;
    char* pc=&a;    
    printf("%p\n",pa);
    printf("%p\n",pc);    //打印运行发现两地址一样
    return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
    int a=0x11223344;
    int* pa=&a;
    *pa=0;   //(运行状态下)在编译器(vscode2022)调试-窗口-内存中查看地址
    return 0;
}

C语言初阶6 -指针_数组名_02

C语言初阶6 -指针_数组_03

可以看到a的内容确实改变(全变成了0)了

现在我们将char * pc来重复上面的操作,看能否实现相应变更指针指向地址内容的功能。

C语言初阶6 -指针_笔记_04

我们发现这里仅前两个变成了00,后面几位没有变化。

通过这个我们可以发现指针的类型还是意义的,其意义在于解引用操作时,对字节的操作数量不同

比如int * 类型的指针,可以改动4个字节的内容,而char * 类型的指针只能更改1个字节的内容

  1. 指针的意义1:指针的解引用(用于访问)

总结:指针类型决定了指针进行解引用操作的时候,能够访问空间的大小

  • int* p : p能够访问4个字节
  • char* p : p能够访问1个字节
  • double* p : p能够访问8个字节

   b. 指针的意义2:指针 + - 整数

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
    int a=0x11223344;
    int* pa=&a;
    char* pc=&a;
    printf("%p\n",pa);      // 00E6F89C
    printf("%p\n",pa+1);    // 00E6F8A0 = 00E6F89C + 4
    printf("%p\n",pc);      // 00E6F89C
    printf("%p\n",pc+1);    // 00E6F89D = 00E6F89C + 1
    return 0;
}

总结:指针的类型决定了指针向前或者向后走一步有多大(距离、指针的步长)。

  • int* p; p + 1 – > 往后跳4字节
  • char* p; p + 1 – >往后跳1字节
  • double* p; p + 1 – > 往后跳8字节

c. 指针两个意义的价值

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
    int arr[10] = { 0 };
    //char* p = arr;    //数据混乱
    int* p = arr;    
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        *(p + i) = i + 1;
    }
    return 0;
}

C语言初阶6 -指针_笔记_05

[1],[2]中的数并非我们想要

野指针

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

野指针成因

指针未初始化

#include<stdio.h>  
int main()    
{    
    int a;//局部变量不初始化,默认是随机值    
    int* p;//局部的指针变量不初始化,默认也会给随机值
    *p = 20;//这时候对指针进行解引用操作,实际上并不知道更改的内存是在哪里
    return 0;//十分危险
    }

指针越界访问

#include<stdio.h> 
int main()    
{    
    int arr[10] = { 0 };    
    int* p = arr;
    int i = 0;
    for (i = 0; i < 12; i++)
    {
    //当指针指向的范围超出数组arr的范围时,就越界访问了,此时p就是野指针
    *(p++)=1;
    } 
    return 0;
    }

指针指向的空间释放

#include<stdio.h>    
int* test()    
{    
    int a = 10;    
    return &a;
}
//1.a 创建的空间在函数结束的时候返回给系统了,不属于当前程序的内容

int main()
{
    int* p = test();
    //2.这时候通过*p 去访问一个返还系统的空间,该空间有可能已经存放其它内容了
    //3.所以p是一个野指针
    *p = 20;
    return 0;
}

那么我们如何能够避免野指针呢?

1.指针初始化

2.小心指针越界

3.指针指向空间释放,即置为(空指针) NULL

int* pa = NULL; //空指针

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

if (pa != NULL){    ...}

指针运算

  • 指针 + - 整数
  • 指针 - 指针
  • 指针的关系运算

指针 + - 整数

#include<stdio.h>
int main()
{    
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10};    
    int i = 0;
    int sz = sizeof(arr) / sizeof(arr[0]);
    int* p = arr;
    for (i = 0; i < sz; i++)
    {
        printf("%d  ", *p);
        //p = p + 1; //指针 + 整数 
        p++;
    }
    return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define a 5
int main()
{
    float values[a];
    float* vp;
    for (vp = &values; vp < &values[a];)
    {
        *vp++=0;
    }
    
    return 0;
}

指针 - 指针

没有指针 + 指针,因为没有意义,就像日期 - 日期 计算天数,而 日期 + 日期 毫无意义

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define a 5
int main()
{
    int arr[10]={1,2,3,4,5,6,7,8,9,10};
    printf("%d\n",&arr[9]-&arr[0]);        //结果为9
    
    char ch[5]={0};
    printf("%d\n",&arr[9]-&ch[0]);            //结果不确定,指针...要使他们都指向一个内存空间
    return 0;
}

#include<stdio.h>
int my_strlen(char* str)
{
        char* start = str;
        while (*str)
            str++;
        return str - start;
}
 
int main()
{
        char arr[] = "abc";
        int len = my_strlen(arr);
        printf("%d\n", len);
     
        return 0;
}

指针的关系运算

关系运算,简单来说就是比较大小,有 > 、 >= 、 == 、 != 、 < 、 <=

#define N_VALUES 5
float values[N_VALUES];
float* vp;
int main()
{
    for (vp = &values[N_VALUES]; vp > &values[0];)
    {
        *--vp = 0;//先--vp后解引用 
    }
    return 0;
}

代码简化, 这将代码修改如下:

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

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

标准规定∶

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

C语言初阶6 -指针_笔记_06

数组和指针

数组名是什么?

在之前的学习中,我们可能已经知道了数组名就是首元素的地址,这里我们验证一下这个说法是否正确,

请看下面这个例子:

C语言初阶6 -指针_笔记_07

但是,数组名就一直是首元素地址吗?

数组名在绝大多数情况下是首元素地址,但有两个例外:

1、& arr — &数组名 - 数组名不是首元素的地址,数组名表示整个数组 &数组名 , 取出的是整个数组的地址。

2、sizeof(arr)----sizeof(数组名)-- - 数组名表示的整个数组–sizeof(数组名)计算的是整个数组的大小。

C语言初阶6 -指针_笔记_08C语言初阶6 -指针_笔记_09

上面我们说了 数组名就是首元素的地址,那么我们通过使用指针来访问数组就成为了可能:

#include<stdio.h>
int main()
{    
    int arr[] = { 1,2,3,4,5,6,7,8,9,10 };    
    int* p = arr;    
    int i = 0;    
    int sz = sizeof(arr) / sizeof(arr[0]);    
    for (i = 0; i < sz; i++)
    {
    	printf("&arr[%d] = %p  <===> p+%d = %p\n", i, &arr[i], i, p + i);
    }
    return 0;
}

二级指针

指针变量也是变量,是变量就有存储空间,就有地址,那么指针变量的地址存放在哪里那?

这就是二级指针,指向内容是存放地址的指针。

通常我们所说的指向数组的指针是一级指针。

#include<stdio.h>
int main()
{    
  int a = 10;    
  int* pa = &a;//pa 一级指针    
  int** ppa = &pa;//ppa 二级指针 ......后面还有 三级指针、四级指针....n级指针    
  return 0;
}

这里我们进行画图演示:

C语言初阶6 -指针_数组_10

好了,现在我们对二级指针应该理解清楚了,但是二级指针怎么应用呢?

如果我们对 ppa 进行解引用操作 * ppa就 能找到 pa 也就是a的地址,再次进行解引用操作** ppa,就能找到a了

#include<stdio.h>
int main()
{    
  int a = 10;    
  int* pa = &a;//pa 一级指针    
  int** ppa = &pa;//ppa 二级指针 ......后面还有 三级指针、四级指针....n级指针         
  **ppa=20;        //对a的操作    
  return 0;
}

指针数组

在我们学习指针和数组的概念时,会接触到两个这样的概念

  • 指针数组-- - 数组-- - 存放指针的数组
  • 数组指针-- - 指针
#include<stdio.h>
int main()
{    
    //整形数组,存放整形的数组    
    int arr1[10];    
    //字符数组,存放字符的数组    
    char arr2[10];    
    
//指针数组,存放指针的数组    
    //存放整形指针的数组
    int* arr3[5];
    //存放字符指针的数组
    char* arr4[10];
    
//例:
    int a = 10, b = 20, c = 30, d = 40, e = 50;
    int* arr[5] = {&a,&b,&c,&d,&e};
    for (int i = 0;i < 5;i++)
    {
        printf("%d ", *(arr[i]));
    }
    return 0;
}

标签:arr,初阶,return,int,C语言,地址,main,指针
From: https://blog.51cto.com/u_16961806/11908868

相关文章

  • 【树莓派C语言开发】实验01:双色LED
    文章目录前言1.实验器材2.实验原理3.控制代码3.1显示器的PWM调光和DC调光3.2编译代码3.2.1直接使用Geany编译器内部选项3.2.2使用linux语言4.如何在使用面包板的同时使用散热小风扇结语前言今晚上没啥事(其实作业没写完),就开始折腾树莓派了我当初买的是树莓派4B(4GB)版本的套件,东......
  • 学习C语言之分支和循环(下)。都是练习,桀桀桀。
    <一>、闰年的判断 <二>、找出100~200内的素数 <三>、猜数字第一种:  第二种:  第三种:限定次数  ......
  • 图书管理系统【C语言课设】【附源码】
    图书管理系统这是一个符合大学生课设实训的系统。一、系统功能        1.登录            2.添加图书        3.查询图书    4.借阅图书        5.归还图书    6.图书热度排行        7.删除图书  ......
  • C语言 跟着Mr.狠人一起实现冒泡排序
    冒泡排序(bubblesort)基本原理很简单,如图所示: 这边方便大家快速观察顺序:这边我们可以观察出冒泡排序是两两相比,每一趟都能确定最后一位成为本趟的最大值。 10个数字9趟就完成了。那我们试着写写代码吧voidbubble_sort(intarr[],intsz){ inti=0;//趟数 for......
  • C语言 解开你对“二级指针”的心结与恐惧
    今天Mr.狠人在这和大家分享二级指针,我们不用把二级指针想的天花乱坠非常困难其实二级指针就是一个指针的地址。我们用代码来辅助理解一下intn=10;int*pn=&n;//pn是一个一级指针变量//pn指向n,指针变量也是一个变量现在我们有了n的地址,并且......
  • C语言指针的进阶理解——指针数组
    //整型数组 //顾名思义是存放整型类型的元素的数组 intarr1[]={1,2,3,4,5};//arr内元素的类型是int //字符数组 //顾名思义是存放字符类型元素的数组 chararr2[]={'a','b','c'};//arr内元素的类型是char那么指针数组你是不是也能推算出来它大概的模样了,差不......
  • 【位运算】--- 初阶题目赏析
     Welcometo9ilk'sCodeWorld    (๑•́₃•̀๑) 个人主页:    9ilk(๑•́₃•̀๑) 文章专栏:   算法Journey 根据上一篇位运算的总结,我们来体会几道初阶题目。......
  • 学习C语言之分支与循环(上)桀桀桀
     晚上好各位,桀桀桀。上面就是我们今天的内容了话不多说,开干。    <一>、if语句1.if语句的语法形式如下:if(表达式) 语句表达式成⽴(为真),则语句执⾏,表达式不成⽴(为假),则语句不执⾏。在C语⾔中,0为假,⾮0表⽰真,也就是表达式的结果如果是0,则语句不执⾏,表达式......
  • C语言程序设计(初识C语言后部分)
    世间风物论自由,喜一生我有,共四海丰收。12.表达式求值表达式求值的顺序一部分是由操作符的优先级和结合性决定。同样,有些表达式的操作符在求职过程中需要转换为其它类型。1)隐式类型转换C的整型算术运算总是至少以缺省(默认)整型类型的精度来进行的。为了获得这个精度,表达......
  • C语言程序设计-扫雷游戏
    愿你我成功会师,翻过雪山,走过草地,不远万里。扫雷游戏game.h#pragmaonce#include<stdio.h>#include<time.h>#include<stdlib.h>#defineROW9#defineCOL9#defineROWSROW+2#defineCOLSCOL+2#defineEASY_COUNT10voidInitBoard(charboard[ROWS][C......