c/c++语言开发共享如何在C编译器内部处理数组和指针类型? (int * a;与int a ;)

我需要一位拥有权威资源的语言律师。

看看下面的测试程序,它在gcc下干净地编译:

#include  void foo(int *a) { a[98] = 0xFEADFACE; } void bar(int b[]) { *(b+498) = 0xFEADFACE; } int main(int argc, char **argv) { int a[100], b[500], *a_p; *(a+99) = 0xDEADBEEF; *(b+499) = *(a+99); foo(a); bar(b); printf("a[98] == %Xna[99] == %Xn", a[98], a[99]); printf("b[498] == %Xnb[499] == %Xn", b[498], b[499]); a_p = a+98; *a_p = 0xDEADFACE; printf("a[98] == %Xna[99] == %Xn", a[98], a[99]); } 

它产生我期望的输出:

 anon@anon:~/study/test_code$ gcc arrayType.c -o arrayType anon@anon:~/study/test_code$ ./arrayType a[98] == FEADFACE a[99] == DEADBEEF b[498] == FEADFACE b[499] == DEADBEEF a[98] == DEADFACE a[99] == DEADBEEF 

a和b是同一类型吗? int *a是否在编译器内部处理与int a[]相同的类型?

从实际的角度来看, int a[100], b[500], *a_p, b_a[]; 所有似乎都是同一类型。 在上面的例子中,我很难相信编译器会在各种情况下不断调整这些类型。 我很高兴被certificate是错的。

