0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

数组和指针有什么区别

Q4MP_gh_c472c21 来源:老吴嵌入式 作者:吴伟东Jack 2021-11-22 09:56 次阅读

一、能力错觉

当书本(或谷歌)摆在眼前时,大脑会产生错觉,以为学习材料也同样存入了大脑,阅读毕竟比回想简单多了。

以为反复的阅读资料就是自己已经掌握知识,这就是能力错觉

解决能力错觉的方法

  • 积极回想——让大脑提取关键概念,而非通过重复阅读被动地获取知识,这样才能高效地学习。

现在网络上盛行各种it类的视频教程,我不否认不少视频教程是高质量的,但是所有视频类资料都有一个问题:

  • 可以让人不用阅读书籍,减少思考,只要被动地听老师们讲课就能舒舒服服地获取到知识,这很容易会让某些初级的软件开发人员形成能力错觉,仿佛视频里写过的代码,解决过的 bug 都是自己已经学到的知识似的。

有效的解决办法是

  • 公开学习笔记和练习代码。公开学习笔记的目的是借助外部压力,高效回想,进而提高自己的学习标准。

  • 另外,公开写作则会给你的写作增加很多维度的外部压力,你会想如何让别人更好地理解我要表达的意思;如何传递更多价值,让别人读完有所收获;如何让更多人看到;如何让别人读得下去;如何排版让大家看得更舒服;


二、数组和指针有什么区别?

正文目录:

1.用于声明时两者有重大区别
2. 你真的理解声明和定义吗?
3. 数组和指针的底层是如何访问数据的?
4. 哪些场景可以用指针代替数组?
5. 为什么C语言要把数组形参退化为指针?
6. 如何使用指针访问多维数组?
7.相关面试题

写作目的:

  • 正确看待数组和指针。

测试环境:

  • Ubuntu 16.04
  • Gcc 5.4.0

1、用于声明时两者有重大区别

1) 误导新手的说法:

由于数组和指针的所谓等价性非常接近,不少程序员有时忽视了二者之间的其他重要区别 ,最误导新手的说法之一就是 “数组和指针是相同的",这是一种非常危险的说法。

看下面这个例子:

externint*x;
externintx[];
  • 第一条语句声明 x 是个 int 型的指针;

  • 第二条语句声明 x 是个 int 型数组,长度尚未确定,即存储长度在别处定义。

2) 为什么有些人会误以为指针和数组总是可以互换?

最主要原因是
对数组的引用 ( x[i] ) 总是可以写成对指针的引用 ( *(x+i) )

  • 即确实存在一种指针和数组的定义完全相同的上下文环境。不幸的是,这只是数组的一种极为普通的用法,并非所有情况下都是如此。

2、你真的理解声明和定义吗?

想要要真正理解为什么 extern int *x 不等于 extern int x[],我们首先需要搞清楚什么是声明,什么是定义。

1) 链接器的视角:

  • C 语言中的对象必须有且只有一个定义,但它可以有多个 extern 声明。这里所说的对象跟 C++ 中的对象并无关系,这里说的对象是 从链接器的视角来看的,链接器将各个函数、变量都视为对象

2) 定义和声明的联系与区别:

  • 定义是一种特殊的声明,它创建了一个对象;声明简单地说明了在其他地方创建的对象的名字。

  • 定义只能出现在一个地方,它指定了对象的类型并分配内存以创建新的对象。声明可以多次出现 以描述对象的类型,用于指代其他地方定义的对象,它不为对象分配内存。

  • extern 对象声明告诉编译器对象的类型和名字,对象的内存分配则在别处进行。由于并未在声明中为数组分配内存,所以并不需要提供关于数组长度的信息 (多维数组例外)。

3) 总结成一句话:

  • 定义 = 声明 + 分配内存 (创建对象)

4) 回过头来看这个例子:

externint*x;
externintx[];

前者声明了一个指针,后者声明了一个数组,那么它们对应的指针和数组的定义(最重要的是内存分配) 能相等吗?


3、数组和指针的底层是如何访问数据的?

现在我们来看看指针和数组的定义与使用。

1) "地址 X (Address)" 和 "地址 X 的内容(Contents of Address)" 之间的区别:

对于"地址 X" 和 "地址 X 的内容",在 C 语言中是用同一个符号来表示这两样东西,由编译器根据上下文环境判断它的具体含义。

2) 看下面这个例子:

X=Y
  • 符号 X 的含义是 X 所代表的地址,它是左值,编译时可知;

  • 符号 Y 的含义是 Y 所代表的地址上的内容,它是右值,运行时才知;

  • 左值包括可修改的左值和不可修改的左值,C 语言中,一般的数据类型都是都可作为可修改的左值,只有数组是不可修改的左值;

  • 数组的地址在编译时可知,编译器有了这个地址 (即数组首地址),就可以直接进行读写操作。而指针必须在运行时取得它的当前值,然后才能对它进行解除引用操作,才能进行读写操作。

