3

这个问题来自 Zed Shaw 的 Learn C the Hard Way。这是关于指针和数组的。我们在这里得到了一些代码:

#include <stdio.h>

int main(int argc, char *argv[])
{
  // create two arrays we care about
  int ages[] = {23, 43, 12, 89, 2};
  char *names[] = {
      "Alan", "Frank",
      "Mary", "John", "Lisa"
  };

  // safely get the size of ages
  int count = sizeof(ages) / sizeof(int);
  int i = 0;

  // first way using indexing
  for(i = 0; i < count; i++) {
      printf("%s has %d years alive.\n",
              names[i], ages[i]);
  }

  printf("---\n");

  // setup the pointers to the start of the arrays
  int *cur_age = ages;
  char **cur_name = names;

  // second way using pointers
  for(i = 0; i < count; i++) {
      printf("%s is %d years old.\n",
            *(cur_name+i), *(cur_age+i));
  }

  printf("---\n");

  // third way, pointers are just arrays
  for(i = 0; i < count; i++) {
      printf("%s is %d years old again.\n",
              cur_name[i], cur_age[i]);
  }

  printf("---\n");

  // fourth way with pointers in a stupid complex way
  for(cur_name = names, cur_age = ages;
          (cur_age - ages) < count;
          cur_name++, cur_age++)
  {
      printf("%s lived %d years so far.\n",
              *cur_name, *cur_age);
  }

  return 0;
}

该指令是“重写该程序中的所有数组用法,使其成为指针。 ”这是否意味着要做类似的事情?

int *ptr;
ptr = &ages[0]
4

5 回答 5

14

让我先说点题外话:

  • 我不认为这是一本很好的书。我认为它混淆了一些主题,使它们看起来比实际更难。对于更好的高级 C 书,我会推荐Peter van der Linden 的 Deep C Secrets,对于初学者的书,我会推荐原版K&R

无论如何,看起来您正在查看本章中的额外学分练习。

  • 另一个旁白——我不认为这是一个特别明智的学习练习(另一个答案指出这个问题没有意义),所以这个讨论会变得有点复杂。相反,我会推荐K&R第 5 章中的练习。

首先,我们需要了解指针与数组不同。我在这里的另一个答案中对此进行了扩展我将从C 常见问题解答中借用相同的图表。以下是我们声明数组或指针时内存中发生的情况:

 char a[] = "hello";  // array

   +---+---+---+---+---+---+
a: | h | e | l | l | o |\0 |
   +---+---+---+---+---+---+

 char *p = "world"; // pointer

   +-----+     +---+---+---+---+---+---+
p: |  *======> | w | o | r | l | d |\0 |
   +-----+     +---+---+---+---+---+---+

所以,在书中的代码中,当我们说:

int ages[] = {23, 43, 12, 89, 2};

我们得到:

      +----+----+----+----+---+
ages: | 23 | 43 | 12 | 89 | 2 |
      +----+----+----+----+---+

为了解释的目的,我将使用非法声明 - 如果我们可以说:

int *ages = {23, 43, 12, 89, 2}; // The C grammar prohibits initialised array
                                 // declarations being assigned to pointers, 
                                 // but I'll get to that

这将导致:

      +---+     +----+----+----+----+---+
ages: | *=====> | 23 | 43 | 12 | 89 | 2 |
      +---+     +----+----+----+----+---+

稍后可以以相同的方式访问这两者 - 第一个元素“23”可以通过 访问ages[0],无论它是数组还是指针。到目前为止,一切都很好。

但是,当我们想要获得计数时,我们会遇到问题。C 不知道数组有多大——它只知道它知道的变量有多大(以字节为单位)。这意味着,使用数组,您可以通过以下方式计算出大小:

int count = sizeof(ages) / sizeof(int);

或者,更安全地:

int count = sizeof(ages) / sizeof(ages[0]);

在数组的情况下,这表示:

int count = the number of bytes in (an array of 6 integers) / 
                 the number of bytes in (an integer)

它正确地给出了数组的长度。但是,对于指针情况,它将显示为:

int count = the number of bytes in (**a pointer**) /
                 the number of bytes in (an integer)

