指针和数组-艰苦学习C [英] Pointers and Arrays -- Learn C the hard way

查看:76
本文介绍了指针和数组-艰苦学习C的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题来自Zed Shaw的《 Learn C the Hard Way》.它是关于指针和数组的.此处提供了一些代码:

This question is from Learn C the Hard Way by Zed Shaw. It's about pointers and arrays. We are given some code here:

#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;
}

该指令用于"重写此程序中的所有数组用法,以便指针."这是否意味着要做类似的事情?

The directive is to "rewrite all the array usage in this program so that it's pointers." Does this mean to do something like?

int *ptr;
ptr = &ages[0]

推荐答案

让我先说一些不合主题的话题:

Let me start off by saying something a little off topic:

  • I don't think this is a very good book. I think it confuses some topics to make them seem harder than they really are. For a better advanced C book, I would recommend Deep C Secrets by Peter van der Linden, and for a beginner's book, I'd recommend the original K & R

无论如何,您似乎正在从本章中查看额外的功劳练习.

Anyway, it looks like you're looking at the extra credit exercises from this chapter.

首先,我们需要了解指针与数组不同.我在此处的另一个答案中对此进行了扩展,并且我将从 C常见问题解答.当我们声明一个数组或一个指针时,这就是内存中发生的事情:

First we need to understand that pointers are not the same as arrays. I've expanded on this in another answer here, and I'm going to borrow the same diagram from the C FAQ. Here's what's happening in memory when we declare an array or a pointer:

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

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

 char *p = "world"; // pointer

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

因此,在书中的代码中,当我们说:

So, in the code from the book, when we say:

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

我们得到:

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

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

I'm going to use an illegal statement for the purpose of explanation - if we could have said:

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 |
      +---+     +----+----+----+----+---+

以后都可以以相同的方式访问这两个对象-ages[0]可以访问第一个元素"23",而不管它是数组还是指针.到目前为止一切顺利.

Both of these can be accessed the same way later on - the first element "23" can be accessed by ages[0], regardless of whether it's an array or a pointer. So far so good.

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

However, when we want to get the count we run in to problems. C doesn't know how big arrays are - it only knows how big (in bytes) the variables it knows about are. This means, with the array, you can work out the size by saying:

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

或更安全地说:

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

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

In the array case, this says:

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

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

which correctly gives the length of the array. However, for the pointer case, it will read:

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

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

which is almost certainly not the same as the length of the array. Where pointers to arrays are used, we need to use another method to work out how long the array is. In C, it is normal to either:

  • 记住有多少个元素:

  • Remember how many elements there were:

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++) {

  • ,或保留一个前哨值(永远不会在数组中作为实际值出现)以指示数组的结尾:

  • or, keep a sentinel value (that will never occur as an actual value in the array) to indicate the end of the array:

    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'指示字符串的结尾)

    (this is how strings work, using the special NUL value '\0' to indicate the end of a string)

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

    Now, remember that I said you can't actually write:

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

    这是因为编译器不允许您将隐式数组分配给指针.如果您确实想要,可以写:

    This is because the compiler won't let you assign an implicit array to a pointer. If you REALLY want to, you can write:

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

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

    But don't, because it is extremely unpleasant to read. For the purposes of this exercise, I would probably write:

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

    请注意,编译器正在将数组名称衰减"为指向该数组中第一个元素的指针-就像您已经编写了:

    Note that the compiler is "decaying" the array name to a pointer to it's first element there - it's as if you had written:

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

    但是-您还可以动态分配数组.对于此示例代码,它会变得很冗长,但是我们可以将其作为学习练习来做到.而不是写:

    However - you can also dynamically allocate the arrays. For this example code, it will become quite wordy, but we can do it as a learning exercise. Instead of writing:

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

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

    We could allocate the memory using 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:

    Note that we then need to free ages when we're done with the memory:

    free(ages); 
    

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

    Note also that there are a few ways to write the malloc call:

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

    这对于初学者来说更容易阅读,但通常被认为是较差的样式,因为如果更改ages的类型,则需要在两个地方进行更改.相反,您可以编写以下任何一个文件:

    This is clearer to read for a beginner, but generally considered bad style because there are two places you need to change if you change the type of ages. Instead, you can write either of:

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

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

    These statements are equivalent - which you choose is a matter of personal style. I prefer the first one.

    最后一件事-如果我们将代码更改为使用数组,则可以考虑更改此内容:

    One final thing - if we're changing the code over to use arrays, you might look at changing this:

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

    但是,您不需要.之所以有点微妙.首先,此声明:

    But, you don't need to. The reason why is a little subtle. First, this declaration:

    char *argv[]
    

    表示存在一个称为argv的指向字符的指针数组".但是,编译器会将函数参数中的数组视为指向数组第一个元素的指针,因此,如果您编写:

    says "there is an array of pointers-to-char called argv". However, the compiler treats arrays in function arguments as a pointer to the first element of the array, so if you write:

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

    编译器实际上会看到:

    int main(int argc, char **argv)
    

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

    This is also the reason that you can omit the length of the first dimension of a multidimensional array used as a function argument - the compiler won't see it.

    这篇关于指针和数组-艰苦学习C的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