C语言中的指针和数组

砾漠实例化
• 阅读 1862

指针是 C 语言中的一个特点,也是内存地址,是内存单元的编号,指针变量是用来存放内存地址的变量,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作;一般把指针称为指针变量,指向的对象可以是变量或者数组等;指针指向数组时,它内容存储的是数组的首地址,所以数组和指针就产生了一定的关系。那什么是数组呢?具有相同类型的若干元素按有序的形式组织起来的一种集合就叫做数组,下面会对指针、指针和数组相结合的一些用法进行分析。

1、指针

1、1 定义 int * 类型的指针变量以及修改这个指针变量,其他类型的指针变量写法也类似

int * p; //p是变量的名字,int * 表示 p 变量存放的是 int 类型变量的地址;它并不是表示定义了名叫 *p 的变量

int i = 5;
p = &i; //p 保存了 i 的地址,所以 p 指向 i
int i2 = 6;
p = &i2; //修改 p 的值不影响 i 的值
int * p2 = &i;
i = 9; //修改 i 的值不影响 p2 的值
printf("%d\n",*p2); //*p2 输出为9,*p2 等同于 i 的值,p2 等同于 i 的地址;*p2 可以理解为以 p2 的内容为地址的变量,p2 的内容就是 i 的地址

1、2 指针与指针变量是不同的概念

系统为每一个内存单元分配一个地址值,C 语言中把这个地址值称为“指针”。例如 int j = 6; int p=&j; 存放变量 j 的内存单元的编号 &j 被称为指针,它的本质是一个操作受限的非负整数。“指针变量”则是存放前述“地址值”的变量,也可以表述为,“指针变量”是存放变量所占内存空间“首地址”的变量。把 j 的指针 &j 赋给了int 型指针变量p,就是说 p 存入着 &i,因此说指针变量是存放指针的变量。

1、3 当某一类型的指针变量没有指向任何一个地址时,它的值为垃圾值

int i = 6;
int * p;
int * p2;
p = &i;
*p = p2; //这里语法编译错误,因为 *p 和 p2 是两个不同的类型,*p 表示 int 类型的内容,p2 表示 int * 类型变量的地址

*p = *p2; //错误的,因为 p2 是垃圾值,所以 *p2 也是垃圾值
p = p2; //p2 是垃圾值,p2 赋值给 p,p 也变成了垃圾值

printf("%d\n",*p); //p 的空间是属于当前程序的,因此当前程序可以读写 p 的内容,如果 p 的内容是垃圾值,那么当前程序不能读写 *p 的内容,因为 *p 所代表的内存单元的控制权限并没有分配给当前程序,所以运行到这一行会出错

1、4 在当前函数中通过获取2个某一类型(一般是基本数据类型)变量的地址并传入另外一个函数进行内容交换,可实现2个某一类型变量内容的交换

void swop(int * p1,int * p2)
{
   int t = 0; //这里的 t 必须定义为 int 类型的,因为 *p1 和 *p2 都是 int 类型的
   t = *p1;
   *p1 = *p2;
   *p2 = t;
   
   /*
   ** 以下这种交换的写法是错误的,只是交换了 p1 和 p2 指向的地址
   ** 并没有交换 以 p1 和 p2 的内容为地址的变量,即 i1 和 i2
   **
   **   int * t;
   **   t = p1;
   **   p1 = p2;
   **   p2 = t;
   */
}

int main()
{
    int i1 = 2;
    int i2 = 3;
    swop(&i1,&i2);
    printf("i1 = %d,i2 = %d\n",i1,i2); //这个时候输出时,i1 = 3,i2 = 2;真正完成了交换
    return 0;
}

1、5 “*” 代表的含义

1.5.1 表示乘法

1.5.2 表示定义指针变量,举个例子
int * p1; //定义了名叫 p1 的变量,int 代表 p1 只能存放 int 变量的地址

1.5.3 表示指针运算符,该运算符放在定义好的指针变量的面前,举个例子
如果 p 是一个已经定义好的指针变量,那么 *p 代表以 p 的内容为地址的变量

1、6 假设 n >= 2 , n 级指针变量只能指向 n - 1 级指针变量的地址

int i = 3; //假设 i 的地址为 1001h
int * p = &i; //p 存放的内容是 i 的地址,假设 p 的地址为 1002h
int ** q = &p; //q 存放的内容是 p 的地址,假设 q 的地址为 1003h
int *** r = &q; //r 存放的内容是 q 的地址,假设 r 的地址为 1004h

//int *** r2 = &p; //error,只能存放二级指针变量的地址

printf("*p = %d\n", *p); //这里输出 i 的内容 3

