几个数组问题

最近在学习C语言。发现源代码里有如下几种定义:

(char *) p[N];

char *p[N];

char (*p)[N];

一开始觉得应该都一样吧,仔细研究了一下,竟然大不相同,而且还是C的难点之一。

下面附上解释:

(char *) p[N]; 把p[N]强制转化成指向char型的指针;

char *p[N]; 一个char指针数组,包含N个指针;

char (*p)[N]; 一个指向char[N]的指针。


char *p和char p[]赋值时的区别

char *s="abc";
char str[]="abccd";

经过反汇编,得到:

char *str="abc";   的汇编代码

mov         dword ptr [ebp-4],offset string "abc" (0046f034)

 

char str[ ]="abccd"; 的汇编代码

mov         eax,[string "abccd" (0046f02c)]
mov         dword ptr [ebp-0Ch],eax
mov         cx,word ptr [string "abccd"+4 (0046f030)]
mov         word ptr [ebp-8],cx

 

1、char *s="abc";

     看这个赋值:

     右边,是"abc",是个字符串常量,存在于内存某处(我的机器上是ds:0x0046f034),程序员不知道,编译器安排的,也没必要知道(当然,这个赋值之后,程序员就知道并能控制这个串了)。字符串常量所在内存是只读的。

     左边,字符指针s,赋值时候,把地址ds:0x0046f034的偏移地址("abc"所在),存放到指针变量s(其地址为 ds:0x0046f034)中。程序员能完全控制的内存只是指针变量所占据的这四个字节内存,只能改变该指针的指向,至于其指向的内存能不能写,那就看程序了,本程序是指向的只读内存,不能写!

2、char str[]="abccd";

     再看这个赋值:

     右边,和上面类似,是"abccd",也是个字符串常量,存在于内存某处(是ds:0x0046f02c),程序员不知道,编译器安排的(这个赋值之后,程序员还是不知道这个常量在哪里,因为并没有用指针指向这块内存,这和上面不同)。该字符串常量所在内存也是只读的。


关于数组形参的真相

  (2011-07-21 17:00:43)
  分类: C语言基础

写了几个函数,想通过数组的方式传参,搞了半天终于发现,所谓C/C++中的"数组形参"是根本不存在的,在数组传入函数时,实际上仅仅传入了指向实参数组的首地址的指针。这也难怪我企图使用sizeof求形参所占内存个数的时候,却得到了一个指针的长度。

void average(int arr[12]);// 实际形参arr是一个int*

int anArray[]={1,2,3};//声明了一个有三个元素的数组

const int anArraySize = sizeof(anArray)/sizeof(anArray[0]); // =3

average(anArray);

好吧,业内专业人士对这种数组到指针的自动转换的行为叫做“退化”。即数组退化成指向其首元素地址的指针。同样,当函数作为形参被进行传递时,也会自动退化。不过,和数组退化时丢失边界不同,一个退化的函数同样具有良好的感知能力,可以保持其参数类型和返回类型。

由于数组形参中数组边界被忽略了,因此通常在声明时最好将其省略。

void average(int arr[]); //忽略边界,形参仅仅是一个int*

当然,如果数组边界的精确数值非常重要,并且希望函数只接受含有特定数量元素的数组,可以考虑使用一个引用形参:

void average(int (&arr)[12]);//引用调用,区别于传值调用

这时,函数就只能接受大小为12的整形数组;

average(anArray);//错误!anArray是一个int[3];

在C++中可以使用模板来帮助代码的泛化:

template<int n>

void average(int (&arr)[n]);//让编译器来替我们推导出n的值

但是,更为传统的做法还是将数组的大小作为形参明确的传入函数:

vodi average(int arr[],int size);

当然,我们也可以使用模板:

template<int n>

inline void average(int (&arr)[n])

{

    average_n(arr,n);

}

从以上讨论中我们可以得知,使用数组作为函数形参的最大问题在于:数组的大小必须以形参的方式显式的编码,并以单独的实参传入,或者在数组内部以一个结束符值作为指示。另一个困难在于,不管数组是如何声明的,一个数组通常是通过指向其首元素地址的指针进行操纵。如果那个指针作为实参传递给函数,我们前面声明引用形参的技巧将无济于事。

int *anArray2 = new int [anArraySize];

average(anArray2);//错误,不能将int *初始化为int (&)[n]

average_n(anArray, anArraySize);//没问题;

出于这些原因,常采用某种标准“容器”(通常是vector或者string)来代替对数组的大多数传统用法,并且经常应该优先考虑使用标准容器。

 

从本质上来说,对维数组形参并不比一维数组来的困难,但他们看上去更有挑战性。

void process(int arr[10][20]);

和一维数组一样,形参并不是一个数组,而是一个指向数组首元素地址的指针。不过,多维数组是数组的数组,因此形参是一个指向数组的指针。

 void process(int (*arr)[20];//一个指针,指向一个具有20个int元素的数组;

注意,第二个边界没有退化,否则将无法对形参执行指针算数。但如前所述,让代码的读者清楚的知道你期望的实参是一个数组,这通常是一个好主意:

void process(int arr[][20]); //仍然是一个指针,但更清晰。

 对于多维数组形参的有效性处理往往退化成一个低级的编程练习,此时需要程序员取代编译器执行索引计算。

void process_2d(int *a, int n, int m)

{

    for (int i=0; i<n; ++i)

        for (int j=0; j<m ++j)

            a[i*m + j] = 0;

}

 同样,我们可以借助模板

template<int n, int m>

inline void process(int (&arr)[n][m])

{

    process_2d(&arr[0][0], n, m);

}


     左边,字符数组str,赋值时候,把地址ds:0x0046f02c("abccd"所在)所指内存中的内容,复制到字符数组str开始(其地址为 ds:0x0013ff74)的内存中,每复制一个字符都会开辟一个字节(char型变量占1字节)内存来存放这个字符(这也是实现了数组元素个数的动态确定)。从字符数组str开始的这部分存放这些字符的内存是程序员可以完全控制的,可读写,因此在这些内存写当然是没有问题的!


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值