【问题标题】:C compound literals, pointer to arraysC 复合文字,指向数组的指针
【发布时间】:2011-03-31 06:21:27
【问题描述】:

我正在尝试将复合文字分配给变量,但它似乎不起作用,请参阅:

  int *p[] = (int *[]) {{1,2,3},{4,5,6}};

我在 gcc 中遇到错误。

但如果我只写这个:

  int p[] = (int []) {1,2,3,4,5,6};

那就没事了。

但这不是我想要的。

我不明白为什么会发生错误,因为如果我像数组一样初始化它,或者将它与字符数组的指针一起使用,没关系,请参阅:

  int *p[] = (int *[]) {{1,2,3},{4,5,6}}; //I got a error
  int p[][3] = {{1,2,3},{4,5,6}}; //it's okay
  char *p[] = (char *[]) {"one", "two"...}; // it's okay!

请注意,我不明白为什么我在第一种形式中出现错误,请我不能,或者我不想像第二种形式那样写,因为它需要是复合文字,我不'不想说编译器的数组有多大。我想要第二个类似的东西,但是对于 int 值。

提前致谢。

【问题讨论】:

  • 为什么需要在一个语句中初始化这些数组?
  • 如果我理解了这个问题,它会简化事情,例如,我可以将 va_args 交换为复合文字......
  • 您多次声明您遇到错误,但从未告诉我们错误是什么。学徒,你什么都没学到吗?
  • int p[] = (int []) {1,2,3,4,5,6}; 在 Linux 上使用 gcc 4.8.2 时出现一些错误。错误是“从非常量数组表达式初始化的数组”。你的 gcc 版本是多少?

标签: c compound-literals


【解决方案1】:

首先,在您的所有示例中,强制转换都是多余的,可以删除。其次,您正在使用初始化多维数组的语法,这需要定义第二维以分配连续的内存块。相反,请尝试以下两种方法之一:

  • 多维数组:

    int p[][3] = {{1,2,3},{4,5,6}};
    
  • 指向一维数组的指针数组:

    int p1[] = {1,2,3};
    int p2[] = {4,5,6};
    int *p[] = {p1,p2};
    

后一种方法的优点是允许不同长度的子数组。而前一种方法确保内存是连续布局的。

我强烈建议您不要使用的另一种方法是将整数编码为字符串文字。这是一个非便携式黑客。此外,字符串文字中的数据应该是恒定的。你的数组需要是可变的吗?

int *p[] = (int *[]) {
    "\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00",
    "\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00"
};

该示例可能适用于 32 位 little-endian 机器,但我是在 iPad 上输入的,目前无法验证。同样,请不要使用它;提起它我都觉得很脏。

您发现的转换方法似乎也适用于指向指针的指针。这也可以像多维数组一样被索引。

int **p = (int *[]) { (int[]) {1,2,3}, (int[]) {4,5,6} };

【讨论】:

  • 是的,在我的例子中它是多余的,但它对函数参数有好处,我怎样才能避免带有指针的维度?像第二个例子...感谢您的回答。
  • @drigoSkalWalker - 你必须告诉 C 最后一个数组大小,因为 C 不会自己尝试猜测。这对于指针算术等很重要。
  • 谢谢伙计,有没有办法像上一个例子一样编写你的第一个例子?因为我不能告诉编译器我的数组有多大,它必须是另一个数组中的一个数组......我开始认为这是不可能的......无论如何,谢谢。
  • 您可以将整数编码为字符串文字,但这不是可移植的。
  • () 中的类型不是强制转换——它是复合文字语法的一部分。
【解决方案2】:

首先了解“数组不是指针”。

int p[] = (int []) {1,2,3,4,5,6};

在上述情况下,p 是一个整数数组。将元素 {1,2,3,4,5,6} 复制到 p。这里不需要类型转换,rvaluelvalue 类型都匹配整数数组,因此不会出错。

int *p[] = (int *[]) {{1,2,3},{4,5,6}};

“请注意,我不明白为什么我在第一个中遇到错误,..”

在上述情况下,p 是一个整数指针数组。但是 {{1,2,3},{4,5,6}} 是一个二维数组(即 [][] ),不能被类型转换为指针数组。你需要初始化为 -

int p[][3] = { {1,2,3},{4,5,6} };
  // ^^ First index of array is optional because with each column having 3 elements
  // it is obvious that array has two rows which compiler can figure out.

但是为什么这个语句会编译?

char *p[] = {"one", "two"...};

字符串文字不同于整数文字。在这种情况下,p 也是一个字符指针数组。当实际说"one" 时,它可以被复制到一个数组中,也可以指向它的位置,认为它是只读的

char cpy[] = "one" ;
cpy[0] = 't' ;  // Not a problem

char *readOnly = "one" ;
readOnly[0] = 't' ;  // Error because of copy of it is not made but pointing
                     // to a read only location.

对于字符串文字,上述任何一种情况都是可能的。所以,这就是编译语句的原因。但是——

char *p[] = {"one", "two"...}; // All the string literals are stored in 
                               // read only locations and at each of the array index 
                               // stores the starting index of each string literal.

我不想说编译器的数组有多大。

使用malloc动态分配内存是解决办法。