printf("*q = %d\n", *q); //这里输出 p 存放的地址,即 i 的地址
printf("**q = %d\n", **q); //这里输出 *p,因为 *(*q) 等同于 *p

printf("*r = %d\n", *r); //这里输出 q 存放的地址
printf("**r = %d\n", **r);  //这里输出 p 存放的地址,因为 *r = q,**r = *q = p
printf("***r = %d\n", ***r); //这里输出 i 的内容,即 3;***r = **q = *p = i 

2 指针和数组

2、1 指针变量指向一维数组时,它存放的是一维数组的地址同时也是一维数组第一个元素的地址

int a[5] = {1,2,3,4,5};
int * p = a;
int * p2 = &a[0];
int boolean = p == p2;
printf("boolean的值为%d\n",boolean); //boolean 的值为1,证明了 a 数组的地址和 a[0] 元素的地址相等

2、3 如果 p 是一个指针变量且指向一个一维数组,可以使用它输出数组元素,且 p[i] 等价于 *(p + i)

int a[5] = {1,2,3,4,5};
int * p = a;
int i = 0;
for (i = 0; i < 5;i++) {
  printf("p[i]的值为%d\n",p[i]); //p[i] 等价于 a[i]
  printf("*(p+i)的值为%d\n",*(p+i)); //因为 a 数组的地址是连续的,p + i 等价于 a 的地址加上 i,即 p + i 等价于 a[i] 的地址,那么 *(p + i) 等价于 a[i] 的元素
}

2、4 指针变量不能相加,不能相乘,也不能相除;如果两个指针变量指向的是同一块连续空间中的不同存储单元,那么这两个指针变量才可以相减

int a[5] = {1,2,3,4,5};
int * p = a;
int * p2 = &a[3];
int j = p2 - p; //正确
int j2 = p2 + p; //error,不能相加
int j3 = p2 / p; //error,不能相除
int j4 = p2 * p; //error,不能相乘

2、5 一个指针变量,无论它指向的地址的变量占多少个字节,该指针变量本身只占4个字节

char c = 'c';
int i = 0;
double d = 2.0;
char * p = &c;
int * p2 = &i;
double * p3 = &d;
printf("p所占的字节数为%d\n",sizeof(p)); //sizeof(p) 输出为 4
printf("p2所占的字节数为%d\n",sizeof(p2)); //sizeof(p2) 输出为 4
printf("p3所占的字节数为%d\n",sizeof(p3)); //sizeof(p3) 输出为 4

2、6 静态数组长度必须事先确定,而且只能是常整数,不可以是变量

int length = 4;
int a[2]; //正确
int a2[length]; //错误的,数组长度不可以是变量

2、7 使用 malloc 函数动态构造数组

int length = 0;
int * pArr;
printf("请输入数组的长度\n");
scanf("%d",&length);

/**
** 1、使用 malloc 函数,要添加 malloc.h 头文件
** 2、malloc 函数只有一个形参而且只能是整形的,它表示请求系统为当前程序分配的字节数
** 3、malloc 函数返回的是第一个字节的地址
** 4、一共分配了 8 * length 个字节,pArr 变量占 4 * length 个字节,pArr 指向的内存也占 4 * length 个字节
** 5、pArr 本身所占的内存是静态分配的,pArr 指向的内存是动态分配的
** 6、动态构造一个一维数组,假设 length = 5,类似 int pArr[20] 数组
*/
pArr = (int *)malloc(4 * length);
int i = 0;
for(i = 0; i < length;i++) {
    pArr[i] = 2 * i;
}
for(i = 0; i < length;i++) {
    printf("%d\n",pArr[i]);
}

2、8 数组动态内存和静态内存的比较

2、8、1 静态数组内存是由系统自动分配,是在栈分配的,由系统自动释放;静态定义的数组,它的内存无法让程序员手动释放,在一个函数运行期间,系统为该函数中数组分配的空间会一直存在,直到该函数运行完毕时,数组的空间才会释放;数组的长度一旦定义,其长度不可以更改;
函数中定义的数组,函数运行完后数组无法再给其他函数使用。

2、8、2 动态数组可以解决静态数组的一些缺陷;动态数组内存是由程序员手动分配的,且是在堆中分配的,需要手动释放。

好了,本篇文章写到这里就结束了,由于本人技术水平有限,难免会有出错的地方,欢迎批评指正,谢谢大家的阅读。

C语言中的指针和数组
关注公众号,阅读更多有趣的文章

