当前位置: 首页 > news >正文

C语言学习笔记--指针和数组的关系

1.数组的本质 

1)数组是一段连续的内存空间

2)数组的空间大小:sizeof(array_type)*array_size; 

3)数组名可看做指向数组第一个元素的常量指针

(4)数组声明时编译器自动分配一片连续的内存空间 ,而指针声明时只分配了用于容纳地址值的 4 字节空间

2.指针的运算

1)指针是一种特殊的变量,与整数的运算规则为: 

p + n == (unsigned int)p + n * sizeof(*p);

当指针 p 指向一个同类型的数组的元素时,p+1 指向当前元素的下一个元素,p-1 指向上一个元素。 

2)指针之间只支持减法运算且参与减法运算的指针类型必须相同。 

   p1 - p2 = ((unsigned int)p1 – (unsigned int)p2)/sizeof(type)

    只有当两个指针指向同一个数组中的元素时,指针相减才有意义,其意义为指针所指元 素的下标差。

    当两个指针指向的元素不在同一个数组中时,结果未定义。

指针运算的应用

#include <stdio.h>
//统计元素的个数
#define DIM(a) (sizeof(a) / sizeof(*a))
int main()
{
    char s[]={'H','e','l','l','o'};//
    char* pBegin = s;
    char* pEnd = s + DIM(s); //关键点,数组名 + n
    char* p = NULL;
    printf("pBegin = %p\n",pBegin); //第 1 个元素的地址
    printf("pEnd = %p\n",pEnd); //最后 1 个元素的地址
    printf("Size: %d\n",pEnd - pBegin); //5
    //在同一行中打印出:Hello
    for (p = pBegin;p < pEnd; p++)
    {
        printf("%c",*p);
    }
    printf("\n");

    return 0;
}

3.数组的访问方式

1)以下标的形式访问数组中的元素:如 a[i];

2)以指针的形式访问数组中的元素:如*(a+i) 

3)下标形式与指针形式的转换:a[n]==*(a+n)==*(n+a)==n[a];

#include <stdio.h>
int main()
{
    int a[5] = {0};
    int* p = a;
    int i = 0;
    for(i=0; i<5; i++)
    {
        p[i] = i + 1; //利用指针给数组元素赋值
    }
    for(i=0; i<5; i++)
    {
        //以指针形式来访问数组元素
        printf("a[%d] = %d\n",i,*(a+i));
    }
    for(i=0; i<5; i++)
    {
        //以下标形式来访问数组元素
        i[a] = i + 10; //比较另类,相当于 a[i] = i + 10
    }
    for(i=0; i<5; i++)
    {
        //通过指针来访问数组元素
        printf("p[%d] = %d\n",i,p[i]);
    }

    return 0;
}

指针和数组不同

//ext.c
//本文件应为.c,而不能是头文件。因为头文件会被直接包含在相应文件中,而.c 文件是分别编译的
int a[5] = {1, 2, 3, 4, 5}; //在该文件中,a 被看作一个数组
#include <stdio.h>
//编译这两个文件:gcc test.c ext.c
int main()
{
    //外部文件中 a 被定义成一个数组。int a[5] = {1, 2, 3, 4, 5};
    extern int a[]; //本文件,如果这样声明,a 仍被看作一个数组
    printf("&a = %p\n", &a); //这里的&a 为整个数组的地址
    printf("a = %p\n",a); //a 为首元素的地址,数值上等于&a
    printf("*a = %d\n",*a); //打印出第 1 个元素,即 1

/*
    extern int* a; //如果这样声明,编译器将 a 当成是一个 int 型的指针来使用。
    printf("&a = %p\n", &a); //会从符号表中查到指针 a 的地址,指向数组
    printf("a = %p\n",a); //从指针 a 处,取一个 int 型的数据,即第 1 个元素,为 1
    printf("*a = %d\n",*a); //试图从地址 0x01 处取出数据,违规内存访问错误。
*/
    return 0;
}

4.a &a 的区别

1a 为数组首元素的地址

2&a 整个数组的地址,即可将数组看成是一种数据结据。如 int[5]; 

3a &a 区别在在指针运算

a + 1 == (unsigned int)a + sizeof(*a);//a 代表首元素地址,*a 表示第 1 个元素

 &a + 1 == (unsigned int)(&a) + sizeof(*&a) == (unsigned int)(&a) + sizeof(a); 

指针运算经典问题

 

#include <stdio.h>

int main()
{
int a[5] = {1, 2, 3, 4, 5}; int* p1 = (int*)(&a + 1); //指向数组最后面,即第5个元素的后面 int* p2 = (int*)((int)a + 1);//指向数组的起始地址+1byte偏移处 int* p3 = (int*)(a + 1); //指向第2个元素 printf("p1[-1] = %d\n",p1[-1]);//输出第5个元素 //数组a在内存中从低地址到高地址的分布如下:(小端模式,低字节放入低地址) //01 00 00 00,02 00 00 00,03 00 00 00,04 00 00 00,05 00 00 00 //p2指向数组起始地址+1字节的偏移处,即01的后面,从这个地方开始读 //出4个字节,00 00 00 02,根据小端模式规则,该值为0x02 00 00 00, printf("p2[0] = 0x%X,p2[0] = %d\n",p2[0],p2[0]);//0x02000000==33554432 printf("p3[1] = %d\n",p3[1]); //输出第3个元素,即3 return 0; }

5.数组参数

1)数组作为函数参数时,编译器将其编译为对应的指针。因此,一般情况下,当定义的函数中有数组参数时,需要定义另一个参数来标示数组的大小。

