欢迎来到千学网!
您现在的位置:首页 > 实用文 > 其他范文

在C语言中什么是指针和数组等价

时间:2023-04-15 08:34:18 其他范文 收藏本文 下载本文

以下是小编精心整理的在C语言中什么是指针和数组等价,本文共4篇,希望对大家有所帮助。

在C语言中什么是指针和数组等价

篇1:在C语言中什么是指针和数组等价

在C语言中什么是指针和数组等价

在C语言中对数组和指针的困惑多数都来自这句话,说数组和指针“等价”不表示它们相同, 甚至也不能互换。它的意思是说数组和指针的算法定义可以用指针方便的访问数组或者模拟数组。

特别地,等价的基础来自这个关键定义:

一个T 的数组类型的左值如果出现在表达式中会蜕变为一个指向数组第一个成员的指针(除了三种例外情况); 结果指针的类型是T的.指针。

这就是说, 一旦数组出现在表达式中, 编译器会隐式地生成一个指向数组第一个成员地指针, 就像程序员写出了&a[0] 一样,

例外的情况是, 数组为sizeof 或&操作符的操作数, 或者为字符数组的字符串初始值。

作为这个这个定义的后果, 编译器并那么不严格区分数组下标操作符和指针。在形如a[i] 的表达式中, 根据上边的规则, 数组蜕化为指针然后按照指针变量的方式如p[i] 那样寻址, 如问题6.2 所述, 尽管最终的内存访问并不一样。如果你把数组地址赋给指针:

p = a;

那么p[3] 和a[3] 将会访问同样的成员。

篇2:C语言中函数和指针的参数传递

最近写二叉树的数据结构实验,想用一个没有返回值的函数来创建一个树,发现这个树就是建立不起来,那么我就用这个例子讨论一下c语言中指针作为形参的函数中传递中隐藏的东西,

大家知道C++中有引用的概念,两个数据引用同一个数据,那么更改任意的一个都相当于更改了本体,那么另一个数据所对应的值也会改变,可是C中是没有这个概念的。所以就产生了一些东西。和我们本来想的有差别。

一、明确C语言中函数的入口:

C语言中函数的形参负责接收外部数据,那么数据究竟怎么进入函数的呢,其实我们在函数体内操作的形参只是传递进来参数的一个副本,也就是说这两个参数虽然名字一样,对应的值一样,但是他们两个对应的内存地址是不一样的,也就是说这就是两个“看上去一模一样”的完全不同的变量。

所以一定要知道,C语言中函数是值传递的,也就是说,C语言只能把值传给函数,而不能把你想要传递的变量完全的放进函数内部。

二、指针传递给函数:

指针作为一个特殊的东西,他的强大之处就在于指针可以直接修改内存地址上的数据。虽然指针特别强大,但是他也难逃函数的限制,你传递给函数一个指针,因为是值传递,那么你在函数体内的使用的形参指针也只是一个副本,只是一个指向的值和你传进来的那个指针一样的一个另外的一个变量。也就是说他和普通常量是没有区别的。

三、我想要达到引用的效果怎么实现

C语言中因为是值传递的,那么我们就传递值,只要讲想要传递进函数的东西的地址传进函数,并且函数用一个指针接收,那么就相当于把这个变量地址原封不动的传递给了函数,形参的指针指向的是外面传进来的地址,有了地址不就好办了吗。

四、下面是我写的一个二叉树建立的一个无返回值的版本:

因为二叉树是用递归建立的,就像建立链表一样,一个节点一个节点建立,如果不获得上一个节点的地址,你怎么把链表连接起来呢,链表就散开了。

所以只能通过传递地址来达到找到已经建好的链表的前驱,才能把各个节点穿起来。

#include#includetypedef struct tree { char t; struct tree *lchild; struct tree *rchild;}Tree;void initTree(Tree **T) { char ch; ch = getchar; if (ch == '#') { *T = NULL; } else { *T = (Tree *)malloc(sizeof(Tree)); (*T)->t = ch; initTree(&(*T)->lchild); initTree(&(*T)->rchild); }}void qianT(Tree *T) { if (T) { printf(“%c ”,T->t); qianT(T->lchild); qianT(T->rchild); }}int main (void) { Tree *T; initTree(&T); qianT(T); return 0;}

注意看树的建立那个函数,我每次都是穿进去一个节点的地址,然后通过地址来找到已经建立好的树,才能将树建立起来,

那么有的人会问了?为什么不是下面这个写法呢?

void initTree(Tree *T) { char ch; ch = getchar(); if (ch == '#') { T = NULL; } else { T = (Tree *)malloc(sizeof(Tree)); T->t = ch; initTree(T->lchild); initTree(T->rchild); }}

这种写法,你每次传进来的都是一个变量,说过,c语言是值传递的,那么每次你申请的节点空间都是给副本申请的,然后递归的是副本的左右孩子,也就是说你的树根本没有建立起来,因为每次申请的内存都没有连接上。

