【问题标题】:C scanf string array [duplicate]C scanf字符串数组[重复]
【发布时间】:2016-11-11 13:26:19
【问题描述】:

我正在尝试通过编写一个小而短的程序来读取文本文件并将其内容放入数组中。我想编写这个程序以将任何文本文件作为字符串读取并将其存储到数组中。这样,您可以读取任何文件,并且无论字符串长度如何,它都会动态构建一个数组并用文件填充它。我将其用作练习 C 的练习,并希望将其推广到其他类型和结构。

但是,由于某种原因,我的第一个条目不匹配导致意外行为,但至少它没有遇到任何段错误。我知道使用 C,您需要从本质上对所有内存进行微管理,并且使用代码,我尝试为每个条目分配内存,但这是正确的方法吗?我在脑海中运行了代码,从 0 个条目开始在逻辑上是有意义的,但我不明白为什么第一个条目失败而其余条目有效。

代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

    //Initialize variables and pointers
    //Create an array of chars to use when reading in
    //Create an array of strings to store
    //i : use to keep track of the number of strings in array
    //j : loop variable
    //size: size of string
    char *s = (char *) malloc(sizeof(char));
    int i=0,j=0;
    int size = 0;
    char **a = (char **) malloc(sizeof(char *));

    //Read in string, assign string to an address at a[]
    while( scanf("%79s",s) == 1){

        //Get the size of the input string
        size = (unsigned) strlen(s);

        //Print some notes here
        printf("\nString is \"%-14s\"\tSize is %-3d, i is currently %d\n",s,size,i);
        printf("Using MALLOC with %d bytes\n",size+1);

        //Allocate memory to copy string
        //
        //For some reason, the commented code works
        //a[i] = (char *) (malloc(sizeof(char)*(size+1)) + 'a');
        a[i] = (char *) (malloc(sizeof(char)*(size+1)) );

        //Go and allocate memory for each character in string to store
        for(j=0; j<(size+1); j++)   a[i][j] = (char) (malloc(sizeof(char)));

        //Print some more notes here
        printf("Size: a[%2d] is %3d bytes, *a[%2d] is %3d bytes, Length of a[%2d] is %d\n",i,(int) sizeof(a[i]),i,(int) sizeof(*a[i]),i,(unsigned) strlen(a[i]));

        //Copy over string and set last char to be end
        for(j=0; j<size; j++)       a[i][j] = (char) s[j];
        a[i][size] = '\0';

        //Print it out and increase i
        printf("a[%3d] is now %s\n",i,a[i]);

        i++;
    }
    printf("I is now %d\n\n\n",i);
    a[i] = NULL;

    //print out array
    for(j=0; j<i; j++)      printf("%3d. %-40s\n",j,a[j]);


    return 0;
}

测试文本文件(numbers.txt):

1
22
333
4444
55555
666666
7777777
88888888
9999999
0000000000
11111111111
222222222

命令:

./a.out

结果:

String is "1             "      Size is 1  , i is currently 0
Using MALLOC with 2 bytes
Size: a[ 0] is   8 bytes, *a[ 0] is   1 bytes, Length of a[ 0] is 2
a[  0] is now 1

String is "22            "      Size is 2  , i is currently 1
Using MALLOC with 3 bytes
Size: a[ 1] is   8 bytes, *a[ 1] is   1 bytes, Length of a[ 1] is 3
a[  1] is now 22

String is "333           "      Size is 3  , i is currently 2
Using MALLOC with 4 bytes
Size: a[ 2] is   8 bytes, *a[ 2] is   1 bytes, Length of a[ 2] is 4
a[  2] is now 333

String is "4444          "      Size is 4  , i is currently 3
Using MALLOC with 5 bytes
Size: a[ 3] is   8 bytes, *a[ 3] is   1 bytes, Length of a[ 3] is 5
a[  3] is now 4444

String is "55555         "      Size is 5  , i is currently 4
Using MALLOC with 6 bytes
Size: a[ 4] is   8 bytes, *a[ 4] is   1 bytes, Length of a[ 4] is 6
a[  4] is now 55555

String is "666666        "      Size is 6  , i is currently 5
Using MALLOC with 7 bytes
Size: a[ 5] is   8 bytes, *a[ 5] is   1 bytes, Length of a[ 5] is 7
a[  5] is now 666666

String is "7777777       "      Size is 7  , i is currently 6
Using MALLOC with 8 bytes
Size: a[ 6] is   8 bytes, *a[ 6] is   1 bytes, Length of a[ 6] is 8
a[  6] is now 7777777

String is "88888888      "      Size is 8  , i is currently 7
Using MALLOC with 9 bytes
Size: a[ 7] is   8 bytes, *a[ 7] is   1 bytes, Length of a[ 7] is 9
a[  7] is now 88888888

