CS50:pset2 /姓名缩写: - 我的代码有效,但我觉得我正在设置一个快捷方式来设置我的数组大小

时间:2017-05-12 06:27:06

标签: c arrays cs50

所以我正在努力解决CS50中首字母问题的“不太舒服”版本,并且从非常详细的代码开始,我已经设法将其简化为:

#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>

int c = 0;

int main(void)
{
    string name = get_string();
    int n = strlen(name);

    char initials[10];

    // first letter is always going to be the first initial
    initials[0] = name[0];

    // count through letters looking for spaces + add the first letter after a 
    // space to the initials array
    for (int j = 0; j < n; j++)
    {
        if (name[j] == 32)
        {
            c += 1;
            initials[c] += name[j+1];
        }
    }    

    // print out initials
    for (int k = 0; k <= c; k++)
    {
        printf("%c", toupper(initials[k]));
    }    

   printf("\n"); 
} 

就像它经过的那样,但是我觉得我正在考虑一点cos我只是从空中选择[10]以获得最初的阵列大小,我知道这不是一个好习惯。为了使它更好'我'试图运行'for'循环迭代通过名称字符串并添加空格数。然后,我想使数组[空格+ 1]好像有2个空格,然后将有3个首字母。我正在尝试的代码是:

string name = get_string();
int n = strlen(name);

for (int i = 0; i < n; i++)
{
    if (name[i] == 32)
    {
        spaces +=1;
    } 

}      

我的想法是,然后我在下一行创建'char initials [spaces + 1]',但即使在我能做到这一点之前,只使用这个'for'循环编译我的代码,当我上传它时返回失败检查(虽然它编译没有问题)。即使我不使用任何'for'循环输出,仅仅是因为它会给我这个错误。

我哪里错了?

对此的任何帮助都将非常感激。

谢谢!

3 个答案:

答案 0 :(得分:1)

首先,请记住,执行速度通常比内存使用更有价值。如果你先去寻找空格,然后分配内存,你必须遍历数组两次。这是以执行速度为代价的内存使用优化。因此,仅仅分配一个“足够大”的数组,让100个字符并保留您拥有的代码可能更有意义。

  

然后我想让数组[spaces + 1]好像有2个空格然后会有3个首字母

请记住,C字符串是空终止的,因此您还需要为空终止符spaces + 1 + 1分配空间。

  

使用这个'for'循环编译我的代码,当我上传它以进行检查时返回失败(虽然它编译没有问题)。即使我不使用任何'for'循环输出,仅仅是因为它会给我这个错误。

什么错误?它是编译还是不编译,你的文本是矛盾的。

确保将spaces初始化为零。

作为旁注,切勿在C代码中使用“幻数”。 if (name[i] == 32),32对任何无法通过内存引用ASCII表的人都是胡言乱语。此外,对于具有可能不具有相同索引号的其他符号表的系统,它是不可移植的。而是写:

if (name[i] == ' ')

答案 1 :(得分:0)

在我看来,迎合这种情况的好方法是库函数snprintf使用的方法:它要求你传入要填充的字符串和该字符串的大小。 In确保不覆盖字符串并且字符串为零终止。

如果字符串足够大,该函数返回写入字符串的字符的长度。您现在可以执行以下两项操作之一:猜测合理的缓冲区大小,并接受偶尔缩短字符串的时间。或者调用长度为零的函数,使用返回值分配一个char缓冲区,然后用第二次调用填充它。

将此方法应用于首字母缩写问题:

int initials(char *ini, int max, const char *str)
{
    int prev = ' ';         // pretend there's a space before the string
    int n = 0;              // actual number of initials

    while (*str) {
        if (prev == ' ' && *str != ' ') {
            if (n + 1 < max) ini[n] = *str;
            n++;
        }

        prev = *str++;
    }

    if (n < max) {
        ini[n] = '\0';
    } else if (max > 0) {
        ini[max] = '\0';
    }    

    return n;
}

然后您可以使用固定大小的缓冲方法:

char *name = "Theodore Quick Brown Fox";

char ini[4];
initials(ini, sizeof(ini), name);

puts(ini);        // prints "TQB", "F" is truncated

或两步动态尺寸方法:

char *name = "Theodore Quick Brown Fox";
int n;

n = initials(NULL, 0, name);

char ini[n + 1];
initials(ini, sizeof(ini), name);

puts(ini);        // prints "TQBF"

(请注意,initals的这种实现将忽略字符串末尾或开头的多个空格和空格。在这些情况下,您的前瞻功能将插入空格。)

答案 2 :(得分:0)

您知道您的initials数组不能比名称本身更大;至多,它不能超过一半(每隔一个字符就是一个空格)。所以使用它作为你的尺寸。最简单的方法是使用可变长度数组:

size_t n = strlen( name ); // strlen returns a size_t type, not int

char initials[n/2+1];         // n/2+1 is not a *constant expression*, so this is
                              // a variable-length array.
memset( initials, 0, n + 1 ); // since initials is a VLA, we can't use an initializer
                              // in the declaration.

唯一的问题是VLA支持可能是不确定的 - 在C99中引入了VLA,但在C2011中是可选的。

或者,您可以使用动态分配的缓冲区:

#include <stdlib.h>
...
size_t n = strlen( name );

char *initials = calloc( n/2+1, sizeof *initials ); // calloc initializes memory to 0

/**
 * code to find and display initials
 */

free( initials );  // release memory before you exit your program.  

虽然,如果您只需要显示首字母,那么就没有理由存储它们 - 只需在找到它们时打印它们。

与其他人建议的一样,使用字符常量' '而不是ASCII代码32来与空格进行比较:

if ( name[j] == ' ' )

或使用isspace库函数(对于空格,制表符,换行符等将返回true):

#include <ctype.h>
...
if ( isspace( name[j] ) )