3) 数组和指针的访问方式是不同的:

chara[9]="abcedefgh";

  • 上面这个例子中,a 是一个数组。

  • 在编译器符号表里有一个符号 a ,它的地址为9980;

  • 数组内的字符都可以从这个地址 + 偏移量找到,编译器甚至并不需要知道数组的总长度;

charc='F';
char*p=&c;

  • 上面这个例子中,p 是一个指针。

  • 在编译器符号表中有一个符号 p, 它的地址为 4624;

  • p 指向的对象是一个字符。为了取得这个字符,必须得到地址 p 的内容 (5081),把它作为字符的地址并从这个地址中取得这个字符 ('F')。

4) 当定义为指针 (char *p),并以数组方式 (p[i]) 引用时会发生什么?

char*p=”abcdefgh”
printf("%c
",p[3]);

char*a=”abcdefgh”
printf("%c
",a[3]);
  • p[3] 和 a[3] 都能成功访问到字符 'd';

  • a[i] 表示 "从 a 的地址开始,前进 i 步,每步都是一个字符(数组类型的长度)”;

  • p[i] 表示 "从 p 所指的地址开始,前进 i 步,每步都是一个字符(即指针所指类型的长度)”;

  • 所以,当你用 extern char *p 来声明 char p[10]时,编译器会把 p[i] 当成一个指针(Address),然后去获取 *(p[i]) (即 Content of Addrss),这时最好的结果是程序立马崩溃,你能快点发现问题。最糟糕的情况是,程序崩溃在将来的某个时刻,你则 debug 到怀疑人生


4、哪些场景可以用指针代替数组?

数组和指针容易混淆使用的 2 大类场景:

  • 声明

  • 在表达式中使用;

1) 声明:
声明的场景包括 3 种:

  • 1> 不可以的场景:定义也是一种声明,定义数组时不能用指针的形式;

  • 2> 不可以的场景:extern 数组时不能改写成指针的形式, 例如:

intchar[10];//define
externchara[];//ok
externchar*a;//error
  • 3> 可以的场景:函数的形参,用数组形式还是指针形式,随你自己的喜好。

2) 在表达式中使用:

  • 在表达式中,指针形式和数组形式等效。

3) 几条重要的规则:

  • 规则1:"表达式中的数组名" 就是指针;

  • 规则2:把数组下标可当作指针的偏移量;

  • 规则3: "作为函数参数的数组名" 等同于指针;


5、为什么C语言要把数组形参退化为指针?

1) 出于效率的考虑:
  • 在 C 语言中,所有非数组形式的数据实参均以传值形式(对实参作一份拷贝并传递给调用的函数,函数不能修改作为实参的实际变量的值)。

  • 如果要拷贝整个数组,无论在时间上还是在内存空间上的开销都可能是非常大的

2) 出于简化编译器的考虑:

  • 在 C 语言中,所有的数组在作为参数传递时都转换为指向数组起始地址的指针,而其他的参数均采用传值调用。

  • 允许程序员把形参声明为数组 (程序员打算传递给函数的东西) 或者指针 (函数实际所接收到的东西)。在函数内部,编译器始终把它当作一个指向数组第一个元素的指针

3) 看下面这个例子:

staticintarray[10],array2[10];

staticvoidfunc1(int*ptr)
{
ptr[1]=3;
*ptr=3;
ptr=array2;
}

staticvoidfunc2(intarray[])
{
array[1]=3;
*array=3;
array=array2;//OK,becausearrayisapointer
printf("*array=%d
",*array);
}

intmain(void)
{
func1(array);
func2(array);

array[1]=3;
*array=3;
array=array2;//ERROR
return0;
}

编译运行:

// main 中调用 array = array2时:
11:error:assignmenttoexpressionwitharraytype

//去掉 main / array = array2时:
$./point_array_arg
*array=0

6、如何使用指针访问多维数组?

1) C 语言的多维数组:

  • 采用最右的下标先变化原则,其最大的用途是存储多个字符串;

  • 单个元素的存储和引用实际上是以线性形式排列在内存中;

  • 不能把一个数组赋值给另一个数组,因为数组作为一个整体不能成为赋值的对象;

  • 可以把数组名赋值给一个指针,是因为在表达式中的数组名被编译器当作一个指针;

  • 指针下标引用的规则告诉我们 pea[i][j] 被编译器解释为 ((pea + i) + j);

  • 可以通过声明一个一维指针数组 ( (char *)pea[4],下标方括号的优先级比指针的星号高),其中每个指针指向一个字符串,来取得类似二维字符数组的效果;

7、相关面试题

1) 找错:计算字符串长度

下面这段程序是为了把字符串转换为大写:

#include