String is "9999999       "      Size is 7  , i is currently 8
Using MALLOC with 8 bytes
Size: a[ 8] is   8 bytes, *a[ 8] is   1 bytes, Length of a[ 8] is 8
a[  8] is now 9999999

String is "0000000000    "      Size is 10 , i is currently 9
Using MALLOC with 11 bytes
Size: a[ 9] is   8 bytes, *a[ 9] is   1 bytes, Length of a[ 9] is 11
a[  9] is now 0000000000

String is "11111111111   "      Size is 11 , i is currently 10
Using MALLOC with 12 bytes
Size: a[10] is   8 bytes, *a[10] is   1 bytes, Length of a[10] is 12
a[ 10] is now 11111111111

String is "222222222     "      Size is 9  , i is currently 11
Using MALLOC with 10 bytes
Size: a[11] is   8 bytes, *a[11] is   1 bytes, Length of a[11] is 10
a[ 11] is now 222222222
I is now 12


  0. ▒"▒
  1. 22
  2. 333
  3. 4444
  4. 55555
  5. 666666
  6. 7777777
  7. 88888888
  8. 9999999
  9. 0000000000
 10. 11111111111
 11. 222222222

【问题讨论】:

  • 在未初始化时使用具有自动存储持续时间的对象的值的未定义行为。将不正确的参数类型传递给可变参数函数的未定义行为。 fflush()ing 非输出流的未定义行为。
  • 您需要做的不仅仅是声明一个指针...您还需要为您读取的字符串(以及指向它们的指针数组)分配内存。如果您事先不知道需要多少空间,您可能还需要随时更改这些分配的大小。
  • 当您将scanf 与字符串一起使用时,您不需要&amp;。数组 a 中的每个元素都是一个字符串,a[0] 是第一个 a[1] 第二个,依此类推。打印字符串时,只需使用字符串/字符数组的 name (指向 char 数组中第一个元素的指针),因此要打印一个字符串 (char *),它是数组 a 中的一个元素,您只需执行 printf("my 1st str is: %s\n", a[0]);。此外,您永远不会使用 &amp; 来打印字符串(或指向结构 int 的指针,例如),如果需要,您将改为使用 * 运算符(但同样,不要使用字符串,因为它 期望 ptr 本身)。
  • 您实现的基本理念是正确的。但是,仍然存在一些问题,例如主要问题是您没有为“a”分配内存。通过此链接http://stackoverflow.com/questions/19068643/dynamic-memory-allocation-for-pointer-arrays 了解如何为指针数组动态分配内存。浏览那里提供的所有答案。另外,尝试使用 fgets() 而不是 scanf() 字符串。
  • "这是因为内存分配方式的原因吗?"在这段代码中没有任何地方被分配内存。当你声明一个指针时,你认为会发生什么,C 会在心理上知道你想要多少内存?

标签: c arrays dynamic c-strings


【解决方案1】:

对于初学者:您的声明 char **a; 声明了一个指针;类型正确,但指向无处。

您当前的代码正在写入内存的某个未定义区域,这有时会起作用,有时会转储;但这样做的效果是不确定的,而且大多是不可预测的。

在 C 中,您需要处理自己的内存。因此,要么,你声明你希望它指向的内存:char a[255][10];(每个字符串中有 255 个字符,其中 10 个),或者你在堆上分配它(使用 malloc)并将其分配给指针:a = (char * *) malloc(sizeof(char *) * 10);(再次得到 10),然后为每个指针:a[i] = (char *) malloc(sizeof(char) * 255);

后者允许您根据需要使用大小变量,并且还允许比第一种方式更大的内存块。

了解了这一点后,将其应用到您要使用的其他变量中。请记住,您声明的每个指针都只是一个指针。你的工作是让它指向有用的东西,并在那个位置提供记忆。

【讨论】:

  • you declare the memory you want this to pointer to point to right with it: char a[10][255] - 不,你不是在这里声明一个指针,你是在声明一个堆栈数组(自动)分配的数组。更糟糕的是,它实际上是 255 个数组,每个大小为 10,而不是你说的。
  • 更正了序列问题。但是a后面就相当于一个指针,可以这样使用。
  • 不,a 的名称。数组名称通常隐式转换为指针的事实与声明的上下文没有直接关系,这就是您所描述的。前者有一些指针没有的基本权力。是的,然后您可以将数组名称传递给期望指针的函数,它们会默默地“衰减”,但它们不是指针。
  • 我知道并同意。对于初学者的答案,我试图跳过一些更好的点。我估计 OP 的专业水平在他只会对这些细节感到困惑的范围内,特别是因为它们与他当前的问题无关。你不会在中学解释积分吧?
  • 您似乎认为在 Stack Exchange 上,答案是专门针对 OP 的,此时此刻。他们不是:他们应该为所有读者服务,因为该网站旨在建立一个良好信息的知识库。你不会为一个学生写一本只包含介绍的数学教科书。因此,我发表评论澄清,因此 OP 或任何其他/未来的读者都可以从准确的信息中受益,而不仅仅是现在易于消化的信息。如果您不想,欢迎您不要将此类 cmets 合并到您的答案中,但它们也可供其他人阅读。
