C - 对动态大小的字符串数组进行排序
C - Sorting a dynamically sized array of strings
我是 C 的新手,正在尝试从文件中读取输入数据流,将其附加到动态大小的数组,这在这一点上效果很好。
之后,我想按字母顺序对数组进行排序。我阅读了 strcmp 的联机帮助页,认为这是执行此操作的最佳方法。但是,每当我尝试执行它时,我都会遇到“分段错误”。分配内存时我到底做错了什么?
这是我的代码供参考:
int main (int argc, char* argv[]) {
int c = getLineCount();
FILE * wordlist = fopen("test", "r");
// If file doesn't exist, handle error
if ( wordlist == NULL ) {
fputs("Unable to open input file", stderr);
exit(1);
}
int length = 101;
char line[length];
int i = 0;
char **words = malloc(c * sizeof(line));
if ( words == NULL ) {
fputs("Unable to allocate Memory", stderr);
exit(1);
}
while (1) {
char *l = fgets(line, length, wordlist);
if ( (l == NULL) ) {
// Check if EOF is reached
if (feof(wordlist)) {
// fputs("--- EOF ---\n", stdout);
break;
// Check if error occured while reading
} else {
fputs("Error reading file", stderr);
exit(1);
}
} else if (strchr(line, '\n') == NULL) {
// Check if line is too long
// Iterate until newline is found or until EOF
int c;
while((c = fgetc(wordlist)) != '\n' && c != 0);
fputs("--- LINE TOO LONG ---\n", stderr);
continue;
} else if ( line[0] == '\n' ) {
// Check if line is only "\n", if yes, ignore the line
continue;
} else {
words[i] = malloc(sizeof(line) * sizeof(char));
if ( words[i] == NULL ) {
fputs("Unable to allocate Memory", stderr);
exit(1);
}
strcpy(words[i], line);
i++;
}
}
// Close file
fclose(wordlist);
char temp[101];
for (int i = 0; i < (length-1); i++) {
int lowest = i;
for (int j = i+1; j < length; j++) {
if (strcmp(words[j], words[lowest]) < 0) {
lowest = j;
}
}
if (lowest != i) {
strcpy(temp, words[i]);
words[i] = words[lowest];
free(words[lowest]);
words[lowest] = malloc(sizeof(temp) * sizeof(char));
if ( words[lowest] == NULL ) {
fputs("Unable to allocate Memory", stderr);
exit(1);
}
strcpy(words[lowest], temp);
}
}
// Print out array
fputs("--- ARRAY ---\n\n", stdout);
for (int i = 0; i < c; i++) {
fputs(words[i], stdout);
}
exit(0);
}
排序算法的上限是length
,这是不正确的,因为length
是输入行缓冲区的大小。
for (int i = 0; i < (length-1); i++) {
上限应该是 i
来自这里的外部范围,因为当新行添加到数组时它会递增:
strcpy(words[i], line);
i++;
这个上限
for (int i = 0; i < c; i++) {
也应该更改,因为有效行数可能与预期行数不匹配。
完成后不要忘记释放内存。
这两行很快造成内存泄漏(words[i]
的原始指针值丢失),然后设置一个 use-after-free 错误,因为 words[i]
的新值是与 words[lowest]
相同的指针值,已被释放。
words[i] = words[lowest];
free(words[lowest]);
这里不需要额外的缓冲区、(解除)分配和字符串复制。例如,如果您对整数数组进行排序,只需交换指针值即可。
char *tmp = words[i];
words[i] = words[lowest];
words[lowest] = tmp;
这是一个常见的粗略示例,没有错误处理,使用 strdup
。它应该说明交换指针值,并确定数组的长度。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINES 256
int main(void) {
char line[4096];
char **lines = malloc(sizeof *lines * MAX_LINES);
size_t len = 0;
while (len < MAX_LINES && fgets(line, sizeof line, stdin))
lines[len++] = strdup(line);
for (size_t i = 0; i < len - 1; i++) {
size_t lowest = i;
for (size_t j = i + 1; j < len; j++)
if (strcmp(lines[j], lines[lowest]) < 0)
lowest = j;
if (lowest != i) {
char *tmp = lines[i];
lines[i] = lines[lowest];
lines[lowest] = tmp;
}
}
for (size_t i = 0; i < len; i++) {
printf("%s", lines[i]);
free(lines[i]);
}
free(lines);
}
stdin
:
one
two
hello
foo
world
^D
stdout
:
foo
hello
one
two
world
我是 C 的新手,正在尝试从文件中读取输入数据流,将其附加到动态大小的数组,这在这一点上效果很好。 之后,我想按字母顺序对数组进行排序。我阅读了 strcmp 的联机帮助页,认为这是执行此操作的最佳方法。但是,每当我尝试执行它时,我都会遇到“分段错误”。分配内存时我到底做错了什么? 这是我的代码供参考:
int main (int argc, char* argv[]) {
int c = getLineCount();
FILE * wordlist = fopen("test", "r");
// If file doesn't exist, handle error
if ( wordlist == NULL ) {
fputs("Unable to open input file", stderr);
exit(1);
}
int length = 101;
char line[length];
int i = 0;
char **words = malloc(c * sizeof(line));
if ( words == NULL ) {
fputs("Unable to allocate Memory", stderr);
exit(1);
}
while (1) {
char *l = fgets(line, length, wordlist);
if ( (l == NULL) ) {
// Check if EOF is reached
if (feof(wordlist)) {
// fputs("--- EOF ---\n", stdout);
break;
// Check if error occured while reading
} else {
fputs("Error reading file", stderr);
exit(1);
}
} else if (strchr(line, '\n') == NULL) {
// Check if line is too long
// Iterate until newline is found or until EOF
int c;
while((c = fgetc(wordlist)) != '\n' && c != 0);
fputs("--- LINE TOO LONG ---\n", stderr);
continue;
} else if ( line[0] == '\n' ) {
// Check if line is only "\n", if yes, ignore the line
continue;
} else {
words[i] = malloc(sizeof(line) * sizeof(char));
if ( words[i] == NULL ) {
fputs("Unable to allocate Memory", stderr);
exit(1);
}
strcpy(words[i], line);
i++;
}
}
// Close file
fclose(wordlist);
char temp[101];
for (int i = 0; i < (length-1); i++) {
int lowest = i;
for (int j = i+1; j < length; j++) {
if (strcmp(words[j], words[lowest]) < 0) {
lowest = j;
}
}
if (lowest != i) {
strcpy(temp, words[i]);
words[i] = words[lowest];
free(words[lowest]);
words[lowest] = malloc(sizeof(temp) * sizeof(char));
if ( words[lowest] == NULL ) {
fputs("Unable to allocate Memory", stderr);
exit(1);
}
strcpy(words[lowest], temp);
}
}
// Print out array
fputs("--- ARRAY ---\n\n", stdout);
for (int i = 0; i < c; i++) {
fputs(words[i], stdout);
}
exit(0);
}
排序算法的上限是length
,这是不正确的,因为length
是输入行缓冲区的大小。
for (int i = 0; i < (length-1); i++) {
上限应该是 i
来自这里的外部范围,因为当新行添加到数组时它会递增:
strcpy(words[i], line);
i++;
这个上限
for (int i = 0; i < c; i++) {
也应该更改,因为有效行数可能与预期行数不匹配。
完成后不要忘记释放内存。
这两行很快造成内存泄漏(words[i]
的原始指针值丢失),然后设置一个 use-after-free 错误,因为 words[i]
的新值是与 words[lowest]
相同的指针值,已被释放。
words[i] = words[lowest];
free(words[lowest]);
这里不需要额外的缓冲区、(解除)分配和字符串复制。例如,如果您对整数数组进行排序,只需交换指针值即可。
char *tmp = words[i];
words[i] = words[lowest];
words[lowest] = tmp;
这是一个常见的粗略示例,没有错误处理,使用 strdup
。它应该说明交换指针值,并确定数组的长度。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINES 256
int main(void) {
char line[4096];
char **lines = malloc(sizeof *lines * MAX_LINES);
size_t len = 0;
while (len < MAX_LINES && fgets(line, sizeof line, stdin))
lines[len++] = strdup(line);
for (size_t i = 0; i < len - 1; i++) {
size_t lowest = i;
for (size_t j = i + 1; j < len; j++)
if (strcmp(lines[j], lines[lowest]) < 0)
lowest = j;
if (lowest != i) {
char *tmp = lines[i];
lines[i] = lines[lowest];
lines[lowest] = tmp;
}
}
for (size_t i = 0; i < len; i++) {
printf("%s", lines[i]);
free(lines[i]);
}
free(lines);
}
stdin
:
one
two
hello
foo
world
^D
stdout
:
foo
hello
one
two
world