CS50: pset2 / initials:- I've got code that works but I feel like I am taking a shortcut with setting my array size

喜你入骨 提交于 2019-12-12 06:57:21

问题


So I am working away on the 'less comfortable' version of the initials problem in CS50, and after beginning with very verbose code I've managed to whittle it down to this:

#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"); 
} 

As it stands like that it passes, but I feel like I am copping out a little cos I just pick [10] out of the air for the initial array size which I know isn't good practice. To make it a little 'better' I've tried to run a 'for' loop to iterate through the name string and add up the number of spaces. I then want to make the array [spaces + 1] as if there are 2 spaces then there will be 3 initials. The code I am trying for that is:

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

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

}      

The thought is that I then make 'char initials[spaces + 1]' on the next line, but even before I can do that, compiling my code with just this 'for' loop returns a fail when I upload it for checking (although it compiles no problem). Even if I don't use any of the 'for' loops output the mere fact it is there gives me this error.

Where am I going wrong?

Any help on this would be much appreciated.

Thanks!


回答1:


First of all, keep in mind that execution speed is most often more valuable than memory use. If you first go look for spaces and after that allocate memory, you have to iterate through the array twice. This is an optimization of memory use at the cost of execution speed. So it might make more sense to just allocate a "large enough" array of lets say 100 characters and keep the code that you have.

I then want to make the array [spaces + 1] as if there are 2 spaces then there will be 3 initials

Keep in mind that C strings are null terminated, so you need to allocate room for the null terminator too, spaces + 1 + 1.

compiling my code with just this 'for' loop returns a fail when I upload it for checking (although it compiles no problem). Even if I don't use any of the 'for' loops output the mere fact it is there gives me this error.

What error? Does it compile or does it not compile, your text is contradicting.

Make sure you initialize spaces to zero.


As a side note, never use "magic numbers" in C code. if (name[i] == 32), 32 is gibberish to anyone who can't cite the ASCII table by memory. In addition, it is non-portable to systems with other symbol tables that might not have the same index numbers. Instead write:

if (name[i] == ' ')



回答2:


In my opinion, a good approach to cater for such situations is the one the library function snprintf uses: It requires you to pass in the string to fill and the size of that string. In ensures that the string isn't overwritten and that the string is zero-terminated.

The function returns the length of the characters written to the string if the had the string been large enough. You can now do one of two things: Guess a reasonable buffer size and accept that the string will be cut short occasionally. Or call the function with a zero length, use the return value to allocate a char buffer and then fill it with a second call.

Applying this approach to your initials problem:

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;
}

You can then either use the fixed-size bufer approach:

char *name = "Theodore Quick Brown Fox";

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

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

Or the two-step dynamic-size approach:

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"

(Note that this implementation of initals will ignore multiple spaces and spaces at the end or at the beginning of the string. Your look-one-ahead function will insert spaces in these cases.)




回答3:


You know your initials array can't be any bigger than the name itself; at most, it can't be more than half as big (every other character is a space). So use that as your size. The easiest way to do that is to use a variable-length array:

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.

The only problem is that VLA support may be iffy - VLAs were introduced in C99, but made optional in C2011.

Alternately, you can use a dynamically-allocated buffer:

#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.  

Although, if all you have to do is display the initials, there's really no reason to store them - just print them as you find them.

Like others have suggested, use the character constant ' ' instead of the ASCII code 32 for comparing against a space:

if ( name[j] == ' ' )

or use the isspace library function (which will return true for spaces, tabs, newlines, etc.):

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


来源:https://stackoverflow.com/questions/43930700/cs50-pset2-initials-ive-got-code-that-works-but-i-feel-like-i-am-taking-a

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!