希望对你有帮助!

【讨论】:

  • 你说 {{1,2,3},{4,5,6}} 是一个二维数组,不能被类型转换为 ponters 数组,但我了解到任何数组是一个指针,像函数指针一样传递,谢谢你的回答!!!
  • 一维数组可能并不意味着数组是指针
  • 数组和指针之间存在细微差别,但我认为除了我的初始化选项之外,这对你的情况并不重要。
  • 其中一个区别是你不能改变数组的地址,但你可以改变指针。因此,您可以调用数组 static address 和指针 - dynamic address
【解决方案3】:

既然没人说过:如果你想要一个指向二维数组的指针,你可以(可能)做类似的事情

int (*p)[][3] = &(int[][3]) {{1,2,3},{4,5,6}};

编辑:或者您可以通过

获得指向其第一个元素的指针
int (*p)[3] = (int[][3]) {{1,2,3},{4,5,6}};

您的示例不起作用的原因是因为{{1,2,3},{4,5,6}} 不是int*[] 类型的有效初始化程序(因为{1,2,3} 不是int* 的有效初始化程序)。请注意,它不是int[2][3]——它只是一个无效的表达式。

它适用于字符串的原因是因为"one"char[]char[N] 的有效初始化器(对于某些N>3)。作为一个表达式,它大约相当于(const char[]){'o','n','e','\0'},除了编译器在失去常量时不会抱怨太多。

是的,初始化程序和表达式之间有很大的区别。我很确定 char s[] = (char[]){3,2,1,0}; 是 C99 中的编译错误(可能是 C++ pre-0x)。还有很多其他的东西,但是T foo = ...; 是变量初始化,而不是赋值,尽管它们看起来很相似。 (它们在 C++ 中特别不同,因为没有调用赋值运算符。)

以及与指针混淆的原因:

  • T[] 类型在必要时隐式转换为 T* 类型(指向其第一个元素的指针)。
  • 函数参数列表中的T arg1[] 实际上表示T * arg1。由于各种原因,您不能将数组传递给函数。这不可能。如果你尝试,你实际上是在传递一个指向数组的指针。 (但是,您可以将包含固定大小数组的结构传递给函数。)
  • 它们都可以取消引用并使用相同的(我认为)语义下标。

编辑: 细心的人可能会注意到我的第一个示例在语法上大致等同于 int * p = &1;,这是无效的。这在 C99 中有效,因为函数内的复合文字“具有与封闭块关联的自动存储持续时间”(ISO/IEC 9899:TC3)。

【讨论】:

  • 您能否添加一些示例,将 T[] 隐式转换为 T(*)[]?你的解释很透彻!我认为您的回答真的看穿了复合文字的迷雾并指出了真正的问题!
  • 经过一番测试,我认为应该是int (*p)[3] = (int[][3]) {{1,2,3},{4,5,6}};
【解决方案4】:

您正在使用的是 int 指针数组。您应该使用指向数组的指针:

int (*p)[] = (int *) {{1,2,3}, {4,5,6}}

查看this答案了解更多详情。

【讨论】:

  • 我认为这甚至不能编译。
  • @tc 检查这个答案stackoverflow.com/questions/859634/… 你应该在评论之前先尝试一下。
  • 我还是很确定它不能编译。
  • 这里是代码:#include void main() { int (*p)[] = (int *) {{1,2,3},{4,5 ,6}}; }
  • 我错了;它似乎可以编译,但等效于int (*p)[] = (int*)1;(“标量初始化器周围的大括号”、“初始化使指针从没有强制转换的整数”、“标量初始化器中的多余元素”、“从不兼容的指针类型初始化”、“返回类型从'main'不是'int'”)。我支持我的反对票。
【解决方案5】:

您似乎混淆了指针和数组。他们不是一回事!数组就是列表本身,而指针只是一个地址。然后,使用指针算法,您可以假装指针是数组,而事实上,数组的名称是指向第一个元素的指针,所以一切都一团糟。 ;)

int *p[] = (int *[]) {{1,2,3},{4,5,6}};      //I got a error

这里,p 是一个指针数组,因此您尝试将地址为 1、2、3 的元素分配给第一个数组,将 4、5、6 的元素分配给第二个数组。发生 seg 错误是因为您无法访问这些内存位置。

int p[][3] = {{1,2,3},{4,5,6}};              //it's okay

这没关系,因为这是一个数组数组,所以这次1、2、3、4、5和6不是地址,而是元素本身。

char *p[] = (char *[]) {"one", "two"...};    // it's okay!

这没关系,因为字符串文字(“one”,“two”,...)不是真正的字符串,而是指向这些字符串的指针,因此您将字符串文字的地址分配给 p[1] “一个”。
顺便说一句,这与char abc[]; abc = "abc"; 相同。这不会编译,因为您不能将指针分配给数组,而 char *def; def = "def"; 解决了问题。

【讨论】:

  • 嗯,char abc[] = "abc"; 编译得很好——字符串文字是 char 数组的有效初始化器。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-25
  • 2021-11-08
  • 2013-04-19
  • 1970-01-01
  • 2023-03-11
  • 1970-01-01
相关资源
最近更新 更多