小冲哥线下课堂


Join the forum, it's quick and easy

小冲哥线下课堂
小冲哥线下课堂
Would you like to react to this message? Create an account in a few clicks or log in to continue.
搜索
 
 

结果按:
 


Rechercher 高级搜索

关键词

最新主题
» 新人来报道了。。。。。。。。。
 2级还在担心吗?进来就给你吃定心丸!<3>--数组和指针问题 Empty周日 十月 14, 2012 3:37 pm 由 15178959183

» 新人报道~我不是吐槽贴。。。
 2级还在担心吗?进来就给你吃定心丸!<3>--数组和指针问题 Empty周三 五月 16, 2012 5:38 pm 由 萝卜爱喝水

» 课后作业1:编一个程序
 2级还在担心吗?进来就给你吃定心丸!<3>--数组和指针问题 Empty周三 五月 09, 2012 11:24 pm 由 廖承启

» C语言初级课堂2--"Hello world"
 2级还在担心吗?进来就给你吃定心丸!<3>--数组和指针问题 Empty周六 五月 05, 2012 12:52 am 由 醉之殇

» 新人来报道了。。。。。。
 2级还在担心吗?进来就给你吃定心丸!<3>--数组和指针问题 Empty周四 五月 03, 2012 8:50 pm 由 sunquanwen

» 新人报到,字怎样就大了
 2级还在担心吗?进来就给你吃定心丸!<3>--数组和指针问题 Empty周三 四月 25, 2012 7:50 am 由 wanghong

» 果断的。来报道啦。。。嘿嘿,我是新手哦
 2级还在担心吗?进来就给你吃定心丸!<3>--数组和指针问题 Empty周六 四月 21, 2012 2:41 pm 由 ゛﹏小冲哥ゞ ✿『人事精英』✿李帅

» 指针数组与行指针的异同
 2级还在担心吗?进来就给你吃定心丸!<3>--数组和指针问题 Empty周一 四月 09, 2012 3:59 pm 由 *小耳朵*

» 行指针q引用数组元素的方法
 2级还在担心吗?进来就给你吃定心丸!<3>--数组和指针问题 Empty周一 四月 09, 2012 3:46 pm 由 *小耳朵*

十一月 2024
周一周二周三周四周五周六周日
    123
45678910
11121314151617
18192021222324
252627282930 

日历 日历

合作伙伴
免费论坛

免費論壇




2级还在担心吗?进来就给你吃定心丸!<3>--数组和指针问题

2 posters

向下

 2级还在担心吗?进来就给你吃定心丸!<3>--数组和指针问题 Empty 2级还在担心吗?进来就给你吃定心丸!<3>--数组和指针问题

帖子  齿轮Gear 周六 三月 17, 2012 3:41 pm

[center]
1 我在一个源文件中定义了char a[6], 在另一个中声明了extern char
*a 。为什么不行?
你在一个源文件中定义了一个字符串, 而在另一个文件中定义了指向字符的
指针。extern char * 的申明不能和真正的定义匹配。类型T 的指针和类型T 的数
组并非同种类型。请使用extern char a[ ]。

2 可是我听说char a[ ] 和char *a 是一样的。
并非如此。(你所听说的应该跟函数的形式参数有关) 数组不
是指针。数组定义char a[6] 请求预留6 个字符的位置, 并用名称“a” 表示。也就
是说, 有一个称为“a” 的位置, 可以放入6 个字符。而指针申明char *p, 请求一个
位置放置一个指针, 用名称“p” 表示。这个指针几乎可以指向任何位置: 任何字符
和任何连续的字符, 或者哪里也不指。
一个图形胜过千言万语。声明
char a[] = "hello";
char *p = "world";
将会初始化下图所示的数据结果:
+---+---+---+---+---+---+
a: | h | e | l | l | o |\0 |
+---+---+---+---+---+---+
+-----+ +---+---+---+---+---+---+
p: | *======> | w | o | r | l | d |\0 |
+-----+ +---+---+---+---+---+---+
根据x 是数组还是指针, 类似x[3] 这样的引用会生成不同的代码。认识到这
一点大有裨益。以上面的声明为例, 当编译器看到表达式a[3] 的时候, 它生成代码
从a 的位置开始跳过3 个, 然后取出那个字符. 如果它看到p[3], 它生成代码找到
“p” 的位置, 取出其中的指针值, 在指针上加3 然后取出指向的字符。换言之, a[3]
是名为a 的对象(的起始位置) 之后3 个位置的值, 而p[3] 是p 指向的对象的3 个
位置之后的值. 在上例中, a[3] 和p[3] 碰巧都是’l’ , 但是编译器到达那里的途径不
尽相同。本质的区别在于类似a 的数组和类似p 的指针一旦在表达式中出现就会
按照不同的方法计算, 不论它们是否有下标。

