有人可以帮我理解下面程序中的这些代码行 这个程序根据作者写的一串hello world然后有一个函数,它也将字符串转换为世界问候,我的任务是这个代码做什么?
char * p_divs = divs; //what does divs do
char tmp;
while(tmp = *p_divs++)
if (tmp == c) return 1
也是void函数中的这段代码
*dest = '\0';//what does this pointer do?
int source_len = strlen(source); //what is source
if (source_len == 0) return;
char * p_source = source + source_len - 1;
char * p_dest = dest;
while(p_source >= source){
while((p_source >= source) && (inDiv(*p_source, divs))) p_source--;
这是主程序
#include <stdio.h>
#include <string.h>
int inDiv(char c, char * divs){
char * p_divs = divs;
char tmp;
while(tmp = *p_divs++)
if (tmp == c) return 1;
return 0;
}
void reverse(char * source, char * dest, char * divs){
*dest = '\0';
int source_len = strlen(source);
if (source_len == 0) return;
char * p_source = source + source_len - 1;
char * p_dest = dest;
while(p_source >= source){
while((p_source >= source) && (inDiv(*p_source, divs))) p_source--;
if (p_source < source) break;
char * w_end = p_source;
while((p_source >= source) && (!inDiv(*p_source, divs))) p_source--;
char * w_beg = p_source + 1;
for(char * p = w_beg; p <= w_end; p++) *p_dest++ = *p;
*p_dest++ = ' ';
}
*p_dest = '\0';
}
#define MAS_SIZE 100
int main(){
char source[MAS_SIZE], dest[MAS_SIZE], divs[MAS_SIZE];
printf("String : "); gets(source);
printf("Dividers : "); gets(divs);
reverse(source, dest, divs);
printf("Reversed string : %s", dest);
return 0;
}
答案 0 :(得分:4)
在这里,可以调用inDiv来搜索字符串c
中的字符divs
,例如:
inDiv('x', "is there an x character in here somewhere?') will return 1
inDiv('x', "ahhh... not this time') will return 0
完成它:
int inDiv(char c, char * divs)
{
char * p_divs = divs; // remember which character we're considering
char tmp;
while(tmp = *p_divs++) // copy that character into tmp, and move p_divs to the next character
// but if tmp is then 0/false, break out of the while loop
if (tmp == c) return 1; // if tmp is the character we're searching for, return "1" meaning found
return 0; // must be here because tmp == 0 indicating end-of-string - return "0" meaning not-found
}
我们可以通过查看呼叫网站来推断reverse
的内容:
int main()
{
char source[MAS_SIZE], dest[MAS_SIZE], divs[MAS_SIZE];
printf("String : ");
gets(source);
printf("Dividers : ");
gets(divs);
reverse(source, dest, divs);
printf("Reversed string : %s", dest);
我们可以看到调用的gets()
从标准输入读取到字符数组source
和divs
- &gt;然后将这些输入提供给reverse()
。打印dest
的方式,显然是source
中字符串反转的目的地。在这个阶段,我们无法洞察divs
的相关性。
让我们来看看源......
void reverse(char * source, char * dest, char * divs)
{
*dest = '\0'; //what does this pointer do?
int source_len = strlen(source); //what is source
if (source_len == 0) return;
char* p_source = source + source_len - 1;
char* p_dest = dest;
while(p_source >= source)
{
while((p_source >= source) && (inDiv(*p_source, divs))) p_source--;
这里,*dest = '\0'
将一个NUL字符写入字符数组dest
- 这是编码字符串结尾位置的正常标记值 - 将其放入第一个字符*dest
意味着我们希望清除目的地。我们知道source
是我们将要撤消的文字输入 - strlen()
会将source_len
设置为其中的字符数。如果没有字符,那么return
因为没有工作要做,输出已经用NUL终止了。否则,会创建一个新指针p_source
并初始化为source + source_len - 1
- &gt;这意味着它指向源中的 last 非NUL字符。 p_dest
指向目标缓冲区开头的NUL字符。
然后循环说:while (p_source >= source)
- 为了做这件事p_source
最初必须是>= source
- 这是有道理的,因为p_source
指向最后一个字符source
1}}是缓冲区中的第一个字符地址;比较意味着我们将一个或两个移向另一个,直到它们交叉 - 每次做一些工作。这将我们带到:
while((p_source >= source) && (inDiv(*p_source, divs))) p_source--;
这是我们刚刚看到的相同测试 - 但这次我们只向p_source
向后移动到字符串的开头,而inDiv(*p_source, divs)
也是如此......这意味着*p_source
处的字符是divs
字符串中的一个字符。这意味着什么基本上:向后移动直到你已经超过字符串的开头(虽然这个测试有未定义的行为,正如Michael Burr在评论中指出的那样,如果字符串碰巧在地址0分配,真的可能无效 - 即使相对于某些特定的数据段,因为指针可以从0变为类似FFFFFFFF十六进制而不会看起来小于0),或者直到找到一个不是“分隔符”字符的字符。
在这里,我们可以深入了解代码的作用...将输入划分为由divs
输入中的任意一组字符分隔的“单词”,然后以空格分隔符的相反顺序写入它们进入目标缓冲区。这有点超越自己 - 但让我们通过它来追踪它:
下一行是......
if (p_source < source) break;
...这意味着如果循环退出已经越过源字符串的前面,那么就会突破所有循环(展望未来,我们看到代码只是在已经生成的结尾处放置一个新的NUL输出和返回 - 但这是我们所期望的吗? - 如果我们在“hello world”中支持“hello”那么我们就会点击字符串的开头并终止循环而不复制最后一个“hello” “输出的字!输出将始终是输入中的所有单词 - 除了第一个单词 - 反转 - 这不是作者描述的行为。”
否则:
char* w_end = p_source; // remember where the non-divider character "word" ends
// move backwards until there are no more characters (p_source < source) or you find a non-divider character
while((p_source >= source) && (!inDiv(*p_source, divs))) p_source--;
// either way that loop exited, the "word" begins at p_source + 1
char * w_beg = p_source + 1;
// append the word between w_beg and w_end to the destination buffer
for(char* p = w_beg; p <= w_end; p++) *p_dest++ = *p;
// also add a space...
*p_dest++ = ' ';
输入中的每个“单词”都会发生这种情况,然后最后一行将NUL终止符添加到目的地。
*p_dest = '\0';
现在,你说:
根据作者写的一串hello world然后在其中有一个函数也将字符串反转为世界你好
好吧,给定输入“hello world”和分隔符字符包括空格(但输入中没有其他字符),那么输出将是“hello world”(注意末尾的空格)。
对于它的价值 - 这段代码并不是那么糟糕......对于ASCIIZ缓冲区的C处理来说很正常,尽管关于输入长度的假设是危险的并且它缺少第一个字.... / p>
**如何修复未定义的行为**
关于未定义的行为 - 地址的最小变化是更改循环,以便它们在缓冲区的开始处终止,并让下一行明确检查它终止的原因并确定需要什么行为。那会有点难看,但不是火箭科学......
答案 1 :(得分:3)
char * p_divs = divs; //what does divs do
char tmp;
while(tmp = *p_divs++)
if (tmp == c) return 1
divs
是一个指向char数组的指针(当然是一个字符串)。 p_divs
只指向相同的字符串,在while循环中,单个字符被引出并写入tmp
,然后指针递增,这意味着下一个字符将被引入下一个迭代器。如果tmp
匹配c
,则函数返回。
修改:您应该详细了解指针,请查看Pointer Arithmetic。
答案 2 :(得分:1)
正如我在评论中指出的那样,我不认为C是这个任务的理想工具(如果有选择,我会毫不犹豫地使用C ++)。
然而,我想如果我要谈论代码是多么可怕,反评论确实是对的:我应该发布一些更好的东西。然而,与有关评论相反,我并不认为这代表了优雅,简洁或表现的妥协。
可能对真正的争论开放的唯一部分是优雅,但认为这足够简单和直接,在这方面几乎没有真正的问题。它显然更简洁 - 使用与原始格式大致相同的格式约定,我的rev_words是14行而不是17。因为大多数人会格式化它们,我的是17行,他是21行。
对于性能,在大多数情况下,我希望这两者在中等效。我避免在阵列的开头运行,这节省了一点点时间。原始包含一个提前退出,这将节省一小部分时间来反转一个空字符串。我认为这两个都是微不足道的。
我认为还有一点要重要得多:我有理由相信我不会像原来那样使用/调用/依赖未定义的行为。我想有些人可能会认为这是合理的,如果它在另一个领域提供了巨大的优势,但鉴于它在其他领域大致束缚或低劣,我无法想象有谁会考虑它(甚至接近)在这种情况下是合理的。
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
int contains(char const *input, char val) {
while (*input != val && *input != '\0')
++input;
return *input == val;
}
void rev_words(char *dest, size_t max_len, char const *input, char const *delims) {
char const *end = input + strlen(input);
char const *start;
char const *pos;
do {
for (; end>input && contains(delims, end[-1]); --end);
for (start=end; start>input && !contains(delims,start[-1]); --start);
for (pos=start; pos<end && max_len>1; --max_len)
*dest++=*pos++;
if (max_len > 1) { --max_len; *dest++ = ' '; }
end=start;
} while (max_len > 1 && start > input);
*dest++ = '\0';
}
int main(){
char reversed[100];
rev_words(reversed, sizeof(reversed), "This is an\tinput\nstring with\tseveral words in\n it.", " \t\n.");
printf("%s\n", reversed);
return 0;
}
编辑::
if (max_len > 1) { --max_len; *dest++ = ' '; }
应该是:
if (max_len > 1 && end-start > 0) { --max_len; *dest++ = ' '; }
如果你想允许max_len&lt; 1,你可以改变:
*dest++ = '\0';
为:
if (max_len > 0) *dest++ = '\0';
如果缓冲区长度可以通过来自(可能是敌对的)用户的输入以某种方式设置,那么这可能是值得的。出于许多目的,仅需要正缓冲区大小就足够了。