void f(int a[])等价于 void f(int* a);

void f(int a[5])等价于 void f(int* a); //就是一个指针,丢失了数组长度的信息

#include <stdio.h>

void func1(char a[5]) //编译时,a被编译为一个指针,丢失了数组长度的信息
{
    printf("In func1:sizeof(a) = %d\n",sizeof(a));//a退化为指针,4
    
    *a = 'a';
     /*对指针所指向的内存进行存取(注意数组名也可以这样对内存
        进行访问,如访问第1次元素*a,对i个元素*(a+i);这说明数组名
        在使用上很像指针,但数组名并不是指针,见下面的分析*/
    
    //再次说明a是指针,而不是数组名
    a = NULL;//编译通过,a是指针类型。(而不是数组名,数组名不能作为左值)
}

void func2(char b[]) //b被编译成一个指针,与数组小大有没有被指定无关。
{
    printf("In func2:sizeof(b) = %d\n",sizeof(b));//b退化为指针,仍为4
    
    *b = 'b'; 
    
    b = NULL;//编译通过,b是指针类型
}

int main()
{
char array[10] = {0}; func1(array); printf("array[0] = %c\n",array[0]); //array[0] = a; func2(array); printf("array[0] = %c\n",array[0]); //array[0] = b; return 0; }

 

参考资料:
www.dt4sw.com
http://www.cnblogs.com/5iedu/category/804081.html

 

转载于:https://www.cnblogs.com/CoderTian/p/5906371.html

相关文章:

  • css3样式二
  • 手机端轻应用模拟原生的下拉刷新效果(JavaScript)
  • 樱花漫地集于我心,蝶舞纷飞祈愿相随---总结 顕出:void-sampling 显示:void-sampling...
  • node.js基础 1之简单的nodejs模块
  • Xcode 8 支持 iOS 7 真机解决过程记录
  • ajax 页面加载
  • C++-Qt【2】-实现一个简单的记事本
  • Python 学习之---文件目录处理
  • bootstrap0
  • 常用快速原型设计工具大比拼、原型设计工具哪个好用
  • Spring注入方式(1)
  • JSTL自定义标签
  • WIN10使用管理员权限运行VS2013
  • jq宽高 详解
  • 转载:第二弹!全球首个微信小程序(应用号)开发教程!通宵吐血赶稿!每日更新!...
  • [笔记] php常见简单功能及函数
  • canvas 绘制双线技巧
  • CentOS7 安装JDK
  • iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码...
  • JavaScript学习总结——原型
  • k8s 面向应用开发者的基础命令
  • Linux CTF 逆向入门
  • Mac转Windows的拯救指南
  • mysql 5.6 原生Online DDL解析
  • Selenium实战教程系列(二)---元素定位
  • 从零开始学习部署
  • 关于for循环的简单归纳
  • 基于Javascript, Springboot的管理系统报表查询页面代码设计
  • 看完九篇字体系列的文章,你还觉得我是在说字体?
  • 利用DataURL技术在网页上显示图片
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 如何用Ubuntu和Xen来设置Kubernetes?
  • 线性表及其算法(java实现)
  • 字符串匹配基础上
  • Hibernate主键生成策略及选择
  • raise 与 raise ... from 的区别
  • ​LeetCode解法汇总307. 区域和检索 - 数组可修改
  • !!java web学习笔记(一到五)
  • #gStore-weekly | gStore最新版本1.0之三角形计数函数的使用
  • #vue3 实现前端下载excel文件模板功能
  • #中国IT界的第一本漂流日记 传递IT正能量# 【分享得“IT漂友”勋章】
  • $.ajax,axios,fetch三种ajax请求的区别
  • (26)4.7 字符函数和字符串函数
  • (android 地图实战开发)3 在地图上显示当前位置和自定义银行位置
  • (C语言)输入一个序列,判断是否为奇偶交叉数
  • (Redis使用系列) Springboot 使用Redis+Session实现Session共享 ,简单的单点登录 五
  • (待修改)PyG安装步骤
  • (二) Windows 下 Sublime Text 3 安装离线插件 Anaconda
  • (附源码)ssm码农论坛 毕业设计 231126
  • (免费领源码)Java#Springboot#mysql农产品销售管理系统47627-计算机毕业设计项目选题推荐
  • (三)Honghu Cloud云架构一定时调度平台
  • (未解决)macOS matplotlib 中文是方框
  • (转)Android学习笔记 --- android任务栈和启动模式
  • .NET CF命令行调试器MDbg入门(四) Attaching to Processes
  • .NET CORE使用Redis分布式锁续命(续期)问题