Having issues on assigning a word to length

Viewed 104

I'm trying to write a program that does the following:

Enter a word: supercalifragilisticoespialidoso

The word's length is: 32.

Enter a smaller number than the length: 23

The word cut on letter 23 is: supercalifragilisticoes.

For that I'm doing:

#include <stdio.h>

#include<string.h>

#define DIM 99

int main() {

    char name[DIM], name2[DIM];
    int length, length2;

    printf("Enter a word: ");
    scanf("%s", name);
    length = strlen(name);
    printf("The word's length is: %d\n", length);
    printf("Enter a smaller number than the length: ");
    scanf("%d", &length2);
    name2 = name[length2];
    printf("The word cut on the letter %d is: %c", length2, name2);
    
    
    return 0;
}

But I get

main.c:16:11: error: assignment to expression with array type

The problem is in the line name2 = name[length2], that's the way I found to create the new smaller word, but it's not right.

Could someone please help?

4 Answers

The mistake is in the line

name2 = name[length2];

You're trying to assign a character (the one of index length2 inside name) to an array (name2).

What you actually want to do is this:

strncpy(name2, name, length2);
name2[length2] = '\0';

This copies the first length2 bytes of name into name2 and adds a terminating null character for safety (strncpy doesn't do it if all of the bytes are written).

If you don't intend to use name again, you could as well remove name2 altogether and add a null character to name:

name[length2] = '\0';

You're also printing a string with a %c format specifier on the last printf call. You should use %s instead.

Other answers have suggested how you might make a copy of the initial substring of your input or how to modify the input string in place. Those are perfectly good approaches, and both have plenty of uses in real-world programs.

However, if all your program needs to do is print the wanted substring then there is no need to do any string manipulation at all. printf can do the job by itself. Given the variables as you declared them and this code ...

    scanf("%s", name);
    length = strlen(name);
    printf("The word's length is: %d\n", length);
    printf("Enter a smaller number than the length: ");
    scanf("%d", &length2);

... you can use a printf format to print name into a bounded-length field whose length is given by another printf argument:

    printf("%.*s\n", length2, name);

That also adds a newline after, which is usually what one wants, but you can leave that off if you prefer.

The .* in the formatting directive indicates that a "precision" is being specified for the field via a printf argument. Other variations can express a fixed precision directly in the format. The significance of a precision depends somewhat on the directive type, but for s directives it is the maximum number of characters to be printed from the corresponding string argument.

There are two main errors in your code, and a couple of other minor points.

First, you can't directly assign arrays (be they character strings or any other array type) in C; for nul-terminated char arrays (strings), you can copy one to another using the strcpy function, or copy part of one to another using strncpy (which is what you want in your case).

Second, you can't print a string using the %c format specifier – you need %s for those.

And, a less serious issue (but one to avoid, if you want to be a good programmer) is that functions that work with string lengths (like strlen and strncpy) generally use size_t types, rather than int; and these require using the %zu format specifier, in place of %d.

Here's a version of your code that does what you want:

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

#define DIM 99

int main()
{
    char name[DIM], name2[DIM] = { 0 }; // Fill with zeros to start, so we will have a nul terminator
    size_t length, length2; // Strictly, use "size_t" for "strlen" and "strncpy"
    printf("Enter a word: ");
    scanf("%s", name);
    length = strlen(name);
    printf("The word's length is: %zu\n", length); // Use "%zu" for the "size_t" type
    printf("Enter a smaller number than the length: ");
    scanf("%zu", &length2); //Again, "%zu" for "size_t"
    strncpy(name2, name, length2); // Use this function to copy a substring!
    printf("The word cut on the letter %zu is: %s", length2, name2);// Note that you need "%s" to print a string
    return 0;
}

There are some other 'safety measures' that you can add to your code, to prevent buffer overruns and other faults. One would be to limit the initial string input to at most DIM - 1 characters; this would be trivial if you had a hard-coded value of 99 in place of DIM, because then you could use a call like the following:

scanf("%98s", name); // Read at most 98 characters (leaving space for the nul-terminator)

However, the macro DIM cannot be used inside the quoted format string. Instead, you can write its value (minus 1) into a separate string and use that as the format argument to scanf; so, we replace our initial scanf call, like so:

//  scanf("%s", name); // This has the potential to overrun the "name" array!
    char fmt[8];
    sprintf(fmt, "%%%ds", DIM - 1); // Write "%98s" into the "fmt" string ...
    scanf(fmt, name);               // ...and use that for the "scanf" format

(Note that some compilers will warn about not using a string literal for the format argument, and some programmers may not 'approve' of doing so; however, it is perfectly legal C and, IMHO, a valid use of the scanf function.)

The assignment:

name2 = name[length2];

does not have the semantics you appear to have assumed. Rather name2 is an array while name[length2] is a a single character at the index length2 of the array name.

In any event arrays are not first-class data types in C and you cannot assign one array to another (as it appears you were perhaps intending) let alone assigning a char for an array.

Here you might explicitly strncpy() to copy the sub-string, but in this case that is perhaps unnecessary. You can simply remove the name2 array and:

printf( "The word cut on the letter %d is: ", length2 ) ;
for( int i = 0; i < length && i < length2; i++ )
{
    putchar( name[i] ) ;
}

If you truly need to store the sub-string rather then simply outputting it then:

int i = 0 ;
for( i = 0; i < length && i < length2; i++ )
{
    name2[i] = name[i] ;
}
name2[i] = '\0' ;

Both have the advantage of behaving deterministically if length2 were less than zero or greater than length which you fail to check.

Using strncpy():

int len = (length2 < 0 || length2 > length) ? 
          length : 
          length2 ;
strncpy( name2, name, len ) ;
name2[len] = '\0' ; 

Of course all those length checks are largely defeated by the lack of safety in acquiring name in the manner you have in the first instance. Consider using fgets() instead, or even getchar() in a loop.

Related