在 c 中反转数组 - 不会打印 -
Reversing array in c - will not print -
此代码的问题在于,当用户在命令行中输入一些文本后,它实际上并没有打印任何内容。
代码的目的是接受用户在文件名后通过命令提示符输入的行数。然后用户将输入一些东西来反转。该程序应该反转每一行的用户输入。
示例输入 = 大红狗
示例输出 = dog red big the
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define SIZE 80
char * reverseWords(char *string);
//argc is the count of cmd arguments.
//each command line argument is of type string
int main(int argc, char *argv[]){
//initialize local variables
int i;
int N;
char str[SIZE];
for(i = 1; i <argc; i++)
{
//set N equal to the users number in the command line
N = atoi(argv[i]);
}
if(argc != 2){//2 means that something is in the argument.
printf("ERROR: Please provide an integer greater than or equal to 0");
exit(1);//exit the program
}else if(N < 0){//We cant have a negative array size.
printf("ERROR: Please provide an integer greater than or equal to 0");
exit(1);//exit the program
}else{
for(i = 0; i < N; i++){
/*
fgets(pointer to array, max # of chars copied,stdin = input from keyboard)
*/
fgets(str,SIZE,stdin);
printf("%s", reverseWords(str)); //<---does not print anything....
}
}
return 0;
}
char * reverseWords(char *line){
//declare local strings
char *temp, *word;
//instantiate index
int index = 0;
int word_len = 0;
/*set index = to size of user input
do this by checking if the index of line is
equal to the null-character.
*/
for(int i = 0; line[i] != '[=10=]';i++)
{
index = i;//index = string length of line.
}
//check if index is less than 0.
//if not we decrement the index value.
for(index; index != -1; index--){
//checking for individual words or letters
if(line[index] == ' ' && word_len > 0){
strncpy(word,line,word_len);
strcat(temp , (word + ' '));
word_len = 0;
}else if(isalnum(line[index])){
word_len == word_len+1;
}//end if
}//end loop
//copy over the last word after the loop(if any)
if(word_len > 0){
strncpy(word,line,word_len);
strcat(temp,word);
}//end if
line = temp;
return line;
}//end procedure
看来你喜欢困难
为您的目的采用此
#include <string.h>
#include <stdio.h>
char* reverse_words(char* str);
int main() {
char arr[] = "the big red dog";
printf("%s", reverse_words(arr));
return 0;
}
char* reverse_words(char* str) {
char delim = ' '; // space
int left = 0;
int reverse_index = 0;
int right = 0;
int len = strlen(str);
char tmp;
while (left < len) {
while (str[right] != delim && right < len)
right++;
reverse_index = right - 1;
while (left < reverse_index){
tmp = str[left];
str[left] = str[reverse_index];
str[reverse_index] = tmp;
left++;
reverse_index--;
}
right++;
left = right;
}
strrev(str);
return str;
}
//output is: dog red big the
如果您由于某些原因没有 strrev,请看这里
char* strrev(char *str) {
char *p1, *p2;
if (! str || ! *str)
return str;
for (p1 = str, p2 = str + strlen(str) - 1;
p2 > p1; ++p1, --p2) {
*p1 ^= *p2;
*p2 ^= *p1;
*p1 ^= *p2;
}
return str;
}
也更清晰的方式,但也更慢
char* strrev(char *str) {
int left = 0;
int right = strlen(str) - 1;
char tmp;
while(left < right) {
tmp = str[left];
str[left] = str[right];
str[right] = tmp;
left++;
right--;
}
return str;
}
毫无疑问,reverseWords
什么都不打印。为什么?
char * reverseWords(char *line){
...
char *temp, *word;
...
line = temp;
return line;
} //end procedure
line
指向哪里? (到 temp
)。 temp
在哪里声明? (在 reverseWords
中)。分配给 temp
多少存储空间(none -- 这是一个未初始化的指针)
此外,与函数 reverseWords
关联的内存在 return 时发生了什么? (它被摧毁了......),所以即使你做了类似 char temp[strlen(line)+1] = "";
的事情,reverseWords
也会冒险进入 Undefined Behavior 因为指针你 return, 指向在 reverseWords
returned...
时被破坏的 reverseWords
堆栈帧中的某处
你如何解决这个问题?您有三个选项,(1) 将第二个指针传递给具有足够存储空间的第二个数组,例如:
char *revwords (char *rline, char *line)
或者,(2) 为 temp
动态分配 存储,以便与 temp
关联的内存在 return 的 reverseWords
,或
(3) 在 reverseWords
中为 temp
使用足够大小的数组,并在 return 之前用 temp
中的数据覆盖 line
。 (例如,使用 strcpy
而不是赋值 line = temp;
)
虽然动态分配很简单,并且在 reverseWords
中创建一个单独的数组很好,但您最好将第二个足够大小的数组作为参数传递给 reverseWords
.
完全不清楚您在代码中使用 argc
和 argv
参数做什么,下面的示例中省略了 main
的参数。下面是一个简短的例子,将从 stdin
,
读取的每行中的单词反转
#include <stdio.h>
#include <string.h>
#define SIZE 256
char *revwords (char *rline, char *line);
int main (void) {
char line[SIZE] = "", rline[SIZE] = ""; /* storage for line/rline */
while (fgets (line, SIZE, stdin)) { /* for each line on stdin */
printf ("\n line: %s\nrline: %s\n", line, revwords (rline, line));
*rline = 0; /* set first char in rline to nul-byte */
}
return 0;
}
char *revwords (char *rline, char *line)
{
size_t lnlen = strlen (line); /* length of line */
/* pointer, end-pointer, rev-pointer and flag pointer-to-space */
char *p = line + lnlen - 1, *ep = p, *rp = rline, *p2space = NULL;
if (!line || !*line) { /* validate line not NULL and not empty */
fprintf (stderr, "revwords() error: 'line' empty of null.\n");
return NULL;
}
if (*ep == '\n') /* if line ends in '\n' -- remove it */
*ep-- = 0;
else /* warn if no '\n' present in line */
fprintf (stderr, "warning: no POSIX '\n' found in line.\n");
for (; ep >= line; ep--) { /* for each char from end-to-beginning */
if (*ep == ' ') { /* is it a space? */
size_t len = p - ep; /* get the length of the word */
strncat (rp, ep + 1, len); /* concatenate word to rline */
if (p == line + lnlen - 1) /* if first word, append ' ' */
strcat (rp, " ");
p = ep; /* update p to last ' ' */
p2space = ep; /* set flag to valid pointer */
}
}
strncat (rp, line, p - line); /* handle first/last word */
if (!p2space) { /* validate line contained ' ', if not return NULL */
fprintf (stderr, "revwords() error: nothing to reverse.\n");
return NULL;
}
return rline; /* return pointer to reversed line */
}
注意: 如果在传递给 revwords
时 line
中没有 '\n'
,您可能正在尝试读取一行长于 SIZE
个字符(或者您正在阅读文件末尾没有 POSIX '\n'
的 last 行),并且您需要按要求处理。在这里我简单警告一下。
例子Use/Output
$ printf "my dog has fleas\nmy cat does too\n" | ./bin/str_rev_words
line: my dog has fleas
rline: fleas has dog my
line: my cat does too
rline: too does cat my
检查一下,如果您有任何问题,请告诉我。有许多方法可以解决这个问题,如果他们以合理有效的方式正确处理逆转,没有比另一种更正确的方法了。任你选。
如果您喜欢使用字符串库函数而不是指针运算,您可以随时执行如下操作:
char *revwords (char *rline, char *line)
{
/* length, pointer, end-pointer, pointer-to-space, copy of line */
size_t len = strlen (line);
char *p = NULL, *p2space = NULL, copy[len+1];
if (!line || !*line) { /* validate line not NULL and not empty */
fprintf (stderr, "revwords() error: 'line' empty of null.\n");
return NULL;
}
if (line[len-1] == '\n') /* remove trailing newline */
line[--len] = 0;
else /* warn if no '\n' present in line */
fprintf (stderr, "warning: no POSIX '\n' found in line.\n");
strncpy (copy, line, len + 1); /* copy line to 'copy' */
/* for each ' ' from end-to-beginning */
while ((p = strrchr (copy, ' '))) {
strcat (rline, p + 1); /* append word to rline */
strcat (rline, " "); /* followed by a space */
p2space = p; /* set p2space to p */
*p2space = 0; /* nul-terminate copy at p */
}
if (p2space) { /* validate space found in line */
*p2space = 0; /* nul-terminate at space */
strcat (rline, copy); /* concatenate first/last word */
}
else { /* no ' ' in line, return NULL */
fprintf (stderr, "revwords() error: nothing to reverse.\n");
return NULL;
}
return rline; /* return pointer to reversed line */
}
注意: 虽然不是错误,但 C 的标准编码风格避免使用 caMelCase
或 MixedCase
变量或函数名称,以支持所有小写,同时保留大写名称以用于宏和常量。为 java 或 C++ 保留 caMelCase
或 MixedCase
。 (它的风格,所以它是你的选择,但它确实在第一印象中说明了你的代码)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define SIZE 80
char *reverseWords(char line[SIZE]){
char temp[SIZE];
#if SIZE > 255
unsigned index[SIZE];
#else
unsigned char index[SIZE];
#endif
int i, index_count = 0;
int inside_word = !!isalpha((unsigned char)line[0]), word = inside_word;
for(index[index_count++] = i = 0; line[i]; ++i){//copy & make index table
unsigned char ch = temp[i] = line[i];
if(inside_word && !isalpha(ch) || !inside_word && isalpha(ch)){//Edge
index[index_count++] = i;
inside_word = !inside_word;
}
}
index[index_count] = i;
int last_word_index = index_count - 1;//last index
last_word_index -= !word ^ (last_word_index & 1);//to word
char *p =line;
for(i = 0; i < index_count-1; ++i){
int len;
if(word){
len = index[last_word_index+1] - index[last_word_index];
memcpy(p, &temp[index[last_word_index]], len);
last_word_index -= 2;
} else {
len = index[i+1] - index[i];
memcpy(p, &temp[index[i]], len);
}
word = !word;
p += len;
}
return line;
}
int main(void){
char str[SIZE];
while(fgets(str, sizeof str, stdin)){
printf("%s", reverseWords(str));
}
}
如果您使用更多 string.h
函数,例如 strlen
,您的问题将会得到简化。此外,您必须使用 malloc
和 calloc
动态分配内存——这里不能使用固定大小的缓冲区。
我现在提出修改后的reverseWords
。
char *myrev(const char *line)
{
char *revword(char *);
size_t i = strlen(line);
int inword = OUT;
size_t nWord = 0, nallocWord;
char *word; // will store the word
size_t nRet = 0, nallocRet;
char *ret; // will store the entire line, but reversed
// establish preconditions
assert(i > 0);
assert(line != NULL);
// alloc memory for word and ret
if ((word = malloc(nallocWord = INITALLOC)) != NULL &&
(ret = calloc(nallocRet = INITALLOC, sizeof(char))) != NULL) {
// walk backwards through line
while (i--) {
if (inword == OUT && isalnum(line[i]))
inword = IN; // we just entered a word
if (inword == IN && isalnum(line[i])) {
// we're inside a word; append current char to the word buffer
word[nWord++] = line[i];
// word buffer exhausted; reallocate
if (nWord == nallocWord)
if ((word = realloc(word, nallocWord += ALLOCSTEP)) == NULL)
return NULL;
}
// if we are in between words or at the end of the line
if (i == 0 || inword == IN && isspace(line[i])) {
inword = OUT;
word[nWord] = '[=10=]';
word = revword(word);
// ret buffer exhausted; reallocate
if (nRet + nWord > nallocRet)
if ((ret = realloc(ret, nallocRet += ALLOCSTEP)) == NULL)
return NULL;
// append word to ret
strcat(ret, word);
strcat(ret, " ");
nRet += nWord + 1;
nWord = 0;
}
}
free(word);
// remove trailing blank
ret[strlen(ret) - 1] = '[=10=]';
return ret;
}
// in case of mem alloc failure
return NULL;
}
我现在来解释一下这个函数的操作。
第一行声明函数revwords
,稍后我会展示。
下一行是变量定义。变量 i
将用作向后遍历的迭代器。我们将其初始化为 line
字符串的长度,包括零终止符。
变量inword
很重要。它用于跟踪我们是否在一个单词中。它将被分配两个常量之一:IN
和 OUT
。
#define IN 0 /* inside a word */
#define OUT 1 /* outside a word */
nWord
和nallocWord
变量分别是word
缓冲区中的字符数,以及为word
分配了多少内存。 word
是我们积累单词的地方。由于输入行将被向后解析,因此 word
缓冲区最初将向后,但我们稍后会反转它。
变量nRet
和nallocRet
的作用相似:分别是ret
缓冲区中的字符数和分配给ret
的字符数。 ret
是我们将存储整个输入行的缓冲区,但每个单词的位置都颠倒了。
然后我们强制执行两个先决条件:字符串的长度必须为正,并且 line
输入缓冲区不能为 NULL。我们通过使用 <assert.h>
.
中的 assert
宏来强制执行这些操作
我们现在进入函数的核心部分。我们在此函数中的策略是先为 word
和 ret
缓冲区占用一定数量的内存,然后在需要时增加缓冲区的大小。所以我们就是这么做的。
行
if ((word = malloc(nallocWord = INITALLOC)) != NULL &&
(ret = calloc(nallocRet = INITALLOC, sizeof(char))) != NULL) {
一开始看起来很可怕,但如果我们把它分成两部分,那就容易多了。 AND 运算符左边的部分为word
分配INITALLOC 字符,并检查return 值是否不为NULL(表示失败)。但是INITALLOC
分配给了nallocWord
,也就是我们前面说的分配给word
的字符数。
AND右边的部分为ret
分配INITALLOC字符,并检查return值是否不为NULL。但是 INITALLOC
被分配给了 nallocRet
。请注意,我们使用了 calloc
函数而不是 malloc
。区别在于 calloc
零初始化它的 return 值,而 malloc
没有。我们需要将 ret
缓冲区初始化为零;稍后你会明白为什么。
#define INITALLOC 16 /* number of characters initially alloc'ed */
#define ALLOCSTEP 32 /* number of characters to increase by */
这些宏的值并不重要,但您仍应为它们选择合理的值,以免执行太多(缓慢的)重新分配。
无论如何,在这个 if
语句中,我们有 while 循环从末尾开始迭代字符串 line
。 while 循环由一系列测试组成。
如果我们在一个词外(inword == OUT
)并且当前字符(line[i]
)是字母数字(即一个词内的一个字符),那么我们改变inword
到 IN
。控制将落入下一个if
,即
如果我们在一个单词(inword == IN
)里面并且当前字符是一个单词字符,那么我们把当前字符添加到word
的末尾,并增加字符数 nWord
。在其中,我们检查 word
是否已用完,在这种情况下会重新分配内存。如果重新分配失败,我们 return NULL
。重新分配通过将 nallocWord
增加 ALLOCSTEP
来实现,这是我们将调整缓冲区大小的字符数。
如果我们在单词之间 (inword == IN && isspace(line[i]
),或者如果我们在行尾 (i == 0)
,那么我们将 inword
更改为 OUT
,空终止 word
,并通过调用 revword
反转它。我们的下一步是将 word
添加到 ret
的末尾。但是,我们必须首先检查是否有足够的 space 来进行连接。条件nRet + nWord > nallocRet
检查ret
中的字符数加上word
中的字符数是否超过nallocRet
,即分配给ret
的字符数缓冲。如果条件为真,则重新分配内存。如果重新分配失败,我们 return NULL
。我们需要检查 i == 0
,因为当循环即将结束时,我们想将最后一个词推入 ret
.
现在,我们可以通过调用 strcat
将 word
附加到 ret
。我们还添加了一个space,这样单词之间就会有spaces。
nRet 更新为 ret
中的新字符数。 + 1
是为了说明单词之间的 space。 nWord 设置为 0,因此下一次循环迭代将覆盖不再需要的 word
的旧内容。
循环完成后,我们释放word
,因为它不再需要,然后删除ret
末尾的space。然后我们returnret
。顺便说一下,调用者有责任释放这段内存。对于 malloc
/calloc
的每个调用,必须有一个相应的 free
.
现在让我们转到revword
,它是反转字符串的函数。
char *revword(char *word)
{
char *p, *q;
assert(word != NULL);
assert(*word != '[=14=]');
for (p = word, q = word + strlen(word) - 1; q > p; ++p, --q) {
char tmp;
tmp = *p;
*p = *q;
*q = tmp;
}
return word;
}
该函数使用两个字符指针,p
和 q
。 p
被分配指向 word
的开始,而 q
被分配指向 word
的结束。 p
指针在每次循环迭代时递增,q
递减,而q
大于p
。在循环体内,我们交换 p
和 q
.
指向的值
最后,我们return反转word
。
现在,我将展示我修改的main
的一点点。
fgets(str, SIZE, stdin);
str[strlen(str) - 1] = '[=15=]';
char *myrev(const char *line);
char *res = myrev(str);
printf("%s", res);
free(res);
这是在循环内部 for (i = 0; i < N; i++)
。
我们必须从 str
缓冲区中删除结尾的换行符,fgets
留在了那里。然后我们声明了myrev
函数,接下来把myrev
的return值存储在一个临时的,这样我们free
的时候就可以使用这个指针了[=120] =]
此代码的问题在于,当用户在命令行中输入一些文本后,它实际上并没有打印任何内容。
代码的目的是接受用户在文件名后通过命令提示符输入的行数。然后用户将输入一些东西来反转。该程序应该反转每一行的用户输入。
示例输入 = 大红狗
示例输出 = dog red big the
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define SIZE 80
char * reverseWords(char *string);
//argc is the count of cmd arguments.
//each command line argument is of type string
int main(int argc, char *argv[]){
//initialize local variables
int i;
int N;
char str[SIZE];
for(i = 1; i <argc; i++)
{
//set N equal to the users number in the command line
N = atoi(argv[i]);
}
if(argc != 2){//2 means that something is in the argument.
printf("ERROR: Please provide an integer greater than or equal to 0");
exit(1);//exit the program
}else if(N < 0){//We cant have a negative array size.
printf("ERROR: Please provide an integer greater than or equal to 0");
exit(1);//exit the program
}else{
for(i = 0; i < N; i++){
/*
fgets(pointer to array, max # of chars copied,stdin = input from keyboard)
*/
fgets(str,SIZE,stdin);
printf("%s", reverseWords(str)); //<---does not print anything....
}
}
return 0;
}
char * reverseWords(char *line){
//declare local strings
char *temp, *word;
//instantiate index
int index = 0;
int word_len = 0;
/*set index = to size of user input
do this by checking if the index of line is
equal to the null-character.
*/
for(int i = 0; line[i] != '[=10=]';i++)
{
index = i;//index = string length of line.
}
//check if index is less than 0.
//if not we decrement the index value.
for(index; index != -1; index--){
//checking for individual words or letters
if(line[index] == ' ' && word_len > 0){
strncpy(word,line,word_len);
strcat(temp , (word + ' '));
word_len = 0;
}else if(isalnum(line[index])){
word_len == word_len+1;
}//end if
}//end loop
//copy over the last word after the loop(if any)
if(word_len > 0){
strncpy(word,line,word_len);
strcat(temp,word);
}//end if
line = temp;
return line;
}//end procedure
看来你喜欢困难
为您的目的采用此
#include <string.h>
#include <stdio.h>
char* reverse_words(char* str);
int main() {
char arr[] = "the big red dog";
printf("%s", reverse_words(arr));
return 0;
}
char* reverse_words(char* str) {
char delim = ' '; // space
int left = 0;
int reverse_index = 0;
int right = 0;
int len = strlen(str);
char tmp;
while (left < len) {
while (str[right] != delim && right < len)
right++;
reverse_index = right - 1;
while (left < reverse_index){
tmp = str[left];
str[left] = str[reverse_index];
str[reverse_index] = tmp;
left++;
reverse_index--;
}
right++;
left = right;
}
strrev(str);
return str;
}
//output is: dog red big the
如果您由于某些原因没有 strrev,请看这里
char* strrev(char *str) {
char *p1, *p2;
if (! str || ! *str)
return str;
for (p1 = str, p2 = str + strlen(str) - 1;
p2 > p1; ++p1, --p2) {
*p1 ^= *p2;
*p2 ^= *p1;
*p1 ^= *p2;
}
return str;
}
也更清晰的方式,但也更慢
char* strrev(char *str) {
int left = 0;
int right = strlen(str) - 1;
char tmp;
while(left < right) {
tmp = str[left];
str[left] = str[right];
str[right] = tmp;
left++;
right--;
}
return str;
}
毫无疑问,reverseWords
什么都不打印。为什么?
char * reverseWords(char *line){
...
char *temp, *word;
...
line = temp;
return line;
} //end procedure
line
指向哪里? (到 temp
)。 temp
在哪里声明? (在 reverseWords
中)。分配给 temp
多少存储空间(none -- 这是一个未初始化的指针)
此外,与函数 reverseWords
关联的内存在 return 时发生了什么? (它被摧毁了......),所以即使你做了类似 char temp[strlen(line)+1] = "";
的事情,reverseWords
也会冒险进入 Undefined Behavior 因为指针你 return, 指向在 reverseWords
returned...
reverseWords
堆栈帧中的某处
你如何解决这个问题?您有三个选项,(1) 将第二个指针传递给具有足够存储空间的第二个数组,例如:
char *revwords (char *rline, char *line)
或者,(2) 为 temp
动态分配 存储,以便与 temp
关联的内存在 return 的 reverseWords
,或
(3) 在 reverseWords
中为 temp
使用足够大小的数组,并在 return 之前用 temp
中的数据覆盖 line
。 (例如,使用 strcpy
而不是赋值 line = temp;
)
虽然动态分配很简单,并且在 reverseWords
中创建一个单独的数组很好,但您最好将第二个足够大小的数组作为参数传递给 reverseWords
.
完全不清楚您在代码中使用 argc
和 argv
参数做什么,下面的示例中省略了 main
的参数。下面是一个简短的例子,将从 stdin
,
#include <stdio.h>
#include <string.h>
#define SIZE 256
char *revwords (char *rline, char *line);
int main (void) {
char line[SIZE] = "", rline[SIZE] = ""; /* storage for line/rline */
while (fgets (line, SIZE, stdin)) { /* for each line on stdin */
printf ("\n line: %s\nrline: %s\n", line, revwords (rline, line));
*rline = 0; /* set first char in rline to nul-byte */
}
return 0;
}
char *revwords (char *rline, char *line)
{
size_t lnlen = strlen (line); /* length of line */
/* pointer, end-pointer, rev-pointer and flag pointer-to-space */
char *p = line + lnlen - 1, *ep = p, *rp = rline, *p2space = NULL;
if (!line || !*line) { /* validate line not NULL and not empty */
fprintf (stderr, "revwords() error: 'line' empty of null.\n");
return NULL;
}
if (*ep == '\n') /* if line ends in '\n' -- remove it */
*ep-- = 0;
else /* warn if no '\n' present in line */
fprintf (stderr, "warning: no POSIX '\n' found in line.\n");
for (; ep >= line; ep--) { /* for each char from end-to-beginning */
if (*ep == ' ') { /* is it a space? */
size_t len = p - ep; /* get the length of the word */
strncat (rp, ep + 1, len); /* concatenate word to rline */
if (p == line + lnlen - 1) /* if first word, append ' ' */
strcat (rp, " ");
p = ep; /* update p to last ' ' */
p2space = ep; /* set flag to valid pointer */
}
}
strncat (rp, line, p - line); /* handle first/last word */
if (!p2space) { /* validate line contained ' ', if not return NULL */
fprintf (stderr, "revwords() error: nothing to reverse.\n");
return NULL;
}
return rline; /* return pointer to reversed line */
}
注意: 如果在传递给 revwords
时 line
中没有 '\n'
,您可能正在尝试读取一行长于 SIZE
个字符(或者您正在阅读文件末尾没有 POSIX '\n'
的 last 行),并且您需要按要求处理。在这里我简单警告一下。
例子Use/Output
$ printf "my dog has fleas\nmy cat does too\n" | ./bin/str_rev_words
line: my dog has fleas
rline: fleas has dog my
line: my cat does too
rline: too does cat my
检查一下,如果您有任何问题,请告诉我。有许多方法可以解决这个问题,如果他们以合理有效的方式正确处理逆转,没有比另一种更正确的方法了。任你选。
如果您喜欢使用字符串库函数而不是指针运算,您可以随时执行如下操作:
char *revwords (char *rline, char *line)
{
/* length, pointer, end-pointer, pointer-to-space, copy of line */
size_t len = strlen (line);
char *p = NULL, *p2space = NULL, copy[len+1];
if (!line || !*line) { /* validate line not NULL and not empty */
fprintf (stderr, "revwords() error: 'line' empty of null.\n");
return NULL;
}
if (line[len-1] == '\n') /* remove trailing newline */
line[--len] = 0;
else /* warn if no '\n' present in line */
fprintf (stderr, "warning: no POSIX '\n' found in line.\n");
strncpy (copy, line, len + 1); /* copy line to 'copy' */
/* for each ' ' from end-to-beginning */
while ((p = strrchr (copy, ' '))) {
strcat (rline, p + 1); /* append word to rline */
strcat (rline, " "); /* followed by a space */
p2space = p; /* set p2space to p */
*p2space = 0; /* nul-terminate copy at p */
}
if (p2space) { /* validate space found in line */
*p2space = 0; /* nul-terminate at space */
strcat (rline, copy); /* concatenate first/last word */
}
else { /* no ' ' in line, return NULL */
fprintf (stderr, "revwords() error: nothing to reverse.\n");
return NULL;
}
return rline; /* return pointer to reversed line */
}
注意: 虽然不是错误,但 C 的标准编码风格避免使用 caMelCase
或 MixedCase
变量或函数名称,以支持所有小写,同时保留大写名称以用于宏和常量。为 java 或 C++ 保留 caMelCase
或 MixedCase
。 (它的风格,所以它是你的选择,但它确实在第一印象中说明了你的代码)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define SIZE 80
char *reverseWords(char line[SIZE]){
char temp[SIZE];
#if SIZE > 255
unsigned index[SIZE];
#else
unsigned char index[SIZE];
#endif
int i, index_count = 0;
int inside_word = !!isalpha((unsigned char)line[0]), word = inside_word;
for(index[index_count++] = i = 0; line[i]; ++i){//copy & make index table
unsigned char ch = temp[i] = line[i];
if(inside_word && !isalpha(ch) || !inside_word && isalpha(ch)){//Edge
index[index_count++] = i;
inside_word = !inside_word;
}
}
index[index_count] = i;
int last_word_index = index_count - 1;//last index
last_word_index -= !word ^ (last_word_index & 1);//to word
char *p =line;
for(i = 0; i < index_count-1; ++i){
int len;
if(word){
len = index[last_word_index+1] - index[last_word_index];
memcpy(p, &temp[index[last_word_index]], len);
last_word_index -= 2;
} else {
len = index[i+1] - index[i];
memcpy(p, &temp[index[i]], len);
}
word = !word;
p += len;
}
return line;
}
int main(void){
char str[SIZE];
while(fgets(str, sizeof str, stdin)){
printf("%s", reverseWords(str));
}
}
如果您使用更多 string.h
函数,例如 strlen
,您的问题将会得到简化。此外,您必须使用 malloc
和 calloc
动态分配内存——这里不能使用固定大小的缓冲区。
我现在提出修改后的reverseWords
。
char *myrev(const char *line)
{
char *revword(char *);
size_t i = strlen(line);
int inword = OUT;
size_t nWord = 0, nallocWord;
char *word; // will store the word
size_t nRet = 0, nallocRet;
char *ret; // will store the entire line, but reversed
// establish preconditions
assert(i > 0);
assert(line != NULL);
// alloc memory for word and ret
if ((word = malloc(nallocWord = INITALLOC)) != NULL &&
(ret = calloc(nallocRet = INITALLOC, sizeof(char))) != NULL) {
// walk backwards through line
while (i--) {
if (inword == OUT && isalnum(line[i]))
inword = IN; // we just entered a word
if (inword == IN && isalnum(line[i])) {
// we're inside a word; append current char to the word buffer
word[nWord++] = line[i];
// word buffer exhausted; reallocate
if (nWord == nallocWord)
if ((word = realloc(word, nallocWord += ALLOCSTEP)) == NULL)
return NULL;
}
// if we are in between words or at the end of the line
if (i == 0 || inword == IN && isspace(line[i])) {
inword = OUT;
word[nWord] = '[=10=]';
word = revword(word);
// ret buffer exhausted; reallocate
if (nRet + nWord > nallocRet)
if ((ret = realloc(ret, nallocRet += ALLOCSTEP)) == NULL)
return NULL;
// append word to ret
strcat(ret, word);
strcat(ret, " ");
nRet += nWord + 1;
nWord = 0;
}
}
free(word);
// remove trailing blank
ret[strlen(ret) - 1] = '[=10=]';
return ret;
}
// in case of mem alloc failure
return NULL;
}
我现在来解释一下这个函数的操作。
第一行声明函数revwords
,稍后我会展示。
下一行是变量定义。变量 i
将用作向后遍历的迭代器。我们将其初始化为 line
字符串的长度,包括零终止符。
变量inword
很重要。它用于跟踪我们是否在一个单词中。它将被分配两个常量之一:IN
和 OUT
。
#define IN 0 /* inside a word */
#define OUT 1 /* outside a word */
nWord
和nallocWord
变量分别是word
缓冲区中的字符数,以及为word
分配了多少内存。 word
是我们积累单词的地方。由于输入行将被向后解析,因此 word
缓冲区最初将向后,但我们稍后会反转它。
变量nRet
和nallocRet
的作用相似:分别是ret
缓冲区中的字符数和分配给ret
的字符数。 ret
是我们将存储整个输入行的缓冲区,但每个单词的位置都颠倒了。
然后我们强制执行两个先决条件:字符串的长度必须为正,并且 line
输入缓冲区不能为 NULL。我们通过使用 <assert.h>
.
assert
宏来强制执行这些操作
我们现在进入函数的核心部分。我们在此函数中的策略是先为 word
和 ret
缓冲区占用一定数量的内存,然后在需要时增加缓冲区的大小。所以我们就是这么做的。
行
if ((word = malloc(nallocWord = INITALLOC)) != NULL &&
(ret = calloc(nallocRet = INITALLOC, sizeof(char))) != NULL) {
一开始看起来很可怕,但如果我们把它分成两部分,那就容易多了。 AND 运算符左边的部分为word
分配INITALLOC 字符,并检查return 值是否不为NULL(表示失败)。但是INITALLOC
分配给了nallocWord
,也就是我们前面说的分配给word
的字符数。
AND右边的部分为ret
分配INITALLOC字符,并检查return值是否不为NULL。但是 INITALLOC
被分配给了 nallocRet
。请注意,我们使用了 calloc
函数而不是 malloc
。区别在于 calloc
零初始化它的 return 值,而 malloc
没有。我们需要将 ret
缓冲区初始化为零;稍后你会明白为什么。
#define INITALLOC 16 /* number of characters initially alloc'ed */
#define ALLOCSTEP 32 /* number of characters to increase by */
这些宏的值并不重要,但您仍应为它们选择合理的值,以免执行太多(缓慢的)重新分配。
无论如何,在这个 if
语句中,我们有 while 循环从末尾开始迭代字符串 line
。 while 循环由一系列测试组成。
如果我们在一个词外(
inword == OUT
)并且当前字符(line[i]
)是字母数字(即一个词内的一个字符),那么我们改变inword
到IN
。控制将落入下一个if
,即如果我们在一个单词(
inword == IN
)里面并且当前字符是一个单词字符,那么我们把当前字符添加到word
的末尾,并增加字符数nWord
。在其中,我们检查word
是否已用完,在这种情况下会重新分配内存。如果重新分配失败,我们 returnNULL
。重新分配通过将nallocWord
增加ALLOCSTEP
来实现,这是我们将调整缓冲区大小的字符数。如果我们在单词之间 (
inword == IN && isspace(line[i]
),或者如果我们在行尾(i == 0)
,那么我们将inword
更改为OUT
,空终止word
,并通过调用revword
反转它。我们的下一步是将word
添加到ret
的末尾。但是,我们必须首先检查是否有足够的 space 来进行连接。条件nRet + nWord > nallocRet
检查ret
中的字符数加上word
中的字符数是否超过nallocRet
,即分配给ret
的字符数缓冲。如果条件为真,则重新分配内存。如果重新分配失败,我们 returnNULL
。我们需要检查i == 0
,因为当循环即将结束时,我们想将最后一个词推入ret
.
现在,我们可以通过调用 strcat
将 word
附加到 ret
。我们还添加了一个space,这样单词之间就会有spaces。
nRet 更新为 ret
中的新字符数。 + 1
是为了说明单词之间的 space。 nWord 设置为 0,因此下一次循环迭代将覆盖不再需要的 word
的旧内容。
循环完成后,我们释放word
,因为它不再需要,然后删除ret
末尾的space。然后我们returnret
。顺便说一下,调用者有责任释放这段内存。对于 malloc
/calloc
的每个调用,必须有一个相应的 free
.
现在让我们转到revword
,它是反转字符串的函数。
char *revword(char *word)
{
char *p, *q;
assert(word != NULL);
assert(*word != '[=14=]');
for (p = word, q = word + strlen(word) - 1; q > p; ++p, --q) {
char tmp;
tmp = *p;
*p = *q;
*q = tmp;
}
return word;
}
该函数使用两个字符指针,p
和 q
。 p
被分配指向 word
的开始,而 q
被分配指向 word
的结束。 p
指针在每次循环迭代时递增,q
递减,而q
大于p
。在循环体内,我们交换 p
和 q
.
最后,我们return反转word
。
现在,我将展示我修改的main
的一点点。
fgets(str, SIZE, stdin);
str[strlen(str) - 1] = '[=15=]';
char *myrev(const char *line);
char *res = myrev(str);
printf("%s", res);
free(res);
这是在循环内部 for (i = 0; i < N; i++)
。
我们必须从 str
缓冲区中删除结尾的换行符,fgets
留在了那里。然后我们声明了myrev
函数,接下来把myrev
的return值存储在一个临时的,这样我们free
的时候就可以使用这个指针了[=120] =]