3 那么, 在C 语言中“指针和数组等价” 到底是什么意思?
在C 语言中对数组和指针的困惑多数都来自这句话。说数组和指针“等价”
不表示它们相同, 甚至也不能互换。它的意思是说数组和指针的算法定义可以用
指针方便的访问数组或者模拟数组。
特别地, 等价的基础来自这个关键定义:
一个T 的数组类型的左值如果出现在表达式中会蜕变为一个指向数
组第一个成员的指针(除了三种例外情况); 结果指针的类型是T 的
指针。
这就是说, 一旦数组出现在表达式中, 编译器会隐式地生成一个指向数组第一
个成员地指针, 就像程序员写出了&a[0] 一样。例外的情况是, 数组为sizeof 或&
操作符的操作数, 或者为字符数组的字符串初始值。
作为这个这个定义的后果, 编译器并那么不严格区分数组下标操作符和指
针。在形如a[i] 的表达式中, 根据上边的规则, 数组蜕化为指针然后按照指针变量
的方式如p[i] 那样寻址, 如问题6.2 所述, 尽管最终的内存访问并不一样。如果你
把数组地址赋给指针:
p = a;
那么p[3] 和a[3] 将会访问同样的成员。
4 那么为什么作为函数形参的数组和指针申明可以互换呢?
这是一种便利。
由于数组会马上蜕变为指针, 数组事实上从来没有传入过函数。允许指针参
数声明为数组只不过是为让它看起来好像传入了数组, 因为该参数可能在函数内
当作数组使用。特别地, 任何声明“看起来象” 数组的参数, 例如
void f(char a[])
{ ... }
在编译器里都被当作指针来处理, 因为在传入数组的时候,那正是函数接收到
的.
void f(char *a)
{ ... }
这种转换仅限于函数形参的声明, 别的地方并不适用。如果这种转换令你困
惑, 请避免它; 很多程序员得出结论, 让形参声明“看上去象” 调用或函数内的用法
所带来的困惑远远大于它所提供的方便。

5 如果你不能给它赋值, 那么数组如何能成为左值呢?
ANSI C 标准定义了“可变左值”, 而数组不是。

6 现实地讲, 数组和指针地区别是什么?
数组自动分配空间, 但是不能重分配或改变大小。指针必须明确赋值以指向
分配的空间(可能使用malloc), 但是可以随意重新赋值(即, 指向不同的对象), 同
时除了表示一个内存块的基址之外, 还有许多其它的用途。
由于数组和指针所谓的等价性, 数组和指针经常看起来可以互
换, 而事实上指向malloc 分配的内存块的指针通常被看作一个真正的数组(也可以
用[ ] 引用)。

7 有人跟我讲, 数组不过是常指针。
这有些过度单纯化了。数组名之所以为“常数” 是因为它不能被赋值, 但是数
组不是指针。
8 我遇到一些“搞笑” 的代码, 包含5["abcdef"] 这样的“表达式”。
这为什么是合法的C 表达式呢?
是的, 弗吉尼亚1, 数组和下标在C 语言中可以互换。这个奇怪的事实来自数
组下标的指针定义, 即对于任何两个表达式a 和e, 只要其中一个是指针表达式而
另一个为整数, 则a[e] 和*((a)+(e)) 完全一样。这种交换性在许多C 语言的书中
被看作值得骄傲的东西, 但是它除了在混乱C 语言竞赛之外, 其实鲜有用武之地。



9 既然数组引用会蜕化为指针, 如果arr 是数组, 那么arr 和&arr 又
有什么区别呢?
区别在于类型。
在标准C 中, &arr 生成一个“T 型数组” 的指针, 指向整个数组。在ANSI 之
前的C 中, &arr 中的& 通常会引起一个警告, 它通常被忽略。在所有的C 编译器
中, 对数组的简单引用(不包括& 操作符)生成一个T 的指针类型的指针, 指向数组
的第一成员。

10 我如何声明一个数组指针?
通常, 你不需要。当人们随便提到数组指针的时候, 他们通常想的是指向它的
第一个元素的指针。
考虑使用指向数组某个元素的指针, 而不是数组的指针。类型T 的数组蜕变
成类型T 的指针, 这很方便; 在结果的指针上使用下标或增量就可
以访问数组中单独的成员。而真正的数组指针, 在使用下标或增量时, 会跳过整
个数组, 通常只在操作数组的数组时有用—— 如果还有一点用的话。
如果你真的需要声明指向整个数组的指针, 使用类似“int (*ap)[N];” 这样的声
明。其中N 是数组的大小。如果数组的大小未知, 原则上可以省略
N, 但是这样生成的类型, “指向大小未知的数组的指针”, 毫无用处。


