(define (cuigang) (coding))

(define (coding) (coding))

对数组名取地址是什么?

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

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 阅读(1565) 评论(17)  编辑 收藏 引用 所属分类: C/C++

评论

# re: 对数组名取地址是什么? 2008-04-05 10:36 lovedday

不错,受教了。  回复  更多评论   

# re: 对数组名取地址是什么? 2008-04-05 10:42 passerby

我一直以为array就等于&array,因为我记得对数组名进行&操作后的值与数组名一样,于是自以为array等于&array,看来我还没有理解其中的含义.
谢谢.  回复  更多评论   

# re: 对数组名取地址是什么? 2008-04-05 14:07 hi

地址一样,解释方式不一样.  回复  更多评论   

# re: 对数组名取地址是什么? 2008-04-05 14:50 Santa

....把数组名当个指针不就很好理解了吗  回复  更多评论   

# re: 对数组名取地址是什么?[未登录] 2008-04-05 19:28 cuigang

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

# re: 对数组名取地址是什么? 2008-04-06 16:17 Xshl5

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.

个人意见,高手勿笑。
  回复  更多评论   

# re: 对数组名取地址是什么? 2008-04-07 22:50 超人

非一般人啊!!@Xshl5
  回复  更多评论   

# re: 对数组名取地址是什么? 2008-04-07 22:53 cuigang

@Xshl5
@超人

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

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

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




  回复  更多评论   

# re: 对数组名取地址是什么? 2008-04-09 01:12 egmkang

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

# re: 对数组名取地址是什么? 2008-04-10 00:12 ww

@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;

这种做法非常聪明,学习了。  回复  更多评论   

# re: 对数组名取地址是什么? 2008-04-10 17:00 Xshl5

@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 不一样。  回复  更多评论   

# re: 对数组名取地址是什么? 2008-04-10 18:20 ww

@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  回复  更多评论   

# re: 对数组名取地址是什么?[未登录] 2008-04-10 20:27 iwong

@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] 类型的呢?  回复  更多评论   

# re: 对数组名取地址是什么?[未登录] 2008-04-10 22:24 cuigang

@Xshl5

看来你还比较执着。 int [N] 是类型是无容置疑的。建议看 ISO 14882  回复  更多评论   

# re: 对数组名取地址是什么? 2008-04-21 23:36 perddy

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

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

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

  回复  更多评论   

# re: 对数组名取地址是什么?[未登录] 2008-04-21 23:41 cuigang

@perddy

你想说明什么呢?  回复  更多评论   

# re: 对数组名取地址是什么? 2008-04-22 14:30 perddy

@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
  回复  更多评论   


标题  
姓名  
主页
验证码 *
内容(提交失败后,可以通过“恢复上次提交”恢复刚刚提交的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
[使用Ctrl+Enter键可以直接提交]