《C指针》学习笔记( 第二、三章)指针基础、指针运算与一维数组

第二章 指针基础

指针:用于存储数据或函数的内存地址的变量,指针也占用内存空间
内存地址也是数字或值

2.1 变量的地址

在这里插入图片描述

上图表示如何用整型变量x存储值40。将值40存储在0x00394768位置,这个位置被变量x引用

内存地址也是数字或值,我们可以用指针来存储或访问某个内存地址

2.2 地址操作符

取址操作符&返回操作数的内存地址

#include <stdio.h>
#include <stdlib.h>

int main()
{
     int a;
     printf("please Input value of a:");
     scanf("%d",&a);
     printf("a=%d",a);
     printf("\naddress of a is %p\n", &a);
    return 0;
}

在这里插入图片描述

在上面的代码中,函数scanf使用取址操作符&得到存储用户输入变量a的地址,scanf函数需要知道输入值应该存储的地址

struct node{
    int a;

    int b;
};
int main()
{
    struct node p;
    printf("Address of p is:%p",&p);
    printf("\nAddress of a is:%p",&p.a);
    printf("\nAddress of b is:%p",&p.b);
    return 0;
}

在这里插入图片描述
注意第一个成员的地址与第二个成员的地址非常相近。这意味着对任意数量的结构体内的成员字段,地址按顺序或依据其大小就近分配

2.3 指针声明

数据类型 * 变量名

例1:一个指针变量指向并存储原始数据类型的地址

int * a; char * b;

解引用操作符(*)帮助编译器识别它是一个指针变量

例2:指针变量作为结构体的数据成员

struct inneer_node{
int in_a;
int in_b;
}
struct node{
int *a;
int *b;
strucy inner_node* in_node;
}

2.4指针赋值

int *a;

指针变量声明时没有指向,程序员必须在解引用它之前让其指向有效的内存地址

使用两种方式实现指针变量指向特定的内存地址

  • 1:利用指针地址&分配变量的地址
    在这里插入图片描述
  • 2:让指针变量指向来自堆的动态内存分配
    在这里插入图片描述

情形1,程序运行时根据变量范围分配内存存储变量x的值40
情形2,存储变量值的内存显然是通过调用malloc函数从堆中返回内存来创建的。
切记任何指针变量的操作都必须在它指向一个有效内存地址的前提下完成,否则会引起分段错误。如果发生分段错误,会导致程序崩溃并最终停止运行

2.5 指针变量的大小

在32位平台下,指针变量的大小是4字节,存储聚合数据类型地址的指针变量大小(如数组和结构体)也是4个字节,显然指针变量的内存地址大小为32位

2.6 指针解引用

使用“取值”操作符(*)可以访问指针变量的地址中存储的值,称为指针解引用

int x = 10;/*某个内存单元存储值10*/
int *ptr = &x; /*现在指针变量"ptr"正指向内存单元x=10*/
printf("Address of variable x=%p\n",&x);/*打印内存单元x的地址 */
printf("Address of variable x = %p\n", ptr); /*使用"ptr"变量,打印内存单元×的地址,它的值在内存单元"x" */
printf("value of variable x = %d\n", x);/*打印变量x的值 */
printf("value stored at address ptr = %p is %d\n", ptr ,*ptr); /*使用取值操作符(*ptr)打印内存单元x的值*/

在这里插入图片描述

变量ptr的值和表达式( &x)的值本质上是等价的,为变量x的内存位置,因为ptr现指向x。
利用解引用操作符(* )得到某些内存单元的值。因此,表达式* ptr 、*(&x)和x的值都为10。

2.7指针的基本用法

2.7.1传值

函数从调用者接收信息并将结果返回给调用者
值传递后将复制到被调用的相应栈中
在这里插入图片描述

2.7.2引用传递

通过引用传递传递变量的内存地址而不是变量值本身
通过引用技术有可能在不同的函数中对某个函数的局部变量进行操作

2.8指针和常量

普通的const变量的含义是初始化时的赋值在其生存期内不能修改

2.8.1常量指针变量

//这一块可能于其他书籍或网页的定义有歧义

常量指针是一个仅指向唯一内存地址的指针变量,因此,指针变量的值不能修改

常量指针声明

 int * const  a;