voidUpperCase(charstr[])
{
inttest=sizeof(str);
inttest2=sizeof(str[0]);

for(size_ti=0;i<sizeof(str)/sizeof(str[0]);++i){
if('a'<=str[i] && str[i]<='z')
str[i]-=('a'-'A');
}
}



intmain(void)
{

charstr[]="aBcDeefGHijKL";
printf("Thelengthofstris%d
",sizeof(str)/sizeof(str[0]));

UpperCase(str);
printf("result:%s
",str);
return0;
}

运行结果:

$./sizeof_array
Thelengthofstris14
result:ABCDEEFGHijKL

问题出在 UpperCase() 里的 sizeof(str),这里的 str 是一个指针而不是数组

正确的写法有2种

  • 给UpperCase传递长度参数,这是最稳妥的方式。
  • 在UpperCase 内使用strlen 获取字符串长度,这种方法仅适用于以 ‘’ 结尾的字符数组;
责任编辑:haq
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 指针
    +关注

    关注

    1

    文章

    480

    浏览量

    70545
  • 代码
    +关注

    关注

    30

    文章

    4774

    浏览量

    68505
  • 数组
    +关注

    关注

    1

    文章

    416

    浏览量

    25939

原文标题:你知道数组和指针有什么区别吗?

文章出处:【微信号:gh_c472c2199c88,微信公众号:嵌入式微处理器】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    指针数组和二维数组有没有区别

    指针数组和二维数组有没有区别?比如这样的两个代码。 int main(){ char *s1[] = { "hello", "world", "total" }; char s2[][
    的头像 发表于 11-24 11:12 135次阅读

    美国多IP服务器和美国多服务器什么区别

    美国多IP服务器和美国多服务器什么区别 美国多IP服务器和美国多服务器在概念、功能以及应用场景上存在明显的区别。主机推荐小编为您整理发布美国多IP服务器和美国多服务器
    的头像 发表于 11-11 10:22 177次阅读

    C语言指针运算符详解

    在C语言中,当你一个指向数组中某个元素的指针时,你可以对该指针执行某些算术运算,例如加法或减法。这些运算可以用来遍历数组中的元素,如ptr
    的头像 发表于 10-30 11:16 228次阅读

    RTOS与Linux到底什么区别

    很多做嵌入式开发的小伙伴都存在这样的疑惑:RTOS与Linux到底什么区别
    的头像 发表于 10-29 09:53 406次阅读

    面试常考+1:函数指针指针函数、数组指针指针数组

    在嵌入式开发领域,函数指针指针函数、数组指针指针数组是一些非常重要但又容易混淆的概念。理解它
    的头像 发表于 08-10 08:11 803次阅读
    面试常考+1:函数<b class='flag-5'>指针</b>与<b class='flag-5'>指针</b>函数、<b class='flag-5'>数组</b><b class='flag-5'>指针</b>与<b class='flag-5'>指针</b><b class='flag-5'>数组</b>

    请问ESPTOUCH和AIRKISS什么区别

    请问ESPTOUCH和AIRKISS什么区别?谢谢!
    发表于 07-12 12:44

    RV 和ARM什么区别

    district RV 和ARM什么区别
    发表于 06-26 12:41

    线路板的层和阶什么区别

    线路板的层和阶什么区别
    的头像 发表于 02-23 17:27 706次阅读

    数组和链表在内存中的区别 数组和链表的优缺点

    数组和链表在内存中的区别 数组和链表的优缺点  数组和链表是常见的数据结构,用于组织和存储数据。它们在内存中的存储方式以及优缺点方面存在一些显著的差异。本文将详细探讨这些差异以及它们的
    的头像 发表于 02-21 11:30 1015次阅读

    数组和链表区别

    数组和链表的区别,这个问题,不仅面试中经常遇到,考研的同学也得掌握才行。
    的头像 发表于 02-19 15:33 500次阅读
    <b class='flag-5'>数组</b>和链表<b class='flag-5'>有</b>何<b class='flag-5'>区别</b>

    TC397多核之间数据访问效率什么区别?本地和全局的效率什么区别

    TC397多核之间数据访问效率什么区别,本地和全局的效率什么区别,可不可以将电机同步ADC采集放到主核0,算法在1核执行
    发表于 02-06 07:42

    SPI和QSPI什么区别

    SPI和QSPI什么区别
    发表于 02-06 06:12

    求助,TC275中不同的STEP什么区别

    TC275中不同的STEP什么区别?我看了一些芯片CA-STEP,DB-STEP,DC-STEP这几个step什么区别呢?或者从哪个手
    发表于 02-04 07:34

    hdi板与普通pcb什么区别

    hdi板与普通pcb什么区别
    的头像 发表于 12-28 10:26 2775次阅读

    pcb软板和硬板什么区别

    pcb软板和硬板什么区别
    的头像 发表于 12-19 10:01 1934次阅读