点赞
收藏
评论区
推荐文章
CuterCorley CuterCorley
4年前
C语言入门系列之8.指针的概念与应用
一、指针引入指针是C语言中的一个重要的概念,也是C语言的一个重要特色。正确而灵活地运用它,可以有效地表示复杂的数据结构;能动态分配内存;能方便地使用字符串;有效而方便地使用数组等。掌握指针的应用,可以使程序简洁、紧凑、高效。可以说,不掌握指针就是没有掌握C的精华。1.地址的概念数据在内存中的存储和读取如下:内存区的每一个字节有一个编号,称为地址
桃浪十七丶 桃浪十七丶
4年前
C语言中指针及其应用基础篇(指针变量,指针和数组)
一、指针变量1.1代码分析先把代码贴上来进行分析。笔者在写代码时候会加上头文件stdlib和代码system("pause");这是为了防止闪屏,在学校时候老师有教过其他写法,但是还是觉得笔者这样写比较容易记得住。cinclude<stdio.hinclude<stdlib.hintmain()intvar20;intp;//定义指针变
Kevin501 Kevin501
5年前
Go语言中new()和make()的区别
1.Go语言中的值类型和引用类型值类型:int,float,bool,string,struct和数组(数组要特别注意,别搞混了)变量直接存储值,分配栈区的内存空间,这些变量所占据的空间在函数被调用完后会自动释放。引用类型:slice,map,chan和值类型对应的指针变量存储的是一个地址(或者理解为指针),指针指向内存中真
Wesley13 Wesley13
4年前
ES6模块与CommonJS模块有什么区别?
ES6Module和CommonJS模块的区别:CommonJS是对模块的浅拷贝,ES6Module是对模块的引用,即ES6Module只存只读,不能改变其值,具体点就是指针指向不能变,类似constimport的接口是readonly(只读状态),不能修改其变量值。即不能修改其变量的指针指向,但可以改变变量内部指针指向,可以对comm
Wesley13 Wesley13
4年前
C89和C99标准比较
1、增加restrict指针C99中增加了公适用于指针的restrict类型修饰符,它是初始访问指针所指对象的惟一途径,因此只有借助restrict指针表达式才能访问对象。restrict指针指针主要用做函数变元,或者指向由malloc()函数所分配的内存变量。restrict数据类型不改变程序的语义。如果某个函数定义了两个restrict指针变
Wesley13 Wesley13
4年前
C++学习(十八)(C语言部分)之 指针2
指针1、指针的概述指针是什么?指针是一个地址是一个常量int整型intaa是变量指针用来做什么?方便使用数组或者字符串像汇编语言一样处理内存地址2、指针变量什么是指针变量?是一个可以存储地址的一个“容器”经常会吧指针变量读作指针后面吧地址当做“指针”吧存储地址的变量叫做“指针变量”
Stella981 Stella981
4年前
Golang教程:指针
什么是指针指针是存储一个变量的内存地址的变量。  !(https://oscimg.oschina.net/oscnet/70c3337580dba5f227a4b71b2f73d6ba527.png)在上图中,变量 b 的值是 156,存储在地址为 0x1040a124 的内存中。变量 a 存储了变量 b 的
Wesley13 Wesley13
4年前
C语言之指针
  学过编程语言的童鞋们都知道指针是C语言的精髓,学好了指针就等于学好了C语言,它能够直接对物理地址进行访问,具有双重功能,是嵌入式设计中必不可少的一门语言。C语言功能强大的主要原因就是具有指针结构。指针是一种特殊的数据类型,直接指向目标的存储地址,实现直接访问对象存储空间功能。指针到底是什么    计算机的内存被划分为多个存储单
小万哥 小万哥
2年前
C 语言指针完全指南:创建、解除引用、指针与数组关系解析
C语言中的指针创建指针我们可以使用引用运算符&获取变量的内存地址:cintmyAge43;//一个int变量printf("%d",myAge);//输出myAge的值(43)printf("%p",&myAge);//输出myAge的内存地址(0x7ff
小万哥 小万哥
2年前
C 语言文件处理全攻略:创建、写入、追加操作解析
C语言中的文件处理在C语言中,您可以通过声明类型为FILE的指针,并使用fopen()函数来创建、打开、读取和写入文件:cFILEfptr;fptrfopen(filename,mode);FILE基本上是一个数据类型,我们需要创建一个指针变量来使用它(f
小万哥 小万哥
1年前
C++ 解引用与函数基础:内存地址、调用方法及声明
C解引用获取内存地址和值在上一页的示例中,我们使用了指针变量来获取变量的内存地址(与引用运算符&一起使用)。但是,你也可以使用指针来获取变量的值,这可以通过使用运算符(解引用运算符)来实现:cstringfood"Pizza";//变量声明stri