【解决方案2】:

我尝试实现这一点的最佳方法之一是使用链表;每一行都将被视为列表的一个“节点”

我建议使用链接列表,因为无论文件中的行数如何,您似乎都希望能够读取其中的信息。因此,当您提到将数字单独存储在数组中时,动态分配数组会很有用。

无论多少行,信息都会存储在链表中(假设您的计算机有足够的可用内存)。

代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define FILENAME file.txt
//While the amount of chars in each line is limited, are you really going to use more than 1000 per line?
#define MAX_LINE_SIZE 1000
struct node {
  char data[MAX_LINE_SIZE];
  struct node* next;
};


void insert_node (struct node **head, char *nodeValue, size_t line_max_size);
void print_list (struct node *head);

int main(int argc, char const *argv[]) {
  //Open the file
  FILE *file_to_open;
  file_to_open = fopen("numbers.txt", "r");
  if(!file_to_open) {
    return 1;
  }
  char currentLine[MAX_LINE_SIZE] = {0};

  struct node* headNode;
  headNode = NULL; //Initialize our first node pointer to be NULL

  while (fgets(currentLine, sizeof(currentLine), file_to_open)) {
    insert_node(&headNode, currentLine, sizeof(currentLine));
  }

  printf("Array list from your file:\n");
  print_list(headNode);

  fclose(file_to_open);
  return 0;
}

void print_list (struct node *head) {
  //We define a new 'currentNode' instead of just using headNode so we don't modify the headNode
  struct node* currentNode = head;

  while (currentNode != NULL) {
      printf("Value: %s", currentNode->data);
      currentNode = currentNode -> next;
  }
}

void insert_node (struct node **head, char *nodeValue, size_t line_max_size) {
  struct node *currentNode = malloc(sizeof(struct node));

  strncpy(currentNode->data, nodeValue, line_max_size);
  currentNode->next = *head;

  /*
  * In order to understand how we add nodes, let's take a look at possible senarios:
  * 1. The list is empty, so we need to add a new node
  * In which case, our memory looks like this where HEAD is a pointer to the first node:
  *  -----
  * |HEAD | --> NULL
  *  -----
  * The line currentNode->next = headNode; will NOT do anything since headNode originally
  * starts out at a value of NULL
  *  Now, we want to set our current node to be the (new) head node
  *  -----      -------------
  * |HEAD | --> |CURRENTNODE| //The head node points to the current node
  *  -----      -------------
  * This is done with headNode = currentNode; (shown below)
  * So, headNode now points directly to the node structure we just created
  *
  * 2. The list is already populated; we need to add a new node to the beginning
  * For the sake of simplicity, let's start out with 1 node:
  * ------  --------------------
  * HEAD  -> FIRST NODE --> NULL
  * ------  --------------------
  * With currentNode->next = headNode, the data structure looks like this:
  * ---------        -----  ---------------------
  * currentNode -->  HEAD -> POINTER TO FIRST NODE
  * ---------        -----  ---------------------
  * Which, obviously needs to be altered since the currentNode should then
  * become the first node & the head should point to the first node:
  * ----  ---             ----------------------------
  * HEAD -> currentNode -->         2nd NODE
  * -----  --             ----------------------------
  * This is done with head = currentNode
  * Note: In this explanation, I did not explain *head = currentNode like in the actual code
  * This was to make the explanation simpler, although we do need to
  * dereference once to access head through the pointer to pointer
  */
  *head = currentNode;
}

我试图解释我是如何在 cmets 中尽我所能在链表中插入节点的,但是关于这些问题的说明总是可以在网上找到。

程序的输出如下所示:

文件中的数组列表: 值:222222222 值:11111111111 值:0000000000 价值:9999999 价值:88888888 价值:7777777 值:666666 价值:55555 价值:4444 价值:333 价值:22 值:1

从技术上讲,在这个程序中每行不能超过 1000 个字符,但是很少需要每行

还要注意,这个程序向后打印出链表(以及numbers.txt 的顺序),因为我们不断地将节点添加到链表的开头。但是,颠倒链表的顺序应该是相当简单的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-27
    • 2017-09-21
    • 2016-04-09
    相关资源
    最近更新 更多