CG@CPPBLOG

/*=========================================*/
随笔 - 76, 文章 - 39, 评论 - 137, 引用 - 0
数据加载中……

对数组名取地址是什么?

这两天有人问以下有什么代码有什么不同?

1 int array[100];
2 
3 memset(array,  0, sizeof(array));
4 memset(&array, 0, sizeof(array));

第3行和第4行有什么不同吗?其实从效果上来说是一样的,但是这里要注意 array 和 &array 的类型是不同的。array 相当于 &array[0],而 &array 是一个指向 int[100] 的指针,类型是 int(*)[100]。
以下代码可以看出这个不同:

 1 #include <stdio.h>
 2 
 3 int main()
 4 {
 5         int array[100= {012};
 6         typedef int (*ARRAY)[100];
 7         int* p1 = array;
 8         ARRAY p2 = &array;
 9         //int* p3 = &array;     //这样编译错误
10         
11         printf("p1 = 0x%08X\n", p1);
12         printf("p2 = 0x%08X\n", p2);
13         printf("p1[2] = %d\n", p1[2]);
14         printf("p2[2] = %d\n", p2[2]);
15         printf("(*p2)[2] = %d\n", (*p2)[2]);
16         
17         getchar();
18         return 0;
19 }

运行结果可能是:

p1 = 0x0022FDF8
p2 
= 0x0022FDF8
p1
[2] = 2
p2
[2] = 2294040
(*p2)
[2] = 2



posted on 2008-04-04 20:06 cuigang 阅读(10148) 评论(21)  编辑 收藏 引用 所属分类: C/C++

评论

# re: 对数组名取地址是什么?  回复  更多评论   

不错,受教了。
2008-04-05 10:36 | lovedday

# re: 对数组名取地址是什么?  回复  更多评论   

我一直以为array就等于&array,因为我记得对数组名进行&操作后的值与数组名一样,于是自以为array等于&array,看来我还没有理解其中的含义.
谢谢.
2008-04-05 10:42 | passerby

# re: 对数组名取地址是什么?  回复  更多评论   

地址一样,解释方式不一样.
2008-04-05 14:07 | hi

# re: 对数组名取地址是什么?  回复  更多评论   

....把数组名当个指针不就很好理解了吗
2008-04-05 14:50 | Santa

# re: 对数组名取地址是什么?[未登录]  回复  更多评论   

@Santa
仅仅认为数组名是个指针,是不行的。首先 array 不是一个变量,对它取地址是件很费解的事情,它并不在内存中,可能只是一个链接时常量。另外,即便 array 是一个变量,存在内存中,它的地址等于自己的值,恐怕更加头大。
2008-04-05 19:28 | cuigang

# re: 对数组名取地址是什么?  回复  更多评论   

1 int array[100];
2
3 memset(array, 0, sizeof(array));
4 memset(&array, 0, sizeof(array));

第3行和第4行其实是一样的,只是不同的写法而已。array OR &array,编译器取的都是数组首地址(即数组名隐式转换成pointer类型)。而不能说array 和 &array是两种不能的类型,数组名本来就不是一种类型。

下面一个相似的例子:
#include <cstdio>

void func(char* str)
{
puts(str);
}

int main(void)
{
func("Normal called..");
(*********************************func)("No ploblem called by this way.");//typeof(func)?
(*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&func)("No ploblem, Just a joke.");//typeof(func)?
return 0;
}

=================================================
D:\workspace\test>g++ test0b.cc -o test0b.exe

D:\workspace\test>test0b
Normal called..
No ploblem called by this way.
No ploblem, Just a joke.

个人意见,高手勿笑。
2008-04-06 16:17 | Xshl5

# re: 对数组名取地址是什么?  回复  更多评论   

非一般人啊!!@Xshl5
2008-04-07 22:50 | 超人

# re: 对数组名取地址是什么?  回复  更多评论   

@Xshl5
@超人

关于函数类型和数组类型的问题虽然表现不一致,但是原因是一样的,因为说明比较长,我重新开一文说明:

http://www.cppblog.com/cuigang/archive/2008/04/07/46464.html

至于数组名和函数名确实分别是数组类型和函数类型的问题,你可以去看C++ 标准 ISO14882。




2008-04-07 22:53 | cuigang

# re: 对数组名取地址是什么?  回复  更多评论   

这个其实是C语言的特性.
C语言中设计的参数传递全部是值传递,除非你声明了&引用传递,他才会传递至.
但是又一个例外就是数组传递的时候是只传首地址的,当时设计的时候考虑了性能问题.array和&array表示的效果是一样的.
这个在Poniters On C里面有详细的描述.
2008-04-09 01:12 | egmkang

# re: 对数组名取地址是什么?  回复  更多评论   

@Xshl5
你可能说的是狭义的“类型”,只包括int、char、float、double等。
照这个说法 int* 都不能算是一种类型,因为:
int* x, y;
该声明实际上得到整型指针变量x和整型变量y,如果int*算是一种类型的话,那么y也应该被声明为int*型。所以一般这样写:
int *x, y;
或者干脆分开:
int* x;
int y;

在赋值语句中,不管等号左边还是等号右边的表达式都是有类型的。要想赋值语句合法,等号右边的类型必须跟等号左边的类型相同(强制转换后相同也算),或者能够隐式转换到左边。

int array[100] = {0, 1, 2}; //array的类型是 int [100]
int* p1 = array; //int[100]可以隐式转换到 int*
int* p2 = &array; //错误!无法从“int (*)[100]”转换为“int *”

实际上array返回的是array[0]的地址,我们可以用*(array + 1)来获得array[1]的值,不用*array++是因为array为右值。

由于int[100]能隐式转换到int*,某些书籍在未经考查的情况下直接说数组名array就是int*类型的,array的类型跟p1类型是一样的。照这种说法,&array就应该是int**类型的。但是:
int** p3 = &p1; //正确!&p1为int**类型。
int** p4 = &array; //错误!无法从“int (*)[100]”转换为“int **”

为了跟&array的类型匹配,LZ自己定义了一个类型:
typedef int (*ARRAY)[100];
ARRAY p2 = &array;

这种做法非常聪明,学习了。
2008-04-10 00:12 | ww

# re: 对数组名取地址是什么?  回复  更多评论   

@ww
类型应该是指:在使用前有明确定义的类型,包括C++内置类型,标准库类型,用户自定义类型。

int* x,y; //对象(变量)x,y是指针类型(int *)
int array[100]; //int[100]是编译器为了方便而显示的“伪”类型,当数组名被使用时,编译器给他转换成适当的类型。
*array=0; //数组名隐式转换成指针类型(int*)
*(array+1)=sizeof(array); //后一个数组名不再转换成指针类型,array[1]=400

typedef int[100] array; //error,int[100] not a typename
typedef int array[100]; //ok
array arr;
所以个人比较倾向于数组名(而不是int[100])隐式转换成指针类型(int*),在很多时候数组名跟指针是等价的。
int array[100]; //当然为了理解方便,去掉变量名int[100]可以看作是array的类型。在char *strncpy(char *,const char *,size_t)中,可以把函数名strncpy的类型看作是char* (char *,const char *,size_t),事实上,编译器在报错时就是这样显示的。注意char* (char *,const char *,size_t)是函数返回char*指针,和char (*)(char *,const char *,size_t)指向函数的指针,该函数返回char 不一样。
2008-04-10 17:00 | Xshl5

# re: 对数组名取地址是什么?  回复  更多评论   

@Xshl5
请注意,int* x, y;实际上声明了一个 int* 类型的变量 x 和一个 int 类型的变量 y ,而不是两个 int* 类型的变量。试着编译以下语句:
int z = 10;
int* x, y;
x = &z; //正确!x为 int* 类型
y = z; //正确!y为 int 类型
x = y; //错误!无法从 int 转换为 int*
y = &z; //错误!无法从 int* 转换为 int
2008-04-10 18:20 | ww

# re: 对数组名取地址是什么?[未登录]  回复  更多评论   

@Xshl5
关于“类型”。

我们知道对于以下函数原型:
void fn(int x, double y);
可以省略掉参数名,仅用参数类型来做函数声明,即:
void fn(int, double);

而以下代码是能通过编译的:
void Print(int [2], int); //注意此处用int[2]作为参数类型!

void _tmain(void)
{
int array[2] = {1, 2};
Print(array, 2);
}

void Print(int iArray[2], int x)
{
//do something
}//end of code

是否可以说明编译器是认可 int[2] 类型的呢?
2008-04-10 20:27 | iwong

# re: 对数组名取地址是什么?[未登录]  回复  更多评论   

@Xshl5

看来你还比较执着。 int [N] 是类型是无容置疑的。建议看 ISO 14882
2008-04-10 22:24 | cuigang

# re: 对数组名取地址是什么?  回复  更多评论   

楼主,是否可以通过下面的代码来说明 array和&array的类型呢?

int array[100];
memset(array, 0, sizeof(array));

printf("sizeof(array)=%d, sizeof(&array)=%d\n", sizeof(array), sizeof(&array));

2008-04-21 23:36 | perddy

# re: 对数组名取地址是什么?[未登录]  回复  更多评论   

@perddy

你想说明什么呢?
2008-04-21 23:41 | cuigang

# re: 对数组名取地址是什么?  回复  更多评论   

@cuigang

由于习惯使用sizeof来判断一个变量的类型,而实验了一下,发现sizeof(array)和 sizeof(&array)结果一样,都为400,所以一开始认为这两个的类型是一样的。
回头又查了下C专家编程(第9章),其中有说:
表达式中的数组名被编译器当作一个指向该数组第一个元素的指针。也即array为一指针类型。
另外注释中有提到:在使用sizeof()、&操作符取数组指针、数组为一字符串常量初始值的情况下,对数组的引用不能用指向该数组第一个元素的指针来代替。

因此,楼主说的是正确的,array和&array类型是不一样的。array为一个指针,而&array是指向数组int [100]的指针。下面的代码(计算步长)也可以证明这个结论:

printf("array=%p, array+1=%p\n", array, array+1);
printf("&array=%p, &array+1=%p\n", &array, &array+1);

结果为:
array=0012FDF0, array+1=0012FDF4
&array=0012FDF0, &array+1=0012FF80
2008-04-22 14:30 | perddy

# re: 对数组名取地址是什么?  回复  更多评论   

C90规定:



Except when it is the operand of the sizeof operator or the unary & operator, or is a character string literal used to initialize an array of character type. or is a wide string literal used to initialize an array with element type compatible with wchar-t, an lvalue that has type “array of type” is converted to an expression that has type “pointer to type” that points to the initial element of the array object and is not an lvalue.



除了作为sizeof、&及用于初始化字符数组的字符串字面量等几种情况外,一个具有数组类型的左值表达式被转换为指向数组首元素的右值指针。这是一个隐式转换过程。这个条款不仅规定了首元素地址这个数值结果,还规定了转换结果的类型:元素指针。
2012-05-20 10:26 | nux

# re: 对数组名取地址是什么?  回复  更多评论   

谢谢了,问题得到了解决
2012-06-30 14:19 | 大笨兔

# re: 对数组名取地址是什么?  回复  更多评论   

谢谢楼主的讲解、分享和大家的讨论!
2015-06-07 13:15 | 孙磊磊

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理