有人能否明确地详细解决这个问题?

    a和b是同一类型吗? int *是否在编译器内部处理与int a []相同的类型?

    来自comp.lang.C FAQ :

    …每当数组出现在表达式中时,编译器会隐式生成指向数组第一个元素的指针,就像程序员编写了&a [0]一样。 (例外情况是数组是sizeof或&运算符的操作数,或者是字符数组的字符串文字初始值设定项…)

    …给定一个数组a和指针p,a [i]forms的表达式会导致数组衰减成指针,遵循上面的规则,然后被下标,就像表达式p中的指针变量一样[i](虽然最终的内存访问会有所不同……

    鉴于。的声明

     char a[] = "hello"; char *p = "world"; 

    …当编译器看到表达式a[3] ,它会发出代码从位置a开始,向后移动三个,然后在那里获取字符。 当它看到表达式p[3] ,它会发出代码从位置p开始,在那里获取指针值,向指针添加三个,最后获取指向的字符。 换句话说, a[3]是名为a的对象过去(开始)的三个位置,而p[3]p指向的对象之后的三个位置。

    重点是我的。 最大的区别似乎是当指针是指针时获取指针,而如果它是数组则没有指向fetch的指针。

    其中一个差异 – int a[x][y]int **a不可互换。

    2.10:

    数组数组(即C中的二维数组)衰减为指向数组的指针,而不是指向指针的指针。

    ab都是整数。 a [0]不是包含内存地址的内存位置,它是包含int的内存位置。

    数组和指针既不相同也不可互换。 表达式中出现的类型为array-of-T的左值(有三个例外)衰减到指向其第一个元素的指针时,数组等效于指针iff ; 结果指针的类型是指向T的指针。 在查看相关代码的汇编输出时,这一点变得清晰。 三个例外,fyi,是当数组是sizeof的操作数或或字符数组的文字字符串初始值设定项时。

    如果你想这样:

     char a[] = "hello"; char *p = "world"; 

    会导致数据结构可以表示如下:

      +---+---+---+---+---+---+ a: | h | e | l | l | o | | +---+---+---+---+---+---+ +-----+ +---+---+---+---+---+---+ p: | *======> | w | o | r | l | d | | +-----+ +---+---+---+---+---+---+ 

    并且认识到像x [3]这样的引用根据x是指针还是数组产生不同的代码。 a [3]用于编译器意味着:从位置a开始并移动三个并在那里获取char。 p [3]表示转到位置p,取消引用那里的值,移动三个并在那里取出char。

    从C语言标准 :

      6.3.2.1.3除非它是sizeof运算符或者运算符的操作数             unary&operator,或者是用于初始化的字符串文字            一个数组,一个类型为''array of type''的表达式           转换为类型''指向类型''的表达式            指向数组对象的初始元素,而不是            一个左值。 如果数组对象有寄存器存储类,则           行为未定义。 

    假设以下代码:

     #include  #include  int main(void) { char foo[10] = {0}; char *p = foo; foo[0] = 'b'; *(foo + 1) = 'a'; strcat(foo, "t"); printf("foo = %s, &foo = %p, &p = %p, sizeof foo = %lu, sizeof p = %lun", foo, &foo, &p, (unsigned long) sizeof foo, (unsigned long) sizeof p); return 0; } 

    foo被声明为一个10元素的char数组,所有元素都被初始化为0.p被声明为指向char的指针,并被初始化为指向foo。

    在线

     char *p = foo; 

    表达式foo的类型为“10-element array of char”; 因为foo不是sizeof或&的操作数,并且不是用于初始化数组的字符串文字,所以它的类型被隐式转换为“指向char的指针”并被设置为指向数组的第一个元素。 该指针值被复制到p。

    在线

     foo[0] = 'b'; *(foo + 1) = 'a'; 

    表达式foo的类型为“10-element array of char”; 因为foo不是sizeof或&的操作数,并且不是用于初始化数组的字符串文字,所以它的类型被隐式转换为“指向char的指针”并被设置为指向数组的第一个元素。 下标表达式被解释为“`*(foo + 0)”。

    在线

     strcat(foo, "t"); 

    foo的类型为“10-element array of char”,字符串文字“t”的类型为“2-element array of char”; 因为既不是sizeof或&的操作数,并且虽然“t”是字符串文字,但它不用于初始化数组,它们都被隐式转换为“指向char的指针”类型,并且指针值被传递给的strcat()。

    在线

      printf("foo = %s, &foo = %p, &p = %p, sizeof foo = %lu, sizeof p = %lun", foo, &foo, &p, (unsigned long) sizeof foo, (unsigned long) sizeof p); 

    如上所述,foo的第一个实例被转换为指向char的指针。 foo的第二个实例是&运算符的操作数,因此它的类型不会转换为“指向char的指针”,而表达式“&foo”的类型是“指向10个元素的char数组”或“char” ( * )[10]“。 将其与表达式“&p”的类型类型进行比较,该表达式是“指向char的指针”或“char ** ”。 foo的第三个实例是sizeof运算符的操作数,因此它的类型不再转换,sizeof返回分配给数组的字节数。 将其与sizeof p的结果进行比较,后者返回分配给指针的字节数。

    每当有人告诉你“数组只是一个指针”时,他们就会从上面引用的标准中剔除这一部分。 数组不是指针,指针不是数组; 但是,在许多情况下,您可以将数组视为指针,并且可以将指针视为数组。 “p”可以替换为第6,7和8行中的“foo”。但是,它们不能作为sizeof或&的操作数互换。

    编辑 :顺便说一下,作为函数参数,

     void foo(int *a); 

     void foo(int a[]); 

    是等价的。 “a []”被解释为“ * a”。 请注意,这适用于函数参数。

    看这里:

    2.2:但我听说char a []与char * a相同。

    我同意sepp2k的回答和Mark Rushakoff的comp.lang.c常见问题报价。 让我在两个声明和一个共同陷阱之间添加一些重要的区别。

    最后,考虑这个陷阱。 如果你有一个C文件

     int a[10]; 

    在另一个

     extern int *a; a[0] = 42; 

    文件将编译和链接没有错误,但代码将不会做你所期望的; 它可能会因空指针赋值而崩溃。 原因是在第二个文件中a是一个指针,其值是第一个文件a[0] ,即最初为0。

    在你的例子中有两个a和两个b。

    作为参数

     void foo(int *a) { a[98] = 0xFEADFACE; } void bar(int b[]) { *(b+498) = 0xFEADFACE; } 

    a和b属于同一类型:指向int的指针。

    作为变量

     int *a; int b[10]; 

    不是同一时间。 第一个是指针,第二个是数组。

    数组行为

    数组(变量与否)在大多数上下文中隐式转换为指向其第一个元素的指针。 在C中没有完成的两个上下文是sizeof和&参数的参数; 在C ++中,有一些与参考参数和模板有关。

    我写了一个变量或不变量,因为转换不仅仅针对变量,一些例子:

     int foo[10][10]; int (*bar)[10]; 

    一些历史

    在B中,C的直接祖先,相当于

     int x[10]; 

    有我们写的当前C的影响

     int _x[10]; int *x = &_x; 

    即它分配了内存并初始化了一个指向它的指针。 有些人似乎误以为在C中它仍然是真的。

    在NB中 – 当C不再是B但尚未被称为C – 时,有一段时间是指针被声明

     int x[]; 

     int foo[10]; 

    会有当前的意思。 函数参数的调整是该时间的残差。

    a和b是同一类型吗?

    是。 [编辑:我应该澄清:函数foo的参数a与函数栏的参数b的类型相同。 两者都是指向int的指针。 main中的局部变量a与int中的局部变量b的类型相同。 两者都是整数的数组(实际上它们不是同一类型,因为它们的大小不同。但两者都是数组)。

    int *是否在编译器内部处理与int a []相同的类型?

    通常不是。 例外情况是当你将foo bar[]作为参数写入函数时(就像你在这里做的那样),它会自动变成foo *bar

    然而,在声明非参数变量时,存在很大差异。

     int * a; /* pointer to int. points nowhere in paticular right now */ int b[10]; /* array of int. Memory for 10 ints has been allocated on the stack */ foo(a); /* calls foo with parameter `int*` */ foo(b); /* also calls foo with parameter `int*` because here the name b basically is a pointer to the first elment of the array */ 

    不,他们不一样! 一个是指向int的指针,另一个是100个int的数组。 是的,他们是一样的!

    好的,我会试着解释这种愚蠢。

    * a和a [100]对于你正在做的事情基本相同。 但是如果我们仔细研究编译器的内存处理逻辑,我们所说的是:

    两者都是指针 。 而且你的代码可以对它们进行相同的处理,并在你想要的那些指针附近践踏内存。 但是, a[100]是在编译时分配的指针的连续内存,而* a只分配指针,因为它不知道你什么时候需要内存(运行时内存噩梦)。

    所以, 谁关心 ,对吧? 好吧,某些function,如sizeof()关心。 sizeof(a)将为*aa[100]返回不同的答案。 这也将在function上有所不同。 在这个函数的情况下,编译器知道差异,所以你也可以在代码中使用它,对于循环,memcpy等等。继续,试试。

    这是一个很大的问题,但我在这里给出的答案就是这个。 编译器知道细微差别,它会生成大多数时候看起来相同的代码,但在重要时会有所不同。 由你来找出* a或[100]对cimpiler意味着什么,以及它将以何种方式对待它。 它们可以有效地相同,但它们不一样。 更糟糕的是,你可以通过调用你喜欢的function来改变整个游戏。

    Phew ……难道现在像c#这样的托管代码如此热门?

    编辑: 我还应该补充一点,你可以做*a_p = X ,但尝试用你的一个数组做到这一点! 数组与指针一样使用内存,但它们无法移动或resize。 *a_p这样的指针可以指向不同的东西。

    为了简单解释一下,我会把帽子扔进戒指:

    因此,在许多情况下,由于数组与指向其第一个元素的指针之间有意的特殊关系,同一段代码可以对未分配为数组的数组和连续的内存块进行操作。 然而, 它们是不同的类型,并且它们在某些情况下的行为有所不同,例如,指向数组的指针与指向指针的指针完全不同。

    这是一个最近的SO问题,触及指针到数组与指针到指针的问题: C中“abc”和{“abc”}之间的区别是什么?

    如果你有一个指向字符数组的指针(并希望获得该数组的大小),你不能使用sizeof(ptr),而是必须使用strlen(ptr)+1!

      以上就是c/c++开发分享如何在C编译器内部处理数组和指针类型? (int * a;与int a ;)相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。

      本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

      ctvol管理联系方式QQ:251552304

      本文章地址:https://www.ctvol.com/c-cdevelopment/549641.html

      (0)
      上一篇 2021年1月14日
      下一篇 2021年1月14日

      精彩推荐