此代码的问题在于,在用户在命令行中输入某些文本后,它实际上不会打印任何内容。
代码的目的是接受用户在文件名后通过命令提示符输入的行数。然后用户将输入内容进行反转。该程序应该反转每一行的用户输入。
示例输入=大红狗
示例输出=狗红色大
#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] != '\0';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
答案 0 :(得分:6)
It should come without surprise that reverseWords
prints nothing. Why?
char * reverseWords(char *line){
...
char *temp, *word;
...
line = temp;
return line;
} //end procedure
Where is line
pointing? (to temp
). Where was temp
declared? (in reverseWords
). How much storage was allocated to temp
(none -- it is an uninitialized pointer)
Further, what happens to the memory associated with the function reverseWords
when it returns? (it's destroyed...), so even if you had done something like char temp[strlen(line)+1] = "";
, reverseWords
would venture off into Undefined Behavior because the pointer you return, points somewhere within the reverseWords
stack frame that was destroyed when reverseWords
returned...
How do you fix this? You have three options, (1) pass a second pointer to a second array with sufficient storage, e.g.:
char *revwords (char *rline, char *line)
or, (2) dynamically allocate storage for temp
so that the memory associated with temp
survives the return of reverseWords
, or
(3) use an adequately sized array for temp
in reverseWords
and overwrite line
with the data in temp
before the return. (e.g. use strcpy
instead of the assignment line = temp;
)
While dynamic allocation is straight forward, and creating a separate array in reverseWords
is fine, you are probably better passing a second sufficiently sized array as a parameter to reverseWords
.
It is completely unclear what you are doing with the argc
and argv
arguments in your code, the arguments to main
have been omitted from the example below. The following is a short example of reversing the words in each line read from 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 */
}
Note: if there is no '\n'
present in line
when passed to revwords
, you are likely trying to read a line longer than SIZE
chars (or you are reading the last line where there is no POSIX '\n'
at the end of the file), and you need to handle that as required. Here I simply warn.
Example 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
Look things over and let me know if you have any questions. There are dozens of ways to approach this problem, no one more right than another if they properly handle the reversal in a reasonable efficient manner. Take your pick.
If you like using the string library functions instead of pointer arithmetic, you could always do something like the following:
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 */
}
Note: While not an error, the standard coding style for C avoids the use of caMelCase
or MixedCase
variable or funciton names in favor of all lower-case while reserving upper-case names for use with macros and constants. Leave caMelCase
or MixedCase
for java or C++. (it's style, so it's your choice, but it does say something about your code on first impression)
答案 1 :(得分:1)
似乎你喜欢困难。
为您的目的采用此
#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;
}
答案 2 :(得分:0)
#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));
}
}
答案 3 :(得分:0)
如果您使用了更多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] = '\0';
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] = '\0';
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字符,并检查返回值是否为NULL(表示失败)。但是INITALLOC
已分配给nallocWord
,正如我们之前所说,它是分配给word
的字符数。
AND右边的部分为ret
分配INITALLOC字符,并检查返回值是否为NULL。但INITALLOC
已分配给nallocRet
。请注意,我们使用了calloc
函数而不是malloc
。不同之处在于calloc
零初始化其返回值,但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
的末尾,并增加字符数{ {1}}。在其中,我们检查nWord
是否已用尽,在这种情况下,内存将被重新分配。如果重新分配失败,我们将返回word
。重新分配的工作原理是NULL
增加nallocWord
,这是我们将调整缓冲区大小的字符数。
如果我们介于单词(ALLOCSTEP
)之间,或者如果我们位于行inword == IN && isspace(line[i]
的末尾,那么我们会将(i == 0)
更改为inword
,null-terminate OUT
,并通过调用word
来反转它。我们的下一步是将revword
添加到word
的末尾。但是,我们必须首先检查是否有足够的空间进行连接。条件ret
会检查nRet + nWord > nallocRet
中的字符数加上ret
中的字符数是否超过word
,这是为nallocRet
分配的字符数缓冲。如果条件为真,则重新分配内存。如果重新分配失败,我们将返回ret
。我们需要检查NULL
,因为当循环即将完成时,我们希望将最后一个单词推送到i == 0
。
现在,我们可以通过调用ret
将word
追加到ret
。我们还添加了一个空格,这样单词之间就会有空格。
nRet已更新为strcat
中的新字符数。 ret
用于说明单词之间的空格。 nWord设置为0,因此下一个循环迭代将覆盖不再需要的+ 1
的旧内容。
循环完成后,我们会释放word
,因为它需要更长时间,然后删除word
末尾的尾随空格。然后我们返回ret
。顺便说一下,呼叫者有责任释放这种记忆。对于ret
/ malloc
的每次通话,必须有相应的calloc
。
现在让我们转向free
,这是反转字符串的函数。
revword
该函数使用两个字符指针char *revword(char *word)
{
char *p, *q;
assert(word != NULL);
assert(*word != '\0');
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
。
现在,我将展示我改变的word
的一点点。
main
这是在循环fgets(str, SIZE, stdin);
str[strlen(str) - 1] = '\0';
char *myrev(const char *line);
char *res = myrev(str);
printf("%s", res);
free(res);
内。
我们必须从for (i = 0; i < N; i++)
缓冲区中删除尾随换行符,其中str
留在那里。然后我们声明fgets
函数,然后将myrev
的返回值存储在临时函数中,这样我们就可以在myrev
时使用该指针。