我正在尝试从python背景教自己C.我目前的小问题是尝试对数组长度等内容进行较少的硬编码,并根据输入动态分配内存。
我写了以下程序。我希望社区的建议可以通过以下方式进行修改:
1。)使first
和last
Name
元素变长。目前他们的长度被硬编码为MAX_NAME_LENGTH
。这将涉及更改Name
s struct
声明以及我为其元素指定值的方式。
2。)奖励:找出一些方法逐步将新元素添加到name_list
数组,而无需事先确定其长度。基本上使它成为可扩展的列表。
/* namelist.c
Loads up a list of names from a file to then do something with them.
*/
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#define DATAFILE "name_list.txt"
#define DATAFILE_FORMAT "%[^,]%*c%[^\n]%*c"
#define MAX_NAME_LENGTH 100
typedef struct {
char first[MAX_NAME_LENGTH];
char last[MAX_NAME_LENGTH];
} Name;
int main() {
FILE *fp = fopen(DATAFILE, "r");
// Get the number of names in DATAFILE at runtime.
Name aName;
int lc = 0;
while ((fscanf(fp, DATAFILE_FORMAT, aName.last, aName.first))!=EOF) lc++;
Name *name_list[lc];
// Now actually pull the data out of the file
rewind(fp);
int n = 0;
while ((fscanf(fp, DATAFILE_FORMAT, aName.last, aName.first))!=EOF)
{
Name *newName = malloc(sizeof(Name));
if (newName == NULL) {
puts("Warning: Was not able to allocate memory for ``Name`` ``newName``on the heap.");
}
memcpy(newName, &aName, sizeof(Name));
name_list[n] = newName;
n++;
}
int i = 1;
for (--n; n >= 0; n--, i++) {
printf("%d: %s %s\n", i, name_list[n]->first, name_list[n]->last);
free(name_list[n]);
name_list[n] = NULL;
}
fclose(fp);
return 0;
}
name_list.txt
的示例内容:
Washington,George
Adams,John
Jefferson,Thomas
Madison,James
更新1:
我已经实现了链接列表和@Williham建议的一些辅助函数,结果如下。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define DATAFILE "name_list.txt"
#define MAX_NAME_LENGTH 30
#define DATAFILE_FORMAT "%29[^,]%*c%29[^\n]%*c"
static const int INPUT_BUFFER_SIZE_DEFAULT = sizeof(char) * MAX_NAME_LENGTH;
typedef struct _Name Name;
struct _Name {
char *first;
char *last;
Name *next;
};
int get_charcount(char *str);
Name * create_name_list(char *filename);
void print_name_list(Name *name);
void free_name_list (Name *name);
int main() {
// Read a list of names into memory and
// return the head of the linked list.
Name *head = create_name_list(DATAFILE);
// Now do something with all this data.
print_name_list(head);
// If you love something, let it go.
free_name_list(head);
head = NULL;
return 0;
}
int get_charcount (char *str)
{
int input_length = 1;
while (str[input_length] != '\0')
{
input_length++;
}
return input_length;
}
Name * create_name_list(char *filename)
{
FILE *fp = fopen(DATAFILE, "r");
char *first_input_buffer = malloc(INPUT_BUFFER_SIZE_DEFAULT);
char *last_input_buffer = malloc(INPUT_BUFFER_SIZE_DEFAULT);
Name *firstNamePtr;
Name *previousNamePtr;
while ((fscanf(fp, DATAFILE_FORMAT, last_input_buffer, first_input_buffer))!=EOF)
{
Name *newNamePtr = malloc(sizeof(Name));
if (previousNamePtr)
{
previousNamePtr->next = newNamePtr;
previousNamePtr = newNamePtr;
}
else
{
firstNamePtr = previousNamePtr = newNamePtr;
}
char *temp_buffer = malloc(get_charcount(first_input_buffer));
strcpy(temp_buffer, first_input_buffer);
newNamePtr->first = malloc(get_charcount(first_input_buffer));
strcpy(newNamePtr->first, temp_buffer);
realloc(temp_buffer, get_charcount(last_input_buffer));
strcpy(temp_buffer, last_input_buffer);
newNamePtr->last = malloc(get_charcount(last_input_buffer));
strcpy(newNamePtr->last, temp_buffer);
free(temp_buffer);
temp_buffer = NULL;
}
previousNamePtr->next = NULL;
previousNamePtr = NULL;
free(first_input_buffer);
free(last_input_buffer);
first_input_buffer = NULL;
last_input_buffer = NULL;
fclose(fp);
return firstNamePtr;
}
void print_name_list (Name *name)
{
static int first_iteration = 1;
if (first_iteration)
{
printf("\nList of Names\n");
printf("=============\n");
first_iteration--;
}
printf("%s %s\n",name->first, name->last);
if (name->next)
print_name_list(name->next);
else
printf("\n");
}
void free_name_list (Name *name)
{
if (name->next)
free_name_list(name->next);
free(name->first);
free(name->last);
name->first = NULL;
name->last = NULL;
name->next = NULL;
free(name);
name = NULL;
}
答案 0 :(得分:5)
一种非常简单的方法是根本不使用数组,而是使用链表:
这可以通过多种方式完成,但最简单的可能是修改Name结构,如下所示:
typedef struct _Name Name;
struct _Name {
char *first;
char *last;
Name *next;
};
使用char *而不是char []将需要一些strcpy
,但这实际上既不是在这里也不是在那里。要扩展数组,您现在可以一次一个地对这些元素进行malloc;然后适当地设置。
注意:在创建新的尾部元素时,请记住在NULL旁边设置。
答案 1 :(得分:4)
没有办法在C中扩展数组。您可以做的就是分配更大的内存块并复制项目。这就是Python所做的事情。
也无法确定数组的大小,因为它只存储项目而没有附加信息(Python数组存储元素旁边的长度。)您可以在数组的末尾放置一个标记并计算元素直到你点击标记,这就是字符串的工作方式(空字符'\ 0'是标记。)
答案 2 :(得分:2)
你必须有一个最大长度数组才能接受输入 - 你不知道输入的长度。但是,您只需要一个该长度的数组。获得输入后,您可以获得它的长度并分配一个恰当大小的数组来存储名称。
至于增加列表,您可以使用realloc
或使用链接列表。
答案 3 :(得分:1)
您是否开始阅读有关链接列表的内容?它可能会帮助您建立一个动态结构。
您还可以查看数组和链表之间的区别。 char指针可以是链表的成员,可以动态分配内存。
从stackoverflow中检查此link以获取有关相同内容的更多信息。
答案 4 :(得分:1)
由于还没有人提到它,如果使用* scanf读取不受信任的输入字符串,则应始终使用最大字段宽度说明符。喜欢
scanf("%19s", str);
请注意,这指定了不包括终止NUL的最大字符串长度,因此在此示例中,str应为20个字符长。
如果不限制此类扫描输入转换,则在缓冲区溢出时,您不仅会遇到未定义的行为,还会出现安全漏洞。
回到关于动态增长缓冲液的问题。想象一下,您正在读取由线组成的输入,每条线最多可以有78个字符宽。在这种情况下,您知道每行的最大长度,但不知道输入的最大长度。执行此操作的常用方法是分配一些默认空间,如果需要增加该空间,请将其增长* 2,这样您就不需要多次重新分配()。