常量指针变量声明时必须初始化,一旦完成初始化,常量指针不能再指向其他内存地址
在这里插入图片描述

2.8.2 常量指针

常量指针是指某个指针变量的值(即变量的内存地址)不能修改指定内存地址存放的值
常量指针声明

const int * a;

在这里插入图片描述

2.8.3 指针常量

不能修改指针变量的值(即不能指向其他内存单元),也不能修改存储在改地址的值

指针常量声明:

const int * const a;

2.9 多级指针

指针变量本身的地址可以存在其他变量中

声明:

int ** a;

在这里插入图片描述

2.10理解神秘的指针表达式

2.10.1 一级指针应引用

int val=10;
int *ptrVal=&val

ptrVal=&val,都是保存的val的地址

2.10.2一级指针解引用

*ptrval=10
*(&val)=10
*ptrVal=*(&val),都是保存的val的值

2.10.3二级指针引用

int** ptrptrvar=&ptrvar

ptrptrvar是一个指向指针变量的指针
二级指针 ptrptrvar存储的是指针变量ptrvar的地址
ptrptrvar=&ptrvar

2.10.4 二级指针解引用

ptrptrvar得到的是ptrvar的地址存放的值(ptrvar的地址存放的值是一个ptrvar指针)
所以
ptrptrvar=ptrvar

根据我自己的总结,一个*可以抵消一个&
在这里插入图片描述

在这里插入图片描述

第三章 指针运算与一维数组

指针变量能做算术运算操作

3.1 数组内存排列

字节序
字节序描述数据将存储在内存时的格式/排列。加载/存储指令从内存读取数据,在运行完数据处理指令后从寄存器将数据写回内存。
在存储和加载时,CPU必须采用硬件支持的字节序格式。
字节序分两类:大端和小端。一个字长为4字节/32位。

在这里插入图片描述

3.2指针运算

指针运算可以使用++,+,- -,-但是不能使用/和*

3.2.1指针加法

#include <stdio.h>
#include <stdlib.h>
int main()
{
int arr[4]={1,2,3,4};
printf("%d\n",arr[0]);
printf("%p\n",arr);
int*ptr=&arr;
printf("int指针\n",arr);
for(int i=0;i<4;i++)
{
   printf("%p\n",ptr);
    ptr++;
}

char*ptr2=(char*)&arr[0];

printf("char指针\n",arr);
for(int j=0;j<4;j++)
{
   printf("address:%p  value:%x\n",ptr2,*ptr2);
   ptr2++;
}
return 0;
}

在这里插入图片描述

从以上代码可以看出,数组名指向数组的首地址
由于Int占4个字节,所以ptr++一次跳过4个字节,指向数组中的下一个元素且指向当前元素的第一个字节
由于char占1个字节,所以,可以把一个Int型数据的地址的首个字节用char*来指向

在这里插入图片描述

指针变量加偏移量是非常合法的操作,但不允许将指针变量加入另一个指针变量中

int *ptr;int *ptr1;
ptr=ptr+1;//合法
ptr=prtr+ptr2;//不合法

3.2.2指针减法

int arr[4]={1,2,3,4};
printf("%d\n",arr[0]);
printf("%p\n",arr);
int*ptr=&arr[3];
printf("int指针\n",arr);
for(int i=3;i>=0;i--)
{
   printf("%p\n",ptr);
    ptr--;
}

在这里插入图片描述

两个指针变量的减法

当两个指针分别指向数组连续内存地址的不同位置,让他们彼此相减得到的结果差表示两个指针变量之间存在的元素数目

在这里插入图片描述

上述代码段中,int * ptr1指向第0个索引,int*ptr2指向第1个索引。当两个变量彼此相减后结果为1,因为这两个连续位置之间只存在一个元素。

比较两个指针变量

ptr1==ptr2
ptr1<ptr2
ptr1>ptr2

3.3数组探究

数组名等同于指定数组的第0个位置的地址,加偏移量之后数组名的作用相当于指针变量

int arr[4]={1,2,3,4};
printf("%p",arr);
for(int i=0;i<4;i++)
{
printf("%p",arr++);
}

不能用arr=arr+1,因为这样就试图修改arr的指向了,这是不可以的
在这里插入图片描述