这几乎肯定与数组的长度不同。在使用指向数组的指针的地方,我们需要使用另一种方法来计算数组的长度。在 C 中,以下两种情况都是正常的:

  • 记住有多少元素:

    int *ages = {23, 43, 12, 89, 2}; // Remember you can't actually
                                     // assign like this, see below
    int ages_length = 5;
    for (i = 0 ; i < ages_length; i++) {
    
  • 或者,保留一个标记值(永远不会作为数组中的实际值出现)来指示数组的结尾:

    int *ages = {23, 43, 12, 89, 2, -1}; // Remember you can't actually
                                         // assign like this, see below
    for (i = 0; ages[i] != -1; i++) {
    

    (这就是字符串的工作方式,使用特殊的 NUL 值 '\0' 来指示字符串的结尾)


现在,请记住我说过你实际上不能写:

    int *ages = {23, 43, 12, 89, 2, -1}; // Illegal

这是因为编译器不允许您将隐式数组分配给指针。如果你真的想,你可以写:

    int *ages = (int *) (int []) {23, 43, 12, 89, 2, -1}; // Horrible style 

但是不要,因为它读起来非常不愉快。出于本练习的目的,我可能会写:

    int ages_array[] = {23, 43, 12, 89, 2, -1};
    int *ages_pointer = ages_array;

请注意,编译器正在将数组名称“衰减”为指向它的第一个元素的指针 - 就好像你写过:

    int ages_array[] = {23, 43, 12, 89, 2, -1};
    int *ages_pointer = &(ages_array[0]);

但是 - 您也可以动态分配数组。对于这个示例代码,它会变得很罗嗦,但我们可以将其作为一个学习练习。而不是写:

int ages[] = {23, 43, 12, 89, 2};

我们可以使用 malloc 分配内存:

int *ages = malloc(sizeof(int) * 5); // create enough space for 5 integers
if (ages == NULL) { 
   /* we're out of memory, print an error and exit */ 
}
ages[0] = 23;
ages[1] = 43;
ages[2] = 12;
ages[3] = 89;
ages[4] = 2;

ages请注意,当我们完成内存时,我们需要释放:

free(ages); 

另请注意,有几种方法可以编写 malloc 调用:

 int *ages = malloc(sizeof(int) * 5);

这对于初学者来说更容易阅读,但通常被认为是不好的风格,因为如果您更改ages. 相反,您可以编写以下任一项:

 int *ages = malloc(sizeof(ages[0]) * 5);
 int *ages = malloc(sizeof(*ages) * 5);

这些陈述是等效的-您选择的内容是个人风格的问题。我更喜欢第一个。


最后一件事 - 如果我们将代码更改为使用数组,您可能会考虑更改:

int main(int argc, char *argv[]) {

但是,你不需要。原因有点微妙。首先,这个声明:

char *argv[]

说“有一个指向字符的数组,称为 argv”。但是,编译器将函数参数中的数组视为指向数组第一个元素的指针,因此如果您编写:

int main(int argc, char *argv[]) {

编译器实际上会看到:

int main(int argc, char **argv)

这也是您可以省略用作函数参数的多维数组的第一维长度的原因 - 编译器不会看到它。

于 2013-06-21T02:12:15.743 回答
1

“用指针使用而不是数组使用来重写代码”的任务没有足够清晰地表达以使其有意义。在 C 语言中,99.9%(只是一个非正式的数字)的数组功能是基于隐式数组到指针的转换,这意味着几乎每次使用数组时,您也使用指针。没有办法解决它。

换句话说,形式上真的不需要重写任何东西。

如果你重写你的代码

int *ptr = &ages[0];

并使用ptr代替ages,您将简单地显式地显式地隐式地显示您的代码中已经存在的东西。如果这就是该任务的真正含义,那么您当然可以这样做。但我认为这种多余的练习没有多大意义。

于 2013-06-21T01:33:57.767 回答
0

这可能意味着像你建议的那样,是的。

但请记住,这ages已经是一个 int 指针 ( int *)——在 C 中,数组只是内存中彼此相邻的一堆东西。表示该数组的变量只是指向该数组中第一个元素的指针,[]运算符是取消引用。

你可以这样想:

当您的程序运行时,某处有一块内存,其中包含
|...| 23 | 43 | 12 | 89 | 2 |...|
Where 每个框表示足够的空间来容纳一个int。那么,程序中
的变量只是一个指针,它保存了该块中第一个元素的地址。ages它“指向” 23,并具有类型int*。如果你取消引用它,你会发现它的*ages计算结果是 23。同样,如果你使用那个地址并“跳过”一个int-size 向前,你会得到43. 在代码中,这看起来就像
*(ages + 1 * sizeof(int))
你可以用1你想跳过的许多元素替换 。因为这真的很丑陋和令人困惑,所以 C 为您提供了一种很好的方式来做完全相同的事情:[]运算符。一般来说,

some_array[n] == *(some_array + n * sizeof(array_element_type))

希望对您有所帮助,祝您学习 C 好运!确保你花时间真正理解数组和指针的相等性;如果你不这样做,以后很多事情都会变得更加困难。

于 2013-06-21T01:30:23.917 回答
0

这是一种在不使用动态分配的情况下更改agesnames成为指针的方法。

  // create two arrays we care about
  const char *ages = "\x17\x2b\x0c\x59\x02";
  const char (*names)[6] = (void *)
      "Alan\0\0" "Frank\0" "Mary\0\0" "John\0\0" "Lisa\0\0";

  // safely get the size of ages
  int count = strlen(ages);

  //...

  // setup the pointers to the start of the arrays
  const char *cur_age = ages;
  const char (*cur_name)[6] = names;

names并且cur_name都是指针类型,尽管它们确实指向数组。

于 2013-06-21T02:04:54.577 回答
0

我的猜测是这意味着

  1. 使用 为每个数组分配内存malloc()并使用 释放内存free()

  2. 在所有 for 循环中使用指针算法。

于 2013-06-21T01:32:50.913 回答