1
binux 2017-11-09 00:30:53 +08:00
那 int** p[10] 是数组还是指针?
|
2
AttufliX 2017-11-09 00:31:36 +08:00 via Android 1
指针,指向一个指向 10 个元素的数组的指针
|
3
congeec 2017-11-09 00:33:43 +08:00 2
https://cdecl.org/?q=int+**p%5B10%5D
从右往左读,不过是 p 那块内存里存了 10 个 int ** |
4
n2ex2 2017-11-09 00:35:15 +08:00 via Android 1
**的数组...
|
5
AttufliX 2017-11-09 00:35:38 +08:00 via Android
上面说错了,cdel 查了下是数组,指向 10 个二维指针的数组
|
6
geelaw 2017-11-09 00:36:41 +08:00 via iPhone 1
等同于 typedef int **ptr; ptr p[10];
|
7
n2ex2 2017-11-09 00:39:46 +08:00 via Android
不过数组名本身就是指针,要说它是指针也说得通...
|
8
lianz 2017-11-09 00:42:40 +08:00 2
数组就是指针,指针就是数组,两者本质上都是一个内存地址,可以直接换用,没任何区别。
比如 : 1:int girls[10] = {....}; 可以直接用指针访问访问:int i = *girls; 2:int *p = (int*) malloc (sizeof(int) * 10); 可以直接用数组方式访问 int i = p[0]; |
9
LeonLi 2017-11-09 00:45:00 +08:00
其实 p 的类型是 int **[]
|
10
XiaoxiaoPu 2017-11-09 00:46:19 +08:00 2
|
11
wsy2220 2017-11-09 00:47:28 +08:00 1
数组, 数组的元素是指针
|
12
LeonLi 2017-11-09 00:53:29 +08:00 2
@lianz 你这个看起来类似是因为数组在访问的时候,其实是走的指针的语法糖的形式,但是说不上没有任何区别,最大的区别其实是数组是有自己的类型的,并不能跟指针等价。比如:
1 数组名是不允许重复赋值的,指针可以 int arr[1]; int *p ; arr = xxx ;//error p = xxx://OK 2 数组是和指针还有一个很明显的区别,数组可以使用 sizeof 算出整个数组大小,而指针的 sizeof 只是计算的指针分配空间的大小,以 32 位为例: int arr[10]; int *p = (int*)malloc(sizeof(int)*10); size_t as = sizeof(arr);// 40 size_t ps = sizeof(p);// 4 |
13
hackpro 2017-11-09 00:59:39 +08:00 via iPhone
P 是一个是个元素的数组
数组中每个元素是个指针 指向另外一个 int * |
14
wwqgtxx 2017-11-09 01:23:43 +08:00 2
@LeonLi
@XiaoxiaoPu 对于你们说的 sizeof 的问题插个话,这个地方完全是编译器实现的问题,在你用 int arr[10]这样定义的时候,sizeof(arr)编译器是完全能推断出来 arr 的 size 的,而如果你用动态分配的话,编译器只能知道这个指针本身多大,根本没办法知道这段内存地址的结尾在哪里,因为那个只有 malloc 的实现( glibc/msvcrt/...)才知道 sizeof 运算符本质上就是一个预处理,在最后程序编译完了之后就消失了,被直接替换成具体的值了,所以 sizeof 所能得到的大小一定是可以静态推断的,这也就是对 std 容器进行求 sizeof 基本上毫无意义的原因 |
15
FrankHB 2017-11-09 01:45:24 +08:00 1
连完整的声明都不是,就半坨声明符,还想啥。
p 就是个不知所云的标识符。没上下文姑且当作没声明了,没有对应的实体,无所谓指针数组。 @wwqgtxx 如果只有某几句关键的废话,那么少妖言惑众,老实重修,算是你勉强表现出你楼上都没注意到的上下文相关特(废)色(物)特性的奖赏。但是非要满嘴跑火车……那么接好问题一箩筐: “完全是编译器实现的问题”?意思说换个编译器可以胡来了? 推断……的 size ? Deduce 还是 infer ? 内存?地址?结尾?哪个轮得到编译器管?哪个决定不能让编译器管?( C 艹 14 都钦定允许合并::operand new 了咋就没几个长眼的呢。) 解释什么叫预处理。是不是对 phase of translation 毫无概念? “编译完了”?谁钦点要求编译的? 直接“替换”?βη等价? “大小一定是可以静态推断的”?题主好像没说不能 VLA 吧? 解释什么叫“基本上毫无意义”。不说语病……是不是不懂啥叫 incomplete type ? |
16
LeonLi 2017-11-09 01:56:41 +08:00
@wwqgtxx
首先说一下你说的编译器实现的问题。在 C 语言标准中,对于那些行为可以由编译器自行决定( implementation-defined behavior )和未定义行为( undefined behavior )是有严格区分的。 以 C99 版标准为例,Rule6.5.3.4 规定了 sizeof 的行为( When applied to an operand that has array type, the result is the total number of bytes in the array.),这个是编译器不能自己改变其行为的。其中,编译器可以自行决定的只有 sizeof 的返回值类型 size_t。 另外,回应一下你说的因为数组定义可以在编译器推断出来的问题。在 C99 标准之后,C 语言支持可变长数组 VLA ( variable-length array )。而 VLA 的 size 是在运行期才能确定的,此时,sizof 的结果由运行时(execution time)确定,返回的依然是整个数组的大小。所以你说的那段话是不成立的。 |
18
FrankHB 2017-11-09 02:26:17 +08:00 1
@LeonLi 要科普完的话,引用 Clause 3 和 Clause 4。
你的说法有个会引起误会的地方,实际情况是 size_t 就是法定的 sizeof 表达式求值结果的无符号整数类型(某种意义上这就是 size_t 的首要意义),C 编译器改变不了这个决定;允许实现变更的是 size_t 和其它整数类型的关系。 嘛,VLA 在 C11 算是 optional 了,不过还有-std=gnu11 之类的垫着就是了…… |
19
q397064399 2017-11-09 07:27:07 +08:00
@congeec #3 好顶赞,,c 语言的古怪 声明 确实太烧脑了
|
20
q397064399 2017-11-09 07:29:44 +08:00
@congeec #3 ** 意味着是 指针的指针, 当前指针指向了一个内存地址,,然后另一个指针里面的内容 就是当前指针的内存地址,我理解的对吗? 这样有什么 意义么?
|
21
n2l 2017-11-09 08:48:17 +08:00 via iPhone
指向字符串的指针数组竟然错误?这网站准不准啊?
https://i.loli.net/2017/11/09/5a03a585b80d3.png |
22
n2l 2017-11-09 08:51:28 +08:00 via iPhone
指向字符串的指针数组正确显示了,重新输入一遍就好了。
|
23
limhiaoing 2017-11-09 09:01:46 +08:00 via iPhone 1
要看上下文的
变量声明的时候是数组 作为函数参数的时候是指针 |
24
smol 2017-11-09 09:34:13 +08:00 2
是数组,数值的元素是 int**。int* a[4]是指针数组,int (*p)[4]才是指向数组的指针 ;类推过来,int** a[4]也是数组。
不过十分讨厌这样的用法,问这个的题就像孔乙己问茴字的四种写法一样。除非有意炫耀,实际代码中正常人不会这么用的,真的需要多重指针(大多是仅用 C 的场合),也会借用 typedef 进行明确声明。 C/C++这样晦涩的语法太多,标准委员会的一帮古董也是脑回路有问题,还在不断添加新的语法特性,也是够了。 |
25
congeec 2017-11-09 09:46:12 +08:00
@q397064399 想想一个函数要修改指针本身的值,怎么修改?
#include <stdio.h> #include <assert.h> void fun(int **p) { ....*p = (int *)0x01; } int main() { ....int *p = NULL; ....fun(&p); ....assert((unsigned long)p == 0x01); ....return 0; } |
27
picone 2017-11-09 09:51:29 +08:00 1
数组就是指针
你可以理解成[]只是一个操作符,本质上还是指针 |
28
FrankHB 2017-11-09 11:44:07 +08:00 2
|
29
lianz 2017-11-09 11:59:04 +08:00
@LeonLi && @XiaoxiaoPu
1 数组名是不允许重复赋值的,指针可以 => 你可以把数组看成 const 指针 2 sizeof 问题: => sizeof 才是真正的语法糖,编译器根据类型信息帮你计算出大小而已。 |
30
enenaaa 2017-11-09 11:59:13 +08:00
因为[]优先级比*高, 所以是数组。
|
32
tabris17 2017-11-09 12:10:34 +08:00
数组不就是指针么,滑稽
|
33
Icarooooos 2017-11-09 12:11:18 +08:00 via Android
是数组,推荐看《 C 专家编程》,里面讲的很详细。
|
34
RLib 2017-11-09 12:15:46 +08:00
只能说数组能隐式弱化成指针, 相反指针转数组只能显式转换
|
35
XiaoxiaoPu 2017-11-09 12:32:59 +08:00 via iPhone
看到大神解释的很清楚了之后,还有人一本正经的说数组就是指针,真是尴尬癌都犯了。
|
37
FrankHB 2017-11-09 13:06:08 +08:00 1
|
38
picone 2017-11-09 13:26:46 +08:00
|
39
jiang1234321 2017-11-09 14:01:30 +08:00
@congeec #3 什么时候需要从左往右读,什么时候从右往左读呢?
|
40
msg7086 2017-11-09 14:40:12 +08:00
@picone 数组不是内存地址吧。只是数组的变量名指向了数组的首地址吧。
申请数组,你说的是 new 么,那个返回的是数组的首地址,数组首地址和数组不是一个概念吧。 |
41
LeonLi 2017-11-09 14:41:08 +08:00
@lianz
我觉得不了解 C 语言就不要硬拗了吧,你说了半天都是你自己所谓的理解,自己去翻一下 C 语言标准看看里面怎么规定的很难么? |
42
fcten 2017-11-09 14:52:41 +08:00 2
说数组和指针是同一个东西的……是不是觉得手机和手机号也是同一个东西?
|
43
picone 2017-11-09 15:14:16 +08:00
@msg7086 额..是的. 数组代表的是它的内存的首地址. 最终数组访问跟直接用指针访问,最后都是同一块的数据, 只是操作的形式不同
|
44
crazyneo 2017-11-09 15:43:03 +08:00
@picone 不明白数组和指针区别的,也就基本告别左右值和模板参数类型推演以及之后一连串的特性了,读一读 essential c 也行,不是你理解的那么回事儿。
|
45
lany 2017-11-09 16:02:06 +08:00
这让我想起了一个好玩的东西。
char* p1 char** p2 char*** p3 char**** p4 char***** p5 |
46
zbcwilliam 2017-11-09 16:10:01 +08:00 1
数组;数组元素是指向指针的指针
|
47
FrankHB 2017-11-09 18:56:56 +08:00
@picone 你就这么急着强行加戏,是嫌 LZ “理解”的还不够多不够混乱?
我有些好奇你是怎么把“指针是内存地址,数组也是内存地址”“数组代表的是它的内存的首地址”这类无中生有的笑话大言不惭拿来帮助别人理解的。 |
48
Pyjamas 2017-11-09 19:00:59 +08:00
|
49
Pyjamas 2017-11-09 19:06:41 +08:00
> 指针是内存地址, 数组也是内存地址, 只是操作不同, 最终还是能定位到指定的内存地址
@picone 你的说法其实可以套用到,int 也是二进制数据,float 也是二进制数据,只是操作方式(解释方式)不同,有什么区别吗? 传值也是传数据,传引用也是传数据,只是数据的地址不同,有什么区别吗? |
50
Pyjamas 2017-11-09 19:09:40 +08:00
@Pyjamas
> 传值也是传数据,传引用也是传数据,只是数据的地址不同,有什么区别吗? 改一下:传值也是传数据,传引用也是传数据,只是操作的形式不同,最终还是能定位到指定的内存地址,所以传值=传引用 |
51
picone 2017-11-09 20:34:27 +08:00
@FrankHB #47 好吧,那你说一下 指针放的不是内存地址是啥, 数组的变量名不是内存首地址又是啥, 或者给个传送门? 直接喷人没意思
|
53
nullcc 2017-11-09 22:06:13 +08:00
有一种笨办法,一层层拆开来看:
int **p[10],p 是什么? 1. **p[10]是一个 int 2. *p[10]是一个 int 型指针 3. p[10]是一个指向 int 型指针的指针 4. p 是一个指向 int 型指针的指针的数组 |
54
lrxiao 2017-11-10 00:38:01 +08:00
这问题居然能引来地球。。
|
55
msg7086 2017-11-10 01:16:46 +08:00
@picone
比如说吧。 int a[5]; 这里你问 a 是个什么东西 —— a 是个数组。 你问 a 作为常量读出来的值是多少 —— 值是 a 的首地址。 所有的变量到最后都是内存里的一小块数据,你也不能说整数就是浮点数吧。 |
56
Mitt 2017-11-10 02:13:19 +08:00
|
57
Mitt 2017-11-10 02:14:01 +08:00
|
58
ryd994 2017-11-10 06:46:57 +08:00 via Android
|
60
smol 2017-11-10 09:36:12 +08:00
@FrankHB 倒不是甩锅给委员会,这个问题跟他们也没毛关系。只是感叹一下委员会还在不断加新的语法特性,C/C++本身就是一把容易伤手的电锯了,他们还要给这把电锯增加钻头,安装链条。是的,新的电锯可以更省力,更全能,但是也更容易伤着人了。
|
61
afpro 2017-11-10 10:21:13 +08:00
楼上都在扯什么鬼啊 不能正常回答楼主问题咩
这种情况类型判断顺序是 从右到左 从里到外 int *p[10] 就是 [10]确定是数组 *确定数组里是指针 int 确定指针指向 int 如果想反过来声明一个指向数组的指针 就用括号 int (*p)[10] |
62
Mitt 2017-11-10 10:35:44 +08:00
@ryd994 这不是优化 而是可以推导的, 就像上面说的可变数组,也不过是编译器提前算好你可变数组的定义长度 然后再 * 你的数组长度而已 并且他只能分配到栈上而不是堆上, 也就是在编译时就已经确定的东西,sizeof 本身并不是一个函数,而是一种运算符,是属于编译器的 你可以看 sizeof 和 strlen 的区别 而且我们说 sizeof 之前的点是数组是不是指针这一说 而举例出的 sizeof
|
63
ryd994 2017-11-10 13:13:58 +08:00 via Android
@Mitt 我觉得你根本没懂什么是 vla,vla 的大小是运行时决定的,和你在这里用的定长数组根本不是一回事。楼上说 vla 也不过是为了说明 sizeof 并不总是可推导的。
|
64
zhicheng 2017-11-10 14:13:22 +08:00
数组是数组,指针是指针,不要乱讲。数组指的是一段内存空间,指针是一段内存地址。
struct foo1 { long bar[1]; }; struct foo2 { long *bar; }; 这两个结构体占的内存一样,可存储的东西很不一样。 |
65
wwqgtxx 2017-11-11 16:07:21 +08:00 via iPhone
@zhicheng 你这个代码在不同的平台上不一定内存占用的一样,因为 long*和 long[0]占用的内存并不永远是一样的
|
67
wwqgtxx 2017-11-11 16:13:28 +08:00 via iPhone
@ryd994 个人感觉 vla 算是对 sizeof 的一个特例(也是有些编译器死活不愿意支持的原因),在排除 vla 的情况下,sizeof 的结果应该就是一个编译期间可以确定的参数。
这个情况就类似于 c++的虚函数导致了需要编译器必须实现一个虚表来间接获得函数入口地址,而正常情况下函数的入口地址应该在编译或者链接期间是一个确定的值 |
68
mn66 2018-03-10 09:48:14 +08:00 via Android
这个应该是指向指针(*p [](是指向整形数组的))的指针,书上都有,应该把书好好看一下,在指针那节。所以 p 是指针。
|