11 我如何在运行期设定数组的大小?我怎样才能避免固定大小的数
组?
由于数组和指针的等价性, 可以用指向malloc 分配的内存的指
针来模拟数组。执行
#include <stdlib.h>
int *dynarray;
dynarray = malloc(10 * sizeof(int));
以后(如果malloc 调用成功), 你可以象传统的静态分配的数组那样引用
dynarry[i] (i 从0 到9)。唯一的区别是sizeof 不能给出“数组” 的大小。

12 我如何声明大小和传入的数组一样的局部数组?
直到最近以前, 你都不能; C 语言的数组维度一直都是编译时常数。但是, C99
引入了变长数组(VLA), 解决了这个问题; 局部数组的大小可以用变量或其它表达
式设置, 可能也包括函数参数。( gcc 提供参数化数组作为扩展已经有些时候了。)
如果你不能使用C99 或gcc, 你必须使用malloc(), 并在函数返回之前调用free()。

13 我该如何动态分配多维数组?
传统的解决方案是分配一个指针数组, 然后把每个指针初始化为动态分配的
“列”。以下为一个二维的例子:
#include <stdlib.h>
int **array1 = malloc(nrows * sizeof(int *));
for(i = 0; i < nrows; i++)
array1[i] = malloc(ncolumns * sizeof(int));
当然, 在真实代码中, 所有的malloc 返回值都必须检查。你也可以使用
sizeof(*array1) 和sizeof(**array1) 代替sizeof(int *) 和sizeof(int)。
你可以让数组的内容连续, 但在后来重新分配列的时候会比较困难, 得使用一
点指针算术:
int **array2 = malloc(nrows * sizeof(int *));
array2[0] = malloc(nrows * ncolumns * sizeof(int));
for(i = 1; i < nrows; i++)
array2[i] = array2[0] + i * ncolumns;
6在两种情况下, 动态数组的成员都可以用正常的数组下标arrayx[i][j] 来访问
(for 0 <= i <nrows 和0 <= j <ncolumns)。
如果上述方案的两次间接因为某种原因不能接受, 你还可以同一个单独的动
态分配的一维数组来模拟二维数组:
int *array3 = malloc(nrows * ncolumns * sizeof(int));
但是, 你现在必须手工计算下标, 用array3[i * ncolumns + j] 访问第i, j 个成
员。使用宏可以隐藏显示的计算, 但是调用它的时候要使用括号和逗号, 这看起来
不太象多维数组语法, 而且宏需要至少访问一维。。
另一种选择是使用数组指针:
int (*array4)[NCOLUMNS] = malloc(nrows * sizeof(*array4));
但是这个语法变得可怕而且运行时最多只能确定一维。
当然, 使用这些技术, 你都必须记住在不用的时候释放数组(这可能需要多个
步骤;)。而且你可能不能混用动态数组和传统的静态分配数组。
最后, 在C99 中你可以使用变长数组。
所有这些技术都可以延伸到三维或更多的维数。


14 有个灵巧的窍门: 如果我这样写int realarray[10]; int *array =
&realarray[-1]; 我就可以把“array” 当作下标从1 开始的数组。
尽管这种技术颇有吸引力(而且在《Numerical Recipes in C》一书的旧版中
使用过), 它却不完全符合C 的标准。指针算术只有在指针所指的内存块之内, 或
者指向虚构的“终结” 元素后的一个时才有定义; 否则, 即使指针并未解参考, 其行
为仍然是未定义的。如果在用偏移作下标运算的时候生成了非法地址(可能因为
地址在经过某个内存段之后“回绕”), 则这段代码会失败。

15 当我向一个接受指针的指针的函数传入二维数组的时候, 编译器报
错了。
数组蜕化为指针的规则 不能递归应用。数组的数组(即C 语
言中的二维数组) 蜕化为数组的指针, 而不是指针的指针。数组指针常常令人困
惑, 需要小心对待;
如果你向函数传递二位数组:
int array[NROWS][NCOLUMNS];
f(array);
那么函数的声明必须匹配:
void f(int a[][NCOLUMNS])
{ ... }
或者
void f(int (*ap)[NCOLUMNS]) /* ap 是个数组指针*/
{ ... }
在第一个声明中, 编译器进行了通常的从“数组的数组” 到“数组的指针” 的
隐式转换(参见问题6.3 和6.4); 第二种形式中的指针定义显而易见。因为被调函
数并不为数组分配地址, 所以它并不需要知道总的大小, 所以行数NROWS 可以省
略。但数组的宽度依然重要, 所以列维度NCOLUMNS (对于三维或多维数组, 相
关的维度) 必须保留。
如果一个函数已经定义为接受指针的指针, 那么几乎可以肯定直接向它传入
二维数组毫无意义。