为什么我的那个写法可以呢,因为我是用一个指向指针的指针来存地址的,我给传进来的地址申请了内存,也就相当于给穿进来的那个节点本身申请了内存,而不是给副本,所以二叉树就顺其自然建立起来了。

最后记住,c语言是值传递的,任何东西传递给函数的都只是值!

篇3:define在c语言中是什么意思

预处理命令以“#”号开头,如包含命令#include,宏定义命令#define等。一般都放在源文件的前面,它们称为预处理部分。

所谓预处理是指在进行编译之前所作的'工作。预处理是C语言的一个重要功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译。

在C或C++语言源程序中允许用一个标识符来表示一个字符串,称为“宏”。被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。宏定义是由源程序中的宏定义命令完成的。宏代换是由预处理程序自动完成的。

篇4:C语言中函数参数为什么是由右往左入栈的?

先通过一个小程序来看一看:

#includevoid foo(int x, int y, int z){printf(x = %d at [%X]n, x, &x);printf(y = %d at [%X]n, y, &y);printf(z = %d at [%X]n, z, &z);}int main(int argc, char *argv[]){foo(100, 200, 300);return 0;}

运行结果:

x = 100 at [BFE28760]

y = 200 at [BFE28764]

z = 300 at [BFE28768]

C程序栈底为高地址,栈顶为低地址,因此上面的实例可以说明函数参数入栈顺序的确是从右至左的,可到底为什么呢?查了一直些文献得知,参数入栈顺序是和具体编译器实现相关的。比如,Pascal语言中参数就是从左到右入栈的,有些语言中还可以通过修饰符进行指定,如Visual C++.即然两种方式都可以,为什么C语言要选择从右至左呢?

进一步发现,Pascal语言不支持可变长参数,而C语言支持这种特色,正是这个原因使得C语言函数参数入栈顺序为从右至左。具体原因为:C方式参数入栈顺序(从右至左)的好处就是可以动态变化参数个数。通过栈堆分析可知,自左向右的入栈方式,最前面的参数被压在栈底。除非知道参数个数,否则是无法通过栈指针的相对位移求得最左边的参数。这样就变成了左边参数的个数不确定,正好和动态参数个数的方向相反。

因此,C语言函数参数采用自右向左的入栈顺序,主要原因是为了支持可变长参数形式。换句话说,如果不支持这个特色,C语言完全和Pascal一样,采用自左向右的参数入栈方式。

这儿其实还涉及到C语言中调用约定所采用的方式,下面简单的介绍一下:

__stdcall与C调用约定(__cdecl)的区别

C调用约定在返回前,要作一次堆栈平衡,也就是参数入栈了多少字节,就要弹出来多少字节.这样很安全.

有一点需要注意:stdcall调用约定如果采用了不定参数,即VARARG的话,则和C调用约定一样,要由调用者来作堆栈平衡.

(1)_stdcall是Pascal方式清理C方式压栈,通常用于Win32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上”@”和参数的字节数。 int f(void *p) –>>_f@4(在外部汇编语言里可以用这个名字引用这个函数)

在WIN32 API中,只有少数几个函数,如wspintf函数是采用C调用约定,其他都是stdcall

(2)C调用约定(即用__cdecl关键字说明)(The C default calling convention)按从右至左的顺序压参数入栈,由调用者把参数弹出栈,

对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数vararg的函数(如printf)只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。 _cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。

(3)__fastcall调用的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。__fastcall方式的函数采用寄存器传递参数,VC将函数编译后会在函数名前面加上”@”前缀,在函数名后加上”@”和参数的字节数。

(4)thiscall仅仅应用于”C++”成员函数。this指针存放于CX/ECX寄存器中,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。

(5)naked call。 当采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。

(这些代码称作 prolog and epilog code,一般,ebp,esp的保存是必须的).

但是naked call不产生这样的代码。naked call不是类型修饰符,故必须和_declspec共同使用。

关键字 __stdcall、__cdecl和__fastcall可以直接加在要输出的函数前。它们对应的命令行参数分别为/Gz、/Gd和/Gr。缺省状态为/Gd,即__cdecl。

要完全模仿PASCAL调用约定首先必须使用__stdcall调用约定,至于函数名修饰约定,可以通过其它方法模仿。还有一个值得一提的是WINAPI宏,Windows.h支持该宏,它可以将出函数翻译成适当的调用约定,在WIN32中,它被定义为__stdcall。使用WINAPI宏可以创建自己的APIs。

综上,其实只有PASCAL调用约定的从左到右入栈的.而且PASCAL不能使用不定参数个数,其参数个数是一定的。

简单总结一下上面的几个调用方式:

指向函数的指针数组的用法

c语言中字符串操作的工具类

c/c++中的指针的应用及注意问题

生活语言中的物理常识

GO语言数组和切片实例详解

c语言实训报告心得体会

c语言实训心得体会字

Go语言中接口组合的实现方法

对汉英语言中不同死亡委婉语的解读

提示语在中间

《在C语言中什么是指针和数组等价(精选4篇).doc》
将本文的Word文档下载到电脑,方便收藏和打印
推荐度:
点击下载文档

文档为doc格式

点击下载本文文档