可以用arr+1指向下一个元素的地址
arr++也是不允许的,因为这试图修改数组变量的起始地址
在这里插入图片描述

arr+2为第二个索引的地址等价于&arr[0]+2
在这里插入图片描述
在这里插入图片描述

arr+offset=&arr[offset];
*(arr+offset)=arr[offset];

3.3.1动态数组

有时候定义数组时不知道要存储的数组元素数目,程序运行时,数组元素数目可能增多也可能减少,所以咱们写程序时应当让数组的大小在运行时可变

int arr[10]:静态数组,运行时大小不能该改变
指针能帮助我们操作内存区域实现根据需求增加或降低内存,利用指针和堆实现该目标,调用malloc()在堆中分配内存

#include <stdio.h>
#include <stdlib.h>
int *ptr=NULL;
static int count=0;

 void insert(int data)
 {
     if(ptr==NULL)
     {
         ptr=(int*)malloc(sizeof(int));//从堆分配空间给第一个data单元
         ptr[0]=data;//利用数组符号访问内存地址来存储数据
     }
     else
     {
         ptr=(int*)realloc(ptr,sizeof(int)*(count+1));//增加内存大小
         ptr[count]=data;//利用数组符号访问内存地址来存储数据
     }
     count++;
 }
 void show()
 {

     for(int i=0;i<count;i++)
     {
         printf("%d\n",ptr[i]);
     }
 }
int main()
{
    int c=0;
    int data;
    while(c!=3)
    {

        printf("Insert choice:\n");
        printf("1 to insert data\n");
        printf("2 to show data\n");
        printf("3 to quit data\n");
        scanf("%d",&c);
        if(c==3)
        {
            break;
        }
        switch(c)
        {
       case 1:
        printf("Data=");
        scanf("%d",&data);
        insert(data);
        break;

        case 2:
        printf("Data in Array\n");
        show();
        break;
        }
    }
    return 0;
}

在这里插入图片描述

在上述代码中,the insert(int data)函数插入第一个元素。调用malloc()函数申请内存并将指针变量ptr指向从堆中返回的内存位置。在代码中,每次只能添加一个数据元素,所以它只需要一个存储空间。
增加内存分配空间给新的数据元素。调用realloc()函数创建数据所需的内存空间并将返回值赋值给指针变量ptr。realloc()调用函数包括期望增加内存空间大小的参数。
变量count统计已插入元素的数目。注意,分配内存大小相对于已插入元素是额外的。

在这里插入图片描述
ptr不是一个指针变量吗,怎么变成数组了?
参考:指针和数组不一样

3.3.2指针数组

指针数组是指在连续单元内存中存储指针变量,该数组中每个位置包含内存中某个数据的地址

指针数组声明:

int * arr[10];
#include <stdio.h>
int main(int argc,char* argv[]){
int arr[4] = {1,2,3,4};
int* arr_ptr[4];
int i;
for(i = 0; i<4; i++){
arr_ptr[i] = arr + i;
}
printf("Address of (arr) array element\n");
for(i = 0; i<4; i++)
{
printf( "Address of %d index = %p\n",i, arr + i);
}
printf( "value of (arr_ptr) array of pointer element\n");
for(i = 0; i<4; i++)
{
printf("value of %d index = %p\n",i,arr_ptr[i]);
}
return 0;
}

在这里插入图片描述

在这里插入图片描述

3.3.3数组指针

数组指针为指向数组的指针变量
数组指针声明

#include <stdio.h>
int main(int argc,char* argv[]){
int arr[4]= {1,2,3,4};
int(*ptr2arr)[4];
int i;
int *ptr = arr;
  ptr2arr = &arr;
for(i =0; i<4; i++){
printf("address of array = %p\n", arr + i);

}
for(i = 0; i<4; i++)
{
printf("Value at %p = %d\n" , (ptr2arr[0] + i),*(ptr2arr[0] + i));
}
return 0;
}

在这里插入图片描述

在上述代码中,ptr2arr为数组指针,指向存储四个整型类型数据元素的数组。

在这里插入图片描述
因为我们代码中用的%d,所以16进制地址转化成了10进制的地址
在这里插入图片描述

关于 *p打印出来的结果竟然是一个地址值这种情况,下面这篇博客有说到

c语言中数组指针取值*(解引用)问题的个人详细理解

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值