16 我怎样编写接受编译时宽度未知的二维数组的函数?
这并非易事。一种办法是传入指向[0][0] 成员的的指针和两个维数, 然后“手
工” 模拟数组下标。

void f2(int *aryp, int nrows, int ncolumns)
{ ... array[i][j] is accessed as aryp[i * ncolumns + j] ... }
这个函数可以用问题6.15 的数组如下调用:
f2(&array[0][0], NROWS, NCOLUMNS);
但是, 必须注明的一点是, 用这种方法通过“手工” 方式模拟下标的程序
未能严格遵循ANSI C 标准; 根据官方的解释, 当x >= NCOLUMNS 时, 访问
&array[0][0][x] 的结果未定义。
C99 允许变长数组, 一旦接受C99 扩展的编译器广泛流传以后, VLA 可能是
首选的解决方案。gcc 支持可变数组已经有些时日了。
当你需要使用各种大小的多维数组的函数时, 一种解决方案是象问题6.13 那
样动态模拟所有的数组。

17 我怎样在函数参数传递时混用静态和动态多维数组?
没有完美的方法。假设有如下声明
int array[NROWS][NCOLUMNS];
int **array1; /* 不齐的*/
int **array2; /* 连续的*/
int *array3; /* "变平的" */
int (*array4)[NCOLUMNS];
指针的初始值如问题6.13 的程序片段, 函数声明如下
void f1a(int a[][NCOLUMNS], int nrows, int ncolumns);
void f1b(int (*a)[NCOLUMNS], int nrows, int ncolumns);
void f2(int *aryp, int nrows, int ncolumns);
void f3(int **pp, int nrows, int ncolumns);
其中f1a() 和f1b() 接受传统的二维数组, f2() 接受“扁平的” 二维数组, f3() 接
受指针的指针模拟的数组(参见问题6.15 和6.16), 下面的调用应该可以如愿运行:
f1a(array, NROWS, NCOLUMNS);
f1b(array, NROWS, NCOLUMNS);
f1a(array4, nrows, NCOLUMNS);
f1b(array4, nrows, NCOLUMNS);
f2(&array[0][0], NROWS, NCOLUMNS);
f2(*array, NROWS, NCOLUMNS);
f2(*array2, nrows, ncolumns);
f2(array3, nrows, ncolumns);
f2(*array4, nrows, NCOLUMNS);
f3(array1, nrows, ncolumns);
f3(array2, nrows, ncolumns);
下面的调用在大多数系统上可能可行, 但是有可疑的类型转换, 而且只有动态
ncolumns 和静态NCOLUMNS 匹配才行:
f1a((int (*)[NCOLUMNS])(*array2), nrows, ncolumns);
f1a((int (*)[NCOLUMNS])(*array2), nrows, ncolumns);
f1b((int (*)[NCOLUMNS])array3, nrows, ncolumns);
f1b((int (*)[NCOLUMNS])array3, nrows, ncolumns);
同时必须注意向f2() 传递&array[0][0] (或者等价的*array) 并不完全符合标
准;
如果你能理解为何上述调用可行且必须这样书写, 而未列出的组合不行, 那么
你对C 语言中的数组和指针就有了很好的理解了。
为免受这些东西的困惑, 一种使用各种大小的多维数组的办法是令它们“全
部” 动态分配, 如问题6.13 所述。如果没有静态多维数组—— 如果
18 当数组是函数的参数时, 为什么sizeof 不能正确报告数组的大小?
编译器把数组参数当作指针对待, 因而报告的时指针的大小。

齿轮Gear

帖子数 : 6
注册日期 : 12-03-17
地点 : 湖北武汉

返回页首 向下

 2级还在担心吗?进来就给你吃定心丸!<3>--数组和指针问题 Empty 回复: 2级还在担心吗?进来就给你吃定心丸!<3>--数组和指针问题

帖子  小希 周六 三月 17, 2012 4:01 pm

沙发啊沙发...

小希
版主
版主

帖子数 : 7
注册日期 : 12-03-17

返回页首 向下

返回页首


 
您在这个论坛的权限:
不能